1
0
Fork 0

gma500: initial medfield merge

We need to merge this ahead of some of the cleanup because a lot of needed
cleanup spans both new and old chips. If we try and clean up and the merge
we end up fighting ourselves.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
[With a load of the cleanup stuff folded in, register stuff reworked sanely]
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
hifive-unleashed-5.1
Kirill A. Shutemov 2012-03-08 16:02:20 +00:00 committed by Dave Airlie
parent c6265ff593
commit 026abc3332
22 changed files with 6345 additions and 0 deletions

View File

@ -28,6 +28,8 @@
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/mfd/intel_msic.h>
#include <linux/gpio.h>
#include <linux/i2c/tc35876x.h>
#include <asm/setup.h>
#include <asm/mpspec_def.h>
@ -686,6 +688,19 @@ static void *msic_ocd_platform_data(void *info)
return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD);
}
/* tc35876x DSI-LVDS bridge chip and panel platform data */
static void *tc35876x_platform_data(void *data)
{
static struct tc35876x_platform_data pdata;
/* gpio pins set to -1 will not be used by the driver */
pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN");
pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN");
pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3");
return &pdata;
}
static const struct devs_id __initconst device_ids[] = {
{"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data},
{"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data},
@ -698,6 +713,7 @@ static const struct devs_id __initconst device_ids[] = {
{"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data},
{"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data},
{"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data},
{"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data},
/* MSIC subdevices */
{"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data},

View File

@ -24,3 +24,10 @@ config DRM_GMA3600
help
Say yes to include basic support for Intel GMA3600/3650 (Intel
Cedar Trail) platforms.
config DRM_MEDFIELD
bool "Intel Medfield support (Experimental)"
depends on DRM_GMA500 && X86_INTEL_MID
help
Say yes to include support for the Intel Medfield platform.

View File

@ -37,4 +37,14 @@ gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \
oaktrail_hdmi.o \
oaktrail_hdmi_i2c.o
gma500_gfx-$(CONFIG_DRM_MEDFIELD) += mdfld_device.o \
mdfld_output.o \
mdfld_intel_display.o \
mdfld_dsi_output.o \
mdfld_dsi_dpi.o \
mdfld_dsi_pkg_sender.o \
mdfld_tpo_vid.o \
mdfld_tmd_vid.o \
tc35876x-dsi-lvds.o
obj-$(CONFIG_DRM_GMA500) += gma500_gfx.o

View File

@ -0,0 +1,691 @@
/**************************************************************************
* 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 "psb_drv.h"
#include "mid_bios.h"
#include "mdfld_output.h"
#include "mdfld_dsi_output.h"
#include "tc35876x-dsi-lvds.h"
#include <asm/intel_scu_ipc.h>
#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 BRIGHTNESS_MIN_LEVEL 1
#define BRIGHTNESS_MAX_LEVEL 100
#define BRIGHTNESS_MASK 0xFF
#define BLC_POLARITY_NORMAL 0
#define BLC_POLARITY_INVERSE 1
#define BLC_ADJUSTMENT_MAX 100
#define MDFLD_BLC_PWM_PRECISION_FACTOR 10
#define MDFLD_BLC_MAX_PWM_REG_FREQ 0xFFFE
#define MDFLD_BLC_MIN_PWM_REG_FREQ 0x2
#define MDFLD_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
#define MDFLD_BACKLIGHT_PWM_CTL_SHIFT (16)
static struct backlight_device *mdfld_backlight_device;
int mdfld_set_brightness(struct backlight_device *bd)
{
struct drm_device *dev =
(struct drm_device *)bl_get_data(mdfld_backlight_device);
struct drm_psb_private *dev_priv = dev->dev_private;
int level = bd->props.brightness;
DRM_DEBUG_DRIVER("backlight level set to %d\n", level);
/* Perform value bounds checking */
if (level < BRIGHTNESS_MIN_LEVEL)
level = BRIGHTNESS_MIN_LEVEL;
if (gma_power_begin(dev, false)) {
u32 adjusted_level = 0;
/*
* Adjust the backlight level with the percent in
* dev_priv->blc_adj2
*/
adjusted_level = level * dev_priv->blc_adj2;
adjusted_level = adjusted_level / BLC_ADJUSTMENT_MAX;
dev_priv->brightness_adjusted = adjusted_level;
if (mdfld_get_panel_type(dev, 0) == TC35876X) {
if (dev_priv->dpi_panel_on[0] ||
dev_priv->dpi_panel_on[2])
tc35876x_brightness_control(dev,
dev_priv->brightness_adjusted);
} else {
if (dev_priv->dpi_panel_on[0])
mdfld_dsi_brightness_control(dev, 0,
dev_priv->brightness_adjusted);
}
if (dev_priv->dpi_panel_on[2])
mdfld_dsi_brightness_control(dev, 2,
dev_priv->brightness_adjusted);
gma_power_end(dev);
}
/* cache the brightness for later use */
dev_priv->brightness = level;
return 0;
}
int mdfld_get_brightness(struct backlight_device *bd)
{
struct drm_device *dev =
(struct drm_device *)bl_get_data(mdfld_backlight_device);
struct drm_psb_private *dev_priv = dev->dev_private;
DRM_DEBUG_DRIVER("brightness = 0x%x \n", dev_priv->brightness);
/* return locally cached var instead of HW read (due to DPST etc.) */
return dev_priv->brightness;
}
static const struct backlight_ops mdfld_ops = {
.get_brightness = mdfld_get_brightness,
.update_status = mdfld_set_brightness,
};
static int device_backlight_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = (struct drm_psb_private *)
dev->dev_private;
dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX;
dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX;
return 0;
}
int mdfld_backlight_init(struct drm_device *dev)
{
struct backlight_properties props;
int ret = 0;
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = BRIGHTNESS_MAX_LEVEL;
props.type = BACKLIGHT_PLATFORM;
mdfld_backlight_device = backlight_device_register("mdfld-bl",
NULL, (void *)dev, &mdfld_ops, &props);
if (IS_ERR(mdfld_backlight_device))
return PTR_ERR(mdfld_backlight_device);
ret = device_backlight_init(dev);
if (ret)
return ret;
mdfld_backlight_device->props.brightness = BRIGHTNESS_MAX_LEVEL;
mdfld_backlight_device->props.max_brightness = BRIGHTNESS_MAX_LEVEL;
backlight_update_status(mdfld_backlight_device);
return 0;
}
#endif
struct backlight_device *mdfld_get_backlight_device(void)
{
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
return mdfld_backlight_device;
#else
return NULL;
#endif
}
/*
* mdfld_save_display_registers
*
* Description: We are going to suspend so save current display
* register state.
*
* Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
*/
static int mdfld_save_display_registers(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct medfield_state *regs = &dev_priv->regs.mdfld;
int i;
/* register */
u32 dpll_reg = MRST_DPLL_A;
u32 fp_reg = MRST_FPA0;
u32 pipeconf_reg = PIPEACONF;
u32 htot_reg = HTOTAL_A;
u32 hblank_reg = HBLANK_A;
u32 hsync_reg = HSYNC_A;
u32 vtot_reg = VTOTAL_A;
u32 vblank_reg = VBLANK_A;
u32 vsync_reg = VSYNC_A;
u32 pipesrc_reg = PIPEASRC;
u32 dspstride_reg = DSPASTRIDE;
u32 dsplinoff_reg = DSPALINOFF;
u32 dsptileoff_reg = DSPATILEOFF;
u32 dspsize_reg = DSPASIZE;
u32 dsppos_reg = DSPAPOS;
u32 dspsurf_reg = DSPASURF;
u32 mipi_reg = MIPI;
u32 dspcntr_reg = DSPACNTR;
u32 dspstatus_reg = PIPEASTAT;
u32 palette_reg = PALETTE_A;
/* pointer to values */
u32 *dpll_val = &regs->saveDPLL_A;
u32 *fp_val = &regs->saveFPA0;
u32 *pipeconf_val = &regs->savePIPEACONF;
u32 *htot_val = &regs->saveHTOTAL_A;
u32 *hblank_val = &regs->saveHBLANK_A;
u32 *hsync_val = &regs->saveHSYNC_A;
u32 *vtot_val = &regs->saveVTOTAL_A;
u32 *vblank_val = &regs->saveVBLANK_A;
u32 *vsync_val = &regs->saveVSYNC_A;
u32 *pipesrc_val = &regs->savePIPEASRC;
u32 *dspstride_val = &regs->saveDSPASTRIDE;
u32 *dsplinoff_val = &regs->saveDSPALINOFF;
u32 *dsptileoff_val = &regs->saveDSPATILEOFF;
u32 *dspsize_val = &regs->saveDSPASIZE;
u32 *dsppos_val = &regs->saveDSPAPOS;
u32 *dspsurf_val = &regs->saveDSPASURF;
u32 *mipi_val = &regs->saveMIPI;
u32 *dspcntr_val = &regs->saveDSPACNTR;
u32 *dspstatus_val = &regs->saveDSPASTATUS;
u32 *palette_val = regs->save_palette_a;
switch (pipe) {
case 0:
break;
case 1:
/* regester */
dpll_reg = MDFLD_DPLL_B;
fp_reg = MDFLD_DPLL_DIV0;
pipeconf_reg = PIPEBCONF;
htot_reg = HTOTAL_B;
hblank_reg = HBLANK_B;
hsync_reg = HSYNC_B;
vtot_reg = VTOTAL_B;
vblank_reg = VBLANK_B;
vsync_reg = VSYNC_B;
pipesrc_reg = PIPEBSRC;
dspstride_reg = DSPBSTRIDE;
dsplinoff_reg = DSPBLINOFF;
dsptileoff_reg = DSPBTILEOFF;
dspsize_reg = DSPBSIZE;
dsppos_reg = DSPBPOS;
dspsurf_reg = DSPBSURF;
dspcntr_reg = DSPBCNTR;
dspstatus_reg = PIPEBSTAT;
palette_reg = PALETTE_B;
/* values */
dpll_val = &regs->saveDPLL_B;
fp_val = &regs->saveFPB0;
pipeconf_val = &regs->savePIPEBCONF;
htot_val = &regs->saveHTOTAL_B;
hblank_val = &regs->saveHBLANK_B;
hsync_val = &regs->saveHSYNC_B;
vtot_val = &regs->saveVTOTAL_B;
vblank_val = &regs->saveVBLANK_B;
vsync_val = &regs->saveVSYNC_B;
pipesrc_val = &regs->savePIPEBSRC;
dspstride_val = &regs->saveDSPBSTRIDE;
dsplinoff_val = &regs->saveDSPBLINOFF;
dsptileoff_val = &regs->saveDSPBTILEOFF;
dspsize_val = &regs->saveDSPBSIZE;
dsppos_val = &regs->saveDSPBPOS;
dspsurf_val = &regs->saveDSPBSURF;
dspcntr_val = &regs->saveDSPBCNTR;
dspstatus_val = &regs->saveDSPBSTATUS;
palette_val = regs->save_palette_b;
break;
case 2:
/* register */
pipeconf_reg = PIPECCONF;
htot_reg = HTOTAL_C;
hblank_reg = HBLANK_C;
hsync_reg = HSYNC_C;
vtot_reg = VTOTAL_C;
vblank_reg = VBLANK_C;
vsync_reg = VSYNC_C;
pipesrc_reg = PIPECSRC;
dspstride_reg = DSPCSTRIDE;
dsplinoff_reg = DSPCLINOFF;
dsptileoff_reg = DSPCTILEOFF;
dspsize_reg = DSPCSIZE;
dsppos_reg = DSPCPOS;
dspsurf_reg = DSPCSURF;
mipi_reg = MIPI_C;
dspcntr_reg = DSPCCNTR;
dspstatus_reg = PIPECSTAT;
palette_reg = PALETTE_C;
/* pointer to values */
pipeconf_val = &regs->savePIPECCONF;
htot_val = &regs->saveHTOTAL_C;
hblank_val = &regs->saveHBLANK_C;
hsync_val = &regs->saveHSYNC_C;
vtot_val = &regs->saveVTOTAL_C;
vblank_val = &regs->saveVBLANK_C;
vsync_val = &regs->saveVSYNC_C;
pipesrc_val = &regs->savePIPECSRC;
dspstride_val = &regs->saveDSPCSTRIDE;
dsplinoff_val = &regs->saveDSPCLINOFF;
dsptileoff_val = &regs->saveDSPCTILEOFF;
dspsize_val = &regs->saveDSPCSIZE;
dsppos_val = &regs->saveDSPCPOS;
dspsurf_val = &regs->saveDSPCSURF;
mipi_val = &regs->saveMIPI_C;
dspcntr_val = &regs->saveDSPCCNTR;
dspstatus_val = &regs->saveDSPCSTATUS;
palette_val = regs->save_palette_c;
break;
default:
DRM_ERROR("%s, invalid pipe number.\n", __func__);
return -EINVAL;
}
/* Pipe & plane A info */
*dpll_val = PSB_RVDC32(dpll_reg);
*fp_val = PSB_RVDC32(fp_reg);
*pipeconf_val = PSB_RVDC32(pipeconf_reg);
*htot_val = PSB_RVDC32(htot_reg);
*hblank_val = PSB_RVDC32(hblank_reg);
*hsync_val = PSB_RVDC32(hsync_reg);
*vtot_val = PSB_RVDC32(vtot_reg);
*vblank_val = PSB_RVDC32(vblank_reg);
*vsync_val = PSB_RVDC32(vsync_reg);
*pipesrc_val = PSB_RVDC32(pipesrc_reg);
*dspstride_val = PSB_RVDC32(dspstride_reg);
*dsplinoff_val = PSB_RVDC32(dsplinoff_reg);
*dsptileoff_val = PSB_RVDC32(dsptileoff_reg);
*dspsize_val = PSB_RVDC32(dspsize_reg);
*dsppos_val = PSB_RVDC32(dsppos_reg);
*dspsurf_val = PSB_RVDC32(dspsurf_reg);
*dspcntr_val = PSB_RVDC32(dspcntr_reg);
*dspstatus_val = PSB_RVDC32(dspstatus_reg);
/*save palette (gamma) */
for (i = 0; i < 256; i++)
palette_val[i] = PSB_RVDC32(palette_reg + (i << 2));
if (pipe == 1) {
regs->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
regs->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
regs->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL);
regs->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL);
return 0;
}
*mipi_val = PSB_RVDC32(mipi_reg);
return 0;
}
/*
* mdfld_restore_display_registers
*
* Description: We are going to resume so restore display register state.
*
* Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
*/
static int mdfld_restore_display_registers(struct drm_device *dev, int pipe)
{
/* To get panel out of ULPS mode. */
u32 temp = 0;
u32 device_ready_reg = DEVICE_READY_REG;
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dsi_config *dsi_config = NULL;
struct medfield_state *regs = &dev_priv->regs.mdfld;
u32 i = 0;
u32 dpll = 0;
u32 timeout = 0;
/* regester */
u32 dpll_reg = MRST_DPLL_A;
u32 fp_reg = MRST_FPA0;
u32 pipeconf_reg = PIPEACONF;
u32 htot_reg = HTOTAL_A;
u32 hblank_reg = HBLANK_A;
u32 hsync_reg = HSYNC_A;
u32 vtot_reg = VTOTAL_A;
u32 vblank_reg = VBLANK_A;
u32 vsync_reg = VSYNC_A;
u32 pipesrc_reg = PIPEASRC;
u32 dspstride_reg = DSPASTRIDE;
u32 dsplinoff_reg = DSPALINOFF;
u32 dsptileoff_reg = DSPATILEOFF;
u32 dspsize_reg = DSPASIZE;
u32 dsppos_reg = DSPAPOS;
u32 dspsurf_reg = DSPASURF;
u32 dspstatus_reg = PIPEASTAT;
u32 mipi_reg = MIPI;
u32 dspcntr_reg = DSPACNTR;
u32 palette_reg = PALETTE_A;
/* values */
u32 dpll_val = regs->saveDPLL_A & ~DPLL_VCO_ENABLE;
u32 fp_val = regs->saveFPA0;
u32 pipeconf_val = regs->savePIPEACONF;
u32 htot_val = regs->saveHTOTAL_A;
u32 hblank_val = regs->saveHBLANK_A;
u32 hsync_val = regs->saveHSYNC_A;
u32 vtot_val = regs->saveVTOTAL_A;
u32 vblank_val = regs->saveVBLANK_A;
u32 vsync_val = regs->saveVSYNC_A;
u32 pipesrc_val = regs->savePIPEASRC;
u32 dspstride_val = regs->saveDSPASTRIDE;
u32 dsplinoff_val = regs->saveDSPALINOFF;
u32 dsptileoff_val = regs->saveDSPATILEOFF;
u32 dspsize_val = regs->saveDSPASIZE;
u32 dsppos_val = regs->saveDSPAPOS;
u32 dspsurf_val = regs->saveDSPASURF;
u32 dspstatus_val = regs->saveDSPASTATUS;
u32 mipi_val = regs->saveMIPI;
u32 dspcntr_val = regs->saveDSPACNTR;
u32 *palette_val = regs->save_palette_a;
switch (pipe) {
case 0:
dsi_config = dev_priv->dsi_configs[0];
break;
case 1:
/* regester */
dpll_reg = MDFLD_DPLL_B;
fp_reg = MDFLD_DPLL_DIV0;
pipeconf_reg = PIPEBCONF;
htot_reg = HTOTAL_B;
hblank_reg = HBLANK_B;
hsync_reg = HSYNC_B;
vtot_reg = VTOTAL_B;
vblank_reg = VBLANK_B;
vsync_reg = VSYNC_B;
pipesrc_reg = PIPEBSRC;
dspstride_reg = DSPBSTRIDE;
dsplinoff_reg = DSPBLINOFF;
dsptileoff_reg = DSPBTILEOFF;
dspsize_reg = DSPBSIZE;
dsppos_reg = DSPBPOS;
dspsurf_reg = DSPBSURF;
dspcntr_reg = DSPBCNTR;
dspstatus_reg = PIPEBSTAT;
palette_reg = PALETTE_B;
/* values */
dpll_val = regs->saveDPLL_B & ~DPLL_VCO_ENABLE;
fp_val = regs->saveFPB0;
pipeconf_val = regs->savePIPEBCONF;
htot_val = regs->saveHTOTAL_B;
hblank_val = regs->saveHBLANK_B;
hsync_val = regs->saveHSYNC_B;
vtot_val = regs->saveVTOTAL_B;
vblank_val = regs->saveVBLANK_B;
vsync_val = regs->saveVSYNC_B;
pipesrc_val = regs->savePIPEBSRC;
dspstride_val = regs->saveDSPBSTRIDE;
dsplinoff_val = regs->saveDSPBLINOFF;
dsptileoff_val = regs->saveDSPBTILEOFF;
dspsize_val = regs->saveDSPBSIZE;
dsppos_val = regs->saveDSPBPOS;
dspsurf_val = regs->saveDSPBSURF;
dspcntr_val = regs->saveDSPBCNTR;
dspstatus_val = regs->saveDSPBSTATUS;
palette_val = regs->save_palette_b;
break;
case 2:
/* regester */
pipeconf_reg = PIPECCONF;
htot_reg = HTOTAL_C;
hblank_reg = HBLANK_C;
hsync_reg = HSYNC_C;
vtot_reg = VTOTAL_C;
vblank_reg = VBLANK_C;
vsync_reg = VSYNC_C;
pipesrc_reg = PIPECSRC;
dspstride_reg = DSPCSTRIDE;
dsplinoff_reg = DSPCLINOFF;
dsptileoff_reg = DSPCTILEOFF;
dspsize_reg = DSPCSIZE;
dsppos_reg = DSPCPOS;
dspsurf_reg = DSPCSURF;
mipi_reg = MIPI_C;
dspcntr_reg = DSPCCNTR;
dspstatus_reg = PIPECSTAT;
palette_reg = PALETTE_C;
/* values */
pipeconf_val = regs->savePIPECCONF;
htot_val = regs->saveHTOTAL_C;
hblank_val = regs->saveHBLANK_C;
hsync_val = regs->saveHSYNC_C;
vtot_val = regs->saveVTOTAL_C;
vblank_val = regs->saveVBLANK_C;
vsync_val = regs->saveVSYNC_C;
pipesrc_val = regs->savePIPECSRC;
dspstride_val = regs->saveDSPCSTRIDE;
dsplinoff_val = regs->saveDSPCLINOFF;
dsptileoff_val = regs->saveDSPCTILEOFF;
dspsize_val = regs->saveDSPCSIZE;
dsppos_val = regs->saveDSPCPOS;
dspsurf_val = regs->saveDSPCSURF;
mipi_val = regs->saveMIPI_C;
dspcntr_val = regs->saveDSPCCNTR;
dspstatus_val = regs->saveDSPCSTATUS;
palette_val = regs->save_palette_c;
dsi_config = dev_priv->dsi_configs[1];
break;
default:
DRM_ERROR("%s, invalid pipe number.\n", __func__);
return -EINVAL;
}
/*make sure VGA plane is off. it initializes to on after reset!*/
PSB_WVDC32(0x80000000, VGACNTRL);
if (pipe == 1) {
PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg);
PSB_RVDC32(dpll_reg);
PSB_WVDC32(fp_val, fp_reg);
} else {
dpll = PSB_RVDC32(dpll_reg);
if (!(dpll & DPLL_VCO_ENABLE)) {
/* When ungating power of DPLL, needs to wait 0.5us
before enable the VCO */
if (dpll & MDFLD_PWR_GATE_EN) {
dpll &= ~MDFLD_PWR_GATE_EN;
PSB_WVDC32(dpll, dpll_reg);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
}
PSB_WVDC32(fp_val, fp_reg);
PSB_WVDC32(dpll_val, dpll_reg);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
dpll_val |= DPLL_VCO_ENABLE;
PSB_WVDC32(dpll_val, dpll_reg);
PSB_RVDC32(dpll_reg);
/* wait for DSI PLL to lock */
while (timeout < 20000 &&
!(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) {
udelay(150);
timeout++;
}
if (timeout == 20000) {
DRM_ERROR("%s, can't lock DSIPLL.\n",
__func__);
return -EINVAL;
}
}
}
/* Restore mode */
PSB_WVDC32(htot_val, htot_reg);
PSB_WVDC32(hblank_val, hblank_reg);
PSB_WVDC32(hsync_val, hsync_reg);
PSB_WVDC32(vtot_val, vtot_reg);
PSB_WVDC32(vblank_val, vblank_reg);
PSB_WVDC32(vsync_val, vsync_reg);
PSB_WVDC32(pipesrc_val, pipesrc_reg);
PSB_WVDC32(dspstatus_val, dspstatus_reg);
/*set up the plane*/
PSB_WVDC32(dspstride_val, dspstride_reg);
PSB_WVDC32(dsplinoff_val, dsplinoff_reg);
PSB_WVDC32(dsptileoff_val, dsptileoff_reg);
PSB_WVDC32(dspsize_val, dspsize_reg);
PSB_WVDC32(dsppos_val, dsppos_reg);
PSB_WVDC32(dspsurf_val, dspsurf_reg);
if (pipe == 1) {
/* restore palette (gamma) */
/*DRM_UDELAY(50000); */
for (i = 0; i < 256; i++)
PSB_WVDC32(palette_val[i], palette_reg + (i << 2));
PSB_WVDC32(regs->savePFIT_CONTROL, PFIT_CONTROL);
PSB_WVDC32(regs->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
/*TODO: resume HDMI port */
/*TODO: resume pipe*/
/*enable the plane*/
PSB_WVDC32(dspcntr_val & ~DISPLAY_PLANE_ENABLE, dspcntr_reg);
return 0;
}
/*set up pipe related registers*/
PSB_WVDC32(mipi_val, mipi_reg);
/*setup MIPI adapter + MIPI IP registers*/
if (dsi_config)
mdfld_dsi_controller_init(dsi_config, pipe);
if (in_atomic() || in_interrupt())
mdelay(20);
else
msleep(20);
/*enable the plane*/
PSB_WVDC32(dspcntr_val, dspcntr_reg);
if (in_atomic() || in_interrupt())
mdelay(20);
else
msleep(20);
/* LP Hold Release */
temp = REG_READ(mipi_reg);
temp |= LP_OUTPUT_HOLD_RELEASE;
REG_WRITE(mipi_reg, temp);
mdelay(1);
/* Set DSI host to exit from Utra Low Power State */
temp = REG_READ(device_ready_reg);
temp &= ~ULPS_MASK;
temp |= 0x3;
temp |= EXIT_ULPS_DEV_READY;
REG_WRITE(device_ready_reg, temp);
mdelay(1);
temp = REG_READ(device_ready_reg);
temp &= ~ULPS_MASK;
temp |= EXITING_ULPS;
REG_WRITE(device_ready_reg, temp);
mdelay(1);
/*enable the pipe*/
PSB_WVDC32(pipeconf_val, pipeconf_reg);
/* restore palette (gamma) */
/*DRM_UDELAY(50000); */
for (i = 0; i < 256; i++)
PSB_WVDC32(palette_val[i], palette_reg + (i << 2));
return 0;
}
static int mdfld_save_registers(struct drm_device *dev)
{
/* mdfld_save_cursor_overlay_registers(dev); */
mdfld_save_display_registers(dev, 0);
mdfld_save_display_registers(dev, 2);
mdfld_disable_crtc(dev, 0);
mdfld_disable_crtc(dev, 2);
return 0;
}
static int mdfld_restore_registers(struct drm_device *dev)
{
mdfld_restore_display_registers(dev, 2);
mdfld_restore_display_registers(dev, 0);
/* mdfld_restore_cursor_overlay_registers(dev); */
return 0;
}
static int mdfld_power_down(struct drm_device *dev)
{
/* FIXME */
return 0;
}
static int mdfld_power_up(struct drm_device *dev)
{
/* FIXME */
return 0;
}
const struct psb_ops mdfld_chip_ops = {
.name = "mdfld",
.accel_2d = 0,
.pipes = 3,
.crtcs = 3,
.sgx_offset = MRST_SGX_OFFSET,
.chip_setup = mid_chip_setup,
.crtc_helper = &mdfld_helper_funcs,
.crtc_funcs = &psb_intel_crtc_funcs,
.output_init = mdfld_output_init,
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
.backlight_init = mdfld_backlight_init,
#endif
.save_regs = mdfld_save_registers,
.restore_regs = mdfld_restore_registers,
.power_down = mdfld_power_down,
.power_up = mdfld_power_up,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
/*
* 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:
* jim liu <jim.liu@intel.com>
* Jackie Li<yaodong.li@intel.com>
*/
#ifndef __MDFLD_DSI_DPI_H__
#define __MDFLD_DSI_DPI_H__
#include "mdfld_dsi_output.h"
#include "mdfld_output.h"
struct mdfld_dsi_dpi_timing {
u16 hsync_count;
u16 hbp_count;
u16 hfp_count;
u16 hactive_count;
u16 vsync_count;
u16 vbp_count;
u16 vfp_count;
};
struct mdfld_dsi_dpi_output {
struct mdfld_dsi_encoder base;
struct drm_device *dev;
int panel_on;
int first_boot;
const struct panel_funcs *p_funcs;
};
#define MDFLD_DSI_DPI_OUTPUT(dsi_encoder)\
container_of(dsi_encoder, struct mdfld_dsi_dpi_output, base)
/* Export functions */
extern int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode,
struct mdfld_dsi_dpi_timing *dpi_timing,
int num_lane, int bpp);
extern struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
struct mdfld_dsi_connector *dsi_connector,
const struct panel_funcs *p_funcs);
/* MDFLD DPI helper functions */
extern void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode);
extern bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder);
extern void mdfld_dsi_dpi_commit(struct drm_encoder *encoder);
extern void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output,
int pipe);
extern void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config,
int pipe);
#endif /*__MDFLD_DSI_DPI_H__*/

View File

@ -0,0 +1,635 @@
/*
* 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:
* jim liu <jim.liu@intel.com>
* Jackie Li<yaodong.li@intel.com>
*/
#include <linux/module.h>
#include "mdfld_dsi_output.h"
#include "mdfld_dsi_dpi.h"
#include "mdfld_output.h"
#include "mdfld_dsi_pkg_sender.h"
#include "tc35876x-dsi-lvds.h"
#include <linux/pm_runtime.h>
#include <asm/intel_scu_ipc.h>
/* get the LABC from command line. */
static int LABC_control = 1;
#ifdef MODULE
module_param(LABC_control, int, 0644);
#else
static int __init parse_LABC_control(char *arg)
{
/* LABC control can be passed in as a cmdline parameter */
/* to enable this feature add LABC=1 to cmdline */
/* to disable this feature add LABC=0 to cmdline */
if (!arg)
return -EINVAL;
if (!strcasecmp(arg, "0"))
LABC_control = 0;
else if (!strcasecmp(arg, "1"))
LABC_control = 1;
return 0;
}
early_param("LABC", parse_LABC_control);
#endif
/**
* Check and see if the generic control or data buffer is empty and ready.
*/
void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg,
u32 fifo_stat)
{
u32 GEN_BF_time_out_count;
/* Check MIPI Adatper command registers */
for (GEN_BF_time_out_count = 0;
GEN_BF_time_out_count < GEN_FB_TIME_OUT;
GEN_BF_time_out_count++) {
if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat)
break;
udelay(100);
}
if (GEN_BF_time_out_count == GEN_FB_TIME_OUT)
DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n",
gen_fifo_stat_reg);
}
/**
* Manage the DSI MIPI keyboard and display brightness.
* FIXME: this is exported to OSPM code. should work out an specific
* display interface to OSPM.
*/
void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe)
{
struct mdfld_dsi_pkg_sender *sender =
mdfld_dsi_get_pkg_sender(dsi_config);
struct drm_device *dev = sender->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
u32 gen_ctrl_val;
if (!sender) {
DRM_ERROR("No sender found\n");
return;
}
/* Set default display backlight value to 85% (0xd8)*/
mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1,
true);
/* Set minimum brightness setting of CABC function to 20% (0x33)*/
mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true);
/* Enable backlight or/and LABC */
gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
BACKLIGHT_ON;
if (LABC_control == 1)
gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
| GAMMA_AUTO;
if (LABC_control == 1)
gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON;
dev_priv->mipi_ctrl_display = gen_ctrl_val;
mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val,
1, true);
mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true);
}
void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level)
{
struct mdfld_dsi_pkg_sender *sender;
struct drm_psb_private *dev_priv;
struct mdfld_dsi_config *dsi_config;
u32 gen_ctrl_val = 0;
int p_type = TMD_VID;
if (!dev || (pipe != 0 && pipe != 2)) {
DRM_ERROR("Invalid parameter\n");
return;
}
p_type = mdfld_get_panel_type(dev, 0);
dev_priv = dev->dev_private;
if (pipe)
dsi_config = dev_priv->dsi_configs[1];
else
dsi_config = dev_priv->dsi_configs[0];
sender = mdfld_dsi_get_pkg_sender(dsi_config);
if (!sender) {
DRM_ERROR("No sender found\n");
return;
}
gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n",
pipe, gen_ctrl_val);
if (p_type == TMD_VID) {
/* Set display backlight value */
mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness,
(u8)gen_ctrl_val, 1, true);
} else {
/* Set display backlight value */
mdfld_dsi_send_mcs_short(sender, write_display_brightness,
(u8)gen_ctrl_val, 1, true);
/* Enable backlight control */
if (level == 0)
gen_ctrl_val = 0;
else
gen_ctrl_val = dev_priv->mipi_ctrl_display;
mdfld_dsi_send_mcs_short(sender, write_ctrl_display,
(u8)gen_ctrl_val, 1, true);
}
}
static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
u8 dcs, u32 *data, bool hs)
{
struct mdfld_dsi_pkg_sender *sender
= mdfld_dsi_get_pkg_sender(dsi_config);
if (!sender || !data) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs);
}
int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode,
bool hs)
{
if (!dsi_config || !mode) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs);
}
int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config,
u32 *result, bool hs)
{
if (!dsi_config || !result) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
return mdfld_dsi_get_panel_status(dsi_config, 0x0f, result, hs);
}
/*
* NOTE: this function was used by OSPM.
* TODO: will be removed later, should work out display interfaces for OSPM
*/
void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe)
{
if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
DRM_ERROR("Invalid parameters\n");
return;
}
mdfld_dsi_dpi_controller_init(dsi_config, pipe);
}
static void mdfld_dsi_connector_save(struct drm_connector *connector)
{
}
static void mdfld_dsi_connector_restore(struct drm_connector *connector)
{
}
/* FIXME: start using the force parameter */
static enum drm_connector_status
mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
{
struct mdfld_dsi_connector *dsi_connector
= mdfld_dsi_connector(connector);
dsi_connector->status = connector_status_connected;
return dsi_connector->status;
}
static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
struct drm_encoder *encoder = connector->encoder;
struct backlight_device *psb_bd;
if (!strcmp(property->name, "scaling mode") && encoder) {
struct psb_intel_crtc *psb_crtc =
to_psb_intel_crtc(encoder->crtc);
bool centerechange;
uint64_t val;
if (!psb_crtc)
goto set_prop_error;
switch (value) {
case DRM_MODE_SCALE_FULLSCREEN:
break;
case DRM_MODE_SCALE_NO_SCALE:
break;
case DRM_MODE_SCALE_ASPECT:
break;
default:
goto set_prop_error;
}
if (drm_connector_property_get_value(connector, property, &val))
goto set_prop_error;
if (val == value)
goto set_prop_done;
if (drm_connector_property_set_value(connector,
property, value))
goto set_prop_error;
centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
(value == DRM_MODE_SCALE_NO_SCALE);
if (psb_crtc->saved_mode.hdisplay != 0 &&
psb_crtc->saved_mode.vdisplay != 0) {
if (centerechange) {
if (!drm_crtc_helper_set_mode(encoder->crtc,
&psb_crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
encoder->crtc->fb))
goto set_prop_error;
} else {
struct drm_encoder_helper_funcs *funcs =
encoder->helper_private;
funcs->mode_set(encoder,
&psb_crtc->saved_mode,
&psb_crtc->saved_adjusted_mode);
}
}
} else if (!strcmp(property->name, "backlight") && encoder) {
if (drm_connector_property_set_value(connector, property,
value))
goto set_prop_error;
else {
psb_bd = mdfld_get_backlight_device();
if (psb_bd) {
psb_bd->props.brightness = value;
mdfld_set_brightness(psb_bd);
}
}
}
set_prop_done:
return 0;
set_prop_error:
return -1;
}
static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
{
struct mdfld_dsi_connector *dsi_connector =
mdfld_dsi_connector(connector);
struct mdfld_dsi_pkg_sender *sender;
if (!dsi_connector)
return;
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
sender = dsi_connector->pkg_sender;
mdfld_dsi_pkg_sender_destroy(sender);
kfree(dsi_connector);
}
static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
{
struct mdfld_dsi_connector *dsi_connector =
mdfld_dsi_connector(connector);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_get_config(dsi_connector);
struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
struct drm_display_mode *dup_mode = NULL;
struct drm_device *dev = connector->dev;
connector->display_info.min_vfreq = 0;
connector->display_info.max_vfreq = 200;
connector->display_info.min_hfreq = 0;
connector->display_info.max_hfreq = 200;
if (fixed_mode) {
dev_dbg(dev->dev, "fixed_mode %dx%d\n",
fixed_mode->hdisplay, fixed_mode->vdisplay);
dup_mode = drm_mode_duplicate(dev, fixed_mode);
drm_mode_probed_add(connector, dup_mode);
return 1;
}
DRM_ERROR("Didn't get any modes!\n");
return 0;
}
static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct mdfld_dsi_connector *dsi_connector =
mdfld_dsi_connector(connector);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_get_config(dsi_connector);
struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
/**
* FIXME: current DC has no fitting unit, reject any mode setting
* request
* Will figure out a way to do up-scaling(pannel fitting) later.
**/
if (fixed_mode) {
if (mode->hdisplay != fixed_mode->hdisplay)
return MODE_PANEL;
if (mode->vdisplay != fixed_mode->vdisplay)
return MODE_PANEL;
}
return MODE_OK;
}
static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
{
if (mode == connector->dpms)
return;
/*first, execute dpms*/
drm_helper_connector_dpms(connector, mode);
}
static struct drm_encoder *mdfld_dsi_connector_best_encoder(
struct drm_connector *connector)
{
struct mdfld_dsi_connector *dsi_connector =
mdfld_dsi_connector(connector);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_get_config(dsi_connector);
return &dsi_config->encoder->base.base;
}
/*DSI connector funcs*/
static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
.dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
.save = mdfld_dsi_connector_save,
.restore = mdfld_dsi_connector_restore,
.detect = mdfld_dsi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = mdfld_dsi_connector_set_property,
.destroy = mdfld_dsi_connector_destroy,
};
/*DSI connector helper funcs*/
static const struct drm_connector_helper_funcs
mdfld_dsi_connector_helper_funcs = {
.get_modes = mdfld_dsi_connector_get_modes,
.mode_valid = mdfld_dsi_connector_mode_valid,
.best_encoder = mdfld_dsi_connector_best_encoder,
};
static int mdfld_dsi_get_default_config(struct drm_device *dev,
struct mdfld_dsi_config *config, int pipe)
{
if (!dev || !config) {
DRM_ERROR("Invalid parameters");
return -EINVAL;
}
config->bpp = 24;
if (mdfld_get_panel_type(dev, pipe) == TC35876X)
config->lane_count = 4;
else
config->lane_count = 2;
config->channel_num = 0;
if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
config->video_mode =
MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
else
config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
return 0;
}
int mdfld_dsi_panel_reset(int pipe)
{
unsigned gpio;
int ret = 0;
switch (pipe) {
case 0:
gpio = 128;
break;
case 2:
gpio = 34;
break;
default:
DRM_ERROR("Invalid output\n");
return -EINVAL;
}
ret = gpio_request(gpio, "gfx");
if (ret) {
DRM_ERROR("gpio_rqueset failed\n");
return ret;
}
ret = gpio_direction_output(gpio, 1);
if (ret) {
DRM_ERROR("gpio_direction_output failed\n");
goto gpio_error;
}
gpio_get_value(128);
gpio_error:
if (gpio_is_valid(gpio))
gpio_free(gpio);
return ret;
}
/*
* MIPI output init
* @dev drm device
* @pipe pipe number. 0 or 2
* @config
*
* Do the initialization of a MIPI output, including create DRM mode objects
* initialization of DSI output on @pipe
*/
void mdfld_dsi_output_init(struct drm_device *dev,
int pipe,
struct mdfld_dsi_config *config,
const struct panel_funcs *p_vid_funcs)
{
struct mdfld_dsi_config *dsi_config;
struct mdfld_dsi_connector *dsi_connector;
struct drm_connector *connector;
struct mdfld_dsi_encoder *encoder;
struct drm_psb_private *dev_priv = dev->dev_private;
struct panel_info dsi_panel_info;
u32 width_mm, height_mm;
dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
if (!dev || ((pipe != 0) && (pipe != 2))) {
DRM_ERROR("Invalid parameter\n");
return;
}
/*create a new connetor*/
dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
if (!dsi_connector) {
DRM_ERROR("No memory");
return;
}
dsi_connector->pipe = pipe;
/*set DSI config*/
if (config)
dsi_config = config;
else {
dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
GFP_KERNEL);
if (!dsi_config) {
DRM_ERROR("cannot allocate memory for DSI config\n");
goto dsi_init_err0;
}
mdfld_dsi_get_default_config(dev, dsi_config, pipe);
}
dsi_connector->private = dsi_config;
dsi_config->changed = 1;
dsi_config->dev = dev;
dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
goto dsi_init_err0;
width_mm = dsi_panel_info.width_mm;
height_mm = dsi_panel_info.height_mm;
dsi_config->mode = dsi_config->fixed_mode;
dsi_config->connector = dsi_connector;
if (!dsi_config->fixed_mode) {
DRM_ERROR("No pannel fixed mode was found\n");
goto dsi_init_err0;
}
if (pipe && dev_priv->dsi_configs[0]) {
dsi_config->dvr_ic_inited = 0;
dev_priv->dsi_configs[1] = dsi_config;
} else if (pipe == 0) {
dsi_config->dvr_ic_inited = 1;
dev_priv->dsi_configs[0] = dsi_config;
} else {
DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
goto dsi_init_err0;
}
connector = &dsi_connector->base.base;
drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
connector->display_info.width_mm = width_mm;
connector->display_info.height_mm = height_mm;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
/*attach properties*/
drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property,
DRM_MODE_SCALE_FULLSCREEN);
drm_connector_attach_property(connector,
dev_priv->backlight_property,
MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
/*init DSI package sender on this output*/
if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
DRM_ERROR("Package Sender initialization failed on pipe %d\n",
pipe);
goto dsi_init_err0;
}
encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
if (!encoder) {
DRM_ERROR("Create DPI encoder failed\n");
goto dsi_init_err1;
}
encoder->private = dsi_config;
dsi_config->encoder = encoder;
encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
INTEL_OUTPUT_MIPI2;
drm_sysfs_connector_add(connector);
return;
/*TODO: add code to destroy outputs on error*/
dsi_init_err1:
/*destroy sender*/
mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
drm_connector_cleanup(connector);
kfree(dsi_config->fixed_mode);
kfree(dsi_config);
dsi_init_err0:
kfree(dsi_connector);
}

View File

@ -0,0 +1,389 @@
/*
* 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:
* jim liu <jim.liu@intel.com>
* Jackie Li<yaodong.li@intel.com>
*/
#ifndef __MDFLD_DSI_OUTPUT_H__
#define __MDFLD_DSI_OUTPUT_H__
#include <linux/backlight.h>
#include <linux/version.h>
#include <drm/drmP.h>
#include <drm/drm.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include "psb_drv.h"
#include "psb_intel_drv.h"
#include "psb_intel_reg.h"
#include "mdfld_output.h"
#include <asm/mrst.h>
#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
#define FLD_MOD(orig, val, start, end) \
(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
#define REG_FLD_MOD(reg, val, start, end) \
REG_WRITE(reg, FLD_MOD(REG_READ(reg), val, start, end))
static inline int REGISTER_FLD_WAIT(struct drm_device *dev, u32 reg,
u32 val, int start, int end)
{
int t = 100000;
while (FLD_GET(REG_READ(reg), start, end) != val) {
if (--t == 0)
return 1;
}
return 0;
}
#define REG_FLD_WAIT(reg, val, start, end) \
REGISTER_FLD_WAIT(dev, reg, val, start, end)
#define REG_BIT_WAIT(reg, val, bitnum) \
REGISTER_FLD_WAIT(dev, reg, val, bitnum, bitnum)
#define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100
#ifdef DEBUG
#define CHECK_PIPE(pipe) ({ \
const typeof(pipe) __pipe = (pipe); \
BUG_ON(__pipe != 0 && __pipe != 2); \
__pipe; })
#else
#define CHECK_PIPE(pipe) (pipe)
#endif
/*
* Actual MIPIA->MIPIC reg offset is 0x800, value 0x400 is valid for 0 and 2
*/
#define REG_OFFSET(pipe) (CHECK_PIPE(pipe) * 0x400)
/* mdfld DSI controller registers */
#define MIPI_DEVICE_READY_REG(pipe) (0xb000 + REG_OFFSET(pipe))
#define MIPI_INTR_STAT_REG(pipe) (0xb004 + REG_OFFSET(pipe))
#define MIPI_INTR_EN_REG(pipe) (0xb008 + REG_OFFSET(pipe))
#define MIPI_DSI_FUNC_PRG_REG(pipe) (0xb00c + REG_OFFSET(pipe))
#define MIPI_HS_TX_TIMEOUT_REG(pipe) (0xb010 + REG_OFFSET(pipe))
#define MIPI_LP_RX_TIMEOUT_REG(pipe) (0xb014 + REG_OFFSET(pipe))
#define MIPI_TURN_AROUND_TIMEOUT_REG(pipe) (0xb018 + REG_OFFSET(pipe))
#define MIPI_DEVICE_RESET_TIMER_REG(pipe) (0xb01c + REG_OFFSET(pipe))
#define MIPI_DPI_RESOLUTION_REG(pipe) (0xb020 + REG_OFFSET(pipe))
#define MIPI_DBI_FIFO_THROTTLE_REG(pipe) (0xb024 + REG_OFFSET(pipe))
#define MIPI_HSYNC_COUNT_REG(pipe) (0xb028 + REG_OFFSET(pipe))
#define MIPI_HBP_COUNT_REG(pipe) (0xb02c + REG_OFFSET(pipe))
#define MIPI_HFP_COUNT_REG(pipe) (0xb030 + REG_OFFSET(pipe))
#define MIPI_HACTIVE_COUNT_REG(pipe) (0xb034 + REG_OFFSET(pipe))
#define MIPI_VSYNC_COUNT_REG(pipe) (0xb038 + REG_OFFSET(pipe))
#define MIPI_VBP_COUNT_REG(pipe) (0xb03c + REG_OFFSET(pipe))
#define MIPI_VFP_COUNT_REG(pipe) (0xb040 + REG_OFFSET(pipe))
#define MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe) (0xb044 + REG_OFFSET(pipe))
#define MIPI_DPI_CONTROL_REG(pipe) (0xb048 + REG_OFFSET(pipe))
#define MIPI_DPI_DATA_REG(pipe) (0xb04c + REG_OFFSET(pipe))
#define MIPI_INIT_COUNT_REG(pipe) (0xb050 + REG_OFFSET(pipe))
#define MIPI_MAX_RETURN_PACK_SIZE_REG(pipe) (0xb054 + REG_OFFSET(pipe))
#define MIPI_VIDEO_MODE_FORMAT_REG(pipe) (0xb058 + REG_OFFSET(pipe))
#define MIPI_EOT_DISABLE_REG(pipe) (0xb05c + REG_OFFSET(pipe))
#define MIPI_LP_BYTECLK_REG(pipe) (0xb060 + REG_OFFSET(pipe))
#define MIPI_LP_GEN_DATA_REG(pipe) (0xb064 + REG_OFFSET(pipe))
#define MIPI_HS_GEN_DATA_REG(pipe) (0xb068 + REG_OFFSET(pipe))
#define MIPI_LP_GEN_CTRL_REG(pipe) (0xb06c + REG_OFFSET(pipe))
#define MIPI_HS_GEN_CTRL_REG(pipe) (0xb070 + REG_OFFSET(pipe))
#define MIPI_GEN_FIFO_STAT_REG(pipe) (0xb074 + REG_OFFSET(pipe))
#define MIPI_HS_LS_DBI_ENABLE_REG(pipe) (0xb078 + REG_OFFSET(pipe))
#define MIPI_DPHY_PARAM_REG(pipe) (0xb080 + REG_OFFSET(pipe))
#define MIPI_DBI_BW_CTRL_REG(pipe) (0xb084 + REG_OFFSET(pipe))
#define MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe) (0xb088 + REG_OFFSET(pipe))
#define MIPI_CTRL_REG(pipe) (0xb104 + REG_OFFSET(pipe))
#define MIPI_DATA_ADD_REG(pipe) (0xb108 + REG_OFFSET(pipe))
#define MIPI_DATA_LEN_REG(pipe) (0xb10c + REG_OFFSET(pipe))
#define MIPI_CMD_ADD_REG(pipe) (0xb110 + REG_OFFSET(pipe))
#define MIPI_CMD_LEN_REG(pipe) (0xb114 + REG_OFFSET(pipe))
/* non-uniform reg offset */
#define MIPI_PORT_CONTROL(pipe) (CHECK_PIPE(pipe) ? MIPI_C : MIPI)
#define DSI_DEVICE_READY (0x1)
#define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1)
#define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1)
#define DSI_POWER_STATE_ULPS_OFFSET (0x1)
#define DSI_ONE_DATA_LANE (0x1)
#define DSI_TWO_DATA_LANE (0x2)
#define DSI_THREE_DATA_LANE (0X3)
#define DSI_FOUR_DATA_LANE (0x4)
#define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3)
#define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5)
#define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7)
#define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7)
#define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7)
#define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7)
#define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13)
#define DSI_INTR_STATE_RXSOTERROR BIT(0)
#define DSI_INTR_STATE_SPL_PKG_SENT BIT(30)
#define DSI_INTR_STATE_TE BIT(31)
#define DSI_HS_TX_TIMEOUT_MASK (0xffffff)
#define DSI_LP_RX_TIMEOUT_MASK (0xffffff)
#define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f)
#define DSI_RESET_TIMER_MASK (0xffff)
#define DSI_DBI_FIFO_WM_HALF (0x0)
#define DSI_DBI_FIFO_WM_QUARTER (0x1)
#define DSI_DBI_FIFO_WM_LOW (0x2)
#define DSI_DPI_TIMING_MASK (0xffff)
#define DSI_INIT_TIMER_MASK (0xffff)
#define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff)
#define DSI_LP_BYTECLK_MASK (0x0ffff)
#define DSI_HS_CTRL_GEN_SHORT_W0 (0x03)
#define DSI_HS_CTRL_GEN_SHORT_W1 (0x13)
#define DSI_HS_CTRL_GEN_SHORT_W2 (0x23)
#define DSI_HS_CTRL_GEN_R0 (0x04)
#define DSI_HS_CTRL_GEN_R1 (0x14)
#define DSI_HS_CTRL_GEN_R2 (0x24)
#define DSI_HS_CTRL_GEN_LONG_W (0x29)
#define DSI_HS_CTRL_MCS_SHORT_W0 (0x05)
#define DSI_HS_CTRL_MCS_SHORT_W1 (0x15)
#define DSI_HS_CTRL_MCS_R0 (0x06)
#define DSI_HS_CTRL_MCS_LONG_W (0x39)
#define DSI_HS_CTRL_VC_OFFSET (0x06)
#define DSI_HS_CTRL_WC_OFFSET (0x08)
#define DSI_FIFO_GEN_HS_DATA_FULL BIT(0)
#define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY BIT(1)
#define DSI_FIFO_GEN_HS_DATA_EMPTY BIT(2)
#define DSI_FIFO_GEN_LP_DATA_FULL BIT(8)
#define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY BIT(9)
#define DSI_FIFO_GEN_LP_DATA_EMPTY BIT(10)
#define DSI_FIFO_GEN_HS_CTRL_FULL BIT(16)
#define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY BIT(17)
#define DSI_FIFO_GEN_HS_CTRL_EMPTY BIT(18)
#define DSI_FIFO_GEN_LP_CTRL_FULL BIT(24)
#define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY BIT(25)
#define DSI_FIFO_GEN_LP_CTRL_EMPTY BIT(26)
#define DSI_FIFO_DBI_EMPTY BIT(27)
#define DSI_FIFO_DPI_EMPTY BIT(28)
#define DSI_DBI_HS_LP_SWITCH_MASK (0x1)
#define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0)
#define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16)
#define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001)
#define DSI_DPI_CTRL_HS_TURN_ON (0x00000002)
/*dsi power modes*/
#define DSI_POWER_MODE_DISPLAY_ON BIT(2)
#define DSI_POWER_MODE_NORMAL_ON BIT(3)
#define DSI_POWER_MODE_SLEEP_OUT BIT(4)
#define DSI_POWER_MODE_PARTIAL_ON BIT(5)
#define DSI_POWER_MODE_IDLE_ON BIT(6)
enum {
MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1,
MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2,
MDFLD_DSI_VIDEO_BURST_MODE = 3,
};
#define DSI_DPI_COMPLETE_LAST_LINE BIT(2)
#define DSI_DPI_DISABLE_BTA BIT(3)
struct mdfld_dsi_connector_state {
u32 mipi_ctrl_reg;
};
struct mdfld_dsi_encoder_state {
};
struct mdfld_dsi_connector {
struct psb_intel_connector base;
int pipe;
void *private;
void *pkg_sender;
/* Connection status */
enum drm_connector_status status;
};
struct mdfld_dsi_encoder {
struct psb_intel_encoder base;
void *private;
};
/*
* DSI config, consists of one DSI connector, two DSI encoders.
* DRM will pick up on DSI encoder basing on differents configs.
*/
struct mdfld_dsi_config {
struct drm_device *dev;
struct drm_display_mode *fixed_mode;
struct drm_display_mode *mode;
struct mdfld_dsi_connector *connector;
struct mdfld_dsi_encoder *encoder;
int changed;
int bpp;
int lane_count;
/*Virtual channel number for this encoder*/
int channel_num;
/*video mode configure*/
int video_mode;
int dvr_ic_inited;
};
static inline struct mdfld_dsi_connector *mdfld_dsi_connector(
struct drm_connector *connector)
{
struct psb_intel_connector *psb_connector;
psb_connector = to_psb_intel_connector(connector);
return container_of(psb_connector, struct mdfld_dsi_connector, base);
}
static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder(
struct drm_encoder *encoder)
{
struct psb_intel_encoder *psb_encoder;
psb_encoder = to_psb_intel_encoder(encoder);
return container_of(psb_encoder, struct mdfld_dsi_encoder, base);
}
static inline struct mdfld_dsi_config *
mdfld_dsi_get_config(struct mdfld_dsi_connector *connector)
{
if (!connector)
return NULL;
return (struct mdfld_dsi_config *)connector->private;
}
static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config)
{
struct mdfld_dsi_connector *dsi_connector;
if (!config)
return NULL;
dsi_connector = config->connector;
if (!dsi_connector)
return NULL;
return dsi_connector->pkg_sender;
}
static inline struct mdfld_dsi_config *
mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder)
{
if (!encoder)
return NULL;
return (struct mdfld_dsi_config *)encoder->private;
}
static inline struct mdfld_dsi_connector *
mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder)
{
struct mdfld_dsi_config *config;
if (!encoder)
return NULL;
config = mdfld_dsi_encoder_get_config(encoder);
if (!config)
return NULL;
return config->connector;
}
static inline void *mdfld_dsi_encoder_get_pkg_sender(
struct mdfld_dsi_encoder *encoder)
{
struct mdfld_dsi_config *dsi_config;
dsi_config = mdfld_dsi_encoder_get_config(encoder);
if (!dsi_config)
return NULL;
return mdfld_dsi_get_pkg_sender(dsi_config);
}
static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder)
{
struct mdfld_dsi_connector *connector;
if (!encoder)
return -1;
connector = mdfld_dsi_encoder_get_connector(encoder);
if (!connector)
return -1;
return connector->pipe;
}
/* Export functions */
extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev,
u32 gen_fifo_stat_reg, u32 fifo_stat);
extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config,
int pipe);
extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe,
int level);
extern void mdfld_dsi_output_init(struct drm_device *dev,
int pipe,
struct mdfld_dsi_config *config,
const struct panel_funcs *p_vid_funcs);
extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config,
int pipe);
extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config,
u32 *mode, bool hs);
extern int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config,
u32 *result, bool hs);
extern int mdfld_dsi_panel_reset(int pipe);
#endif /*__MDFLD_DSI_OUTPUT_H__*/

View File

@ -0,0 +1,694 @@
/*
* 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:
* Jackie Li<yaodong.li@intel.com>
*/
#include <linux/freezer.h>
#include "mdfld_dsi_output.h"
#include "mdfld_dsi_pkg_sender.h"
#include "mdfld_dsi_dpi.h"
#define MDFLD_DSI_READ_MAX_COUNT 5000
enum data_type {
DSI_DT_GENERIC_SHORT_WRITE_0 = 0x03,
DSI_DT_GENERIC_SHORT_WRITE_1 = 0x13,
DSI_DT_GENERIC_SHORT_WRITE_2 = 0x23,
DSI_DT_GENERIC_READ_0 = 0x04,
DSI_DT_GENERIC_READ_1 = 0x14,
DSI_DT_GENERIC_READ_2 = 0x24,
DSI_DT_GENERIC_LONG_WRITE = 0x29,
DSI_DT_DCS_SHORT_WRITE_0 = 0x05,
DSI_DT_DCS_SHORT_WRITE_1 = 0x15,
DSI_DT_DCS_READ = 0x06,
DSI_DT_DCS_LONG_WRITE = 0x39,
};
enum {
MDFLD_DSI_PANEL_MODE_SLEEP = 0x1,
};
enum {
MDFLD_DSI_PKG_SENDER_FREE = 0x0,
MDFLD_DSI_PKG_SENDER_BUSY = 0x1,
};
static const char *const dsi_errors[] = {
"RX SOT Error",
"RX SOT Sync Error",
"RX EOT Sync Error",
"RX Escape Mode Entry Error",
"RX LP TX Sync Error",
"RX HS Receive Timeout Error",
"RX False Control Error",
"RX ECC Single Bit Error",
"RX ECC Multibit Error",
"RX Checksum Error",
"RX DSI Data Type Not Recognised",
"RX DSI VC ID Invalid",
"TX False Control Error",
"TX ECC Single Bit Error",
"TX ECC Multibit Error",
"TX Checksum Error",
"TX DSI Data Type Not Recognised",
"TX DSI VC ID invalid",
"High Contention",
"Low contention",
"DPI FIFO Under run",
"HS TX Timeout",
"LP RX Timeout",
"Turn Around ACK Timeout",
"ACK With No Error",
"RX Invalid TX Length",
"RX Prot Violation",
"HS Generic Write FIFO Full",
"LP Generic Write FIFO Full",
"Generic Read Data Avail"
"Special Packet Sent",
"Tearing Effect",
};
static inline int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender,
u32 mask)
{
struct drm_device *dev = sender->dev;
u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg;
int retry = 0xffff;
while (retry--) {
if ((mask & REG_READ(gen_fifo_stat_reg)) == mask)
return 0;
udelay(100);
}
DRM_ERROR("fifo is NOT empty 0x%08x\n", REG_READ(gen_fifo_stat_reg));
return -EIO;
}
static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
{
return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(10) | BIT(18) |
BIT(26) | BIT(27) | BIT(28)));
}
static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
{
return wait_for_gen_fifo_empty(sender, (BIT(10) | BIT(26)));
}
static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
{
return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(18)));
}
static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask)
{
u32 intr_stat_reg = sender->mipi_intr_stat_reg;
struct drm_device *dev = sender->dev;
dev_dbg(sender->dev->dev, "Handling error 0x%08x\n", mask);
switch (mask) {
case BIT(0):
case BIT(1):
case BIT(2):
case BIT(3):
case BIT(4):
case BIT(5):
case BIT(6):
case BIT(7):
case BIT(8):
case BIT(9):
case BIT(10):
case BIT(11):
case BIT(12):
case BIT(13):
dev_dbg(sender->dev->dev, "No Action required\n");
break;
case BIT(14):
/*wait for all fifo empty*/
/*wait_for_all_fifos_empty(sender)*/;
break;
case BIT(15):
dev_dbg(sender->dev->dev, "No Action required\n");
break;
case BIT(16):
break;
case BIT(17):
break;
case BIT(18):
case BIT(19):
dev_dbg(sender->dev->dev, "High/Low contention detected\n");
/*wait for contention recovery time*/
/*mdelay(10);*/
/*wait for all fifo empty*/
if (0)
wait_for_all_fifos_empty(sender);
break;
case BIT(20):
dev_dbg(sender->dev->dev, "No Action required\n");
break;
case BIT(21):
/*wait for all fifo empty*/
/*wait_for_all_fifos_empty(sender);*/
break;
case BIT(22):
break;
case BIT(23):
case BIT(24):
case BIT(25):
case BIT(26):
case BIT(27):
dev_dbg(sender->dev->dev, "HS Gen fifo full\n");
REG_WRITE(intr_stat_reg, mask);
wait_for_hs_fifos_empty(sender);
break;
case BIT(28):
dev_dbg(sender->dev->dev, "LP Gen fifo full\n");
REG_WRITE(intr_stat_reg, mask);
wait_for_lp_fifos_empty(sender);
break;
case BIT(29):
case BIT(30):
case BIT(31):
dev_dbg(sender->dev->dev, "No Action required\n");
break;
}
if (mask & REG_READ(intr_stat_reg))
dev_dbg(sender->dev->dev,
"Cannot clean interrupt 0x%08x\n", mask);
return 0;
}
static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender)
{
struct drm_device *dev = sender->dev;
u32 intr_stat_reg = sender->mipi_intr_stat_reg;
u32 mask;
u32 intr_stat;
int i;
int err = 0;
intr_stat = REG_READ(intr_stat_reg);
for (i = 0; i < 32; i++) {
mask = (0x00000001UL) << i;
if (intr_stat & mask) {
dev_dbg(sender->dev->dev, "[DSI]: %s\n", dsi_errors[i]);
err = handle_dsi_error(sender, mask);
if (err)
DRM_ERROR("Cannot handle error\n");
}
}
return err;
}
static int send_short_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
u8 cmd, u8 param, bool hs)
{
struct drm_device *dev = sender->dev;
u32 ctrl_reg;
u32 val;
u8 virtual_channel = 0;
if (hs) {
ctrl_reg = sender->mipi_hs_gen_ctrl_reg;
/* FIXME: wait_for_hs_fifos_empty(sender); */
} else {
ctrl_reg = sender->mipi_lp_gen_ctrl_reg;
/* FIXME: wait_for_lp_fifos_empty(sender); */
}
val = FLD_VAL(param, 23, 16) | FLD_VAL(cmd, 15, 8) |
FLD_VAL(virtual_channel, 7, 6) | FLD_VAL(data_type, 5, 0);
REG_WRITE(ctrl_reg, val);
return 0;
}
static int send_long_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
u8 *data, int len, bool hs)
{
struct drm_device *dev = sender->dev;
u32 ctrl_reg;
u32 data_reg;
u32 val;
u8 *p;
u8 b1, b2, b3, b4;
u8 virtual_channel = 0;
int i;
if (hs) {
ctrl_reg = sender->mipi_hs_gen_ctrl_reg;
data_reg = sender->mipi_hs_gen_data_reg;
/* FIXME: wait_for_hs_fifos_empty(sender); */
} else {
ctrl_reg = sender->mipi_lp_gen_ctrl_reg;
data_reg = sender->mipi_lp_gen_data_reg;
/* FIXME: wait_for_lp_fifos_empty(sender); */
}
p = data;
for (i = 0; i < len / 4; i++) {
b1 = *p++;
b2 = *p++;
b3 = *p++;
b4 = *p++;
REG_WRITE(data_reg, b4 << 24 | b3 << 16 | b2 << 8 | b1);
}
i = len % 4;
if (i) {
b1 = 0; b2 = 0; b3 = 0;
switch (i) {
case 3:
b1 = *p++;
b2 = *p++;
b3 = *p++;
break;
case 2:
b1 = *p++;
b2 = *p++;
break;
case 1:
b1 = *p++;
break;
}
REG_WRITE(data_reg, b3 << 16 | b2 << 8 | b1);
}
val = FLD_VAL(len, 23, 8) | FLD_VAL(virtual_channel, 7, 6) |
FLD_VAL(data_type, 5, 0);
REG_WRITE(ctrl_reg, val);
return 0;
}
static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
u8 *data, u16 len)
{
u8 cmd;
switch (data_type) {
case DSI_DT_DCS_SHORT_WRITE_0:
case DSI_DT_DCS_SHORT_WRITE_1:
case DSI_DT_DCS_LONG_WRITE:
cmd = *data;
break;
default:
return 0;
}
/*this prevents other package sending while doing msleep*/
sender->status = MDFLD_DSI_PKG_SENDER_BUSY;
/*wait for 120 milliseconds in case exit_sleep_mode just be sent*/
if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) {
/*TODO: replace it with msleep later*/
mdelay(120);
}
if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) {
/*TODO: replace it with msleep later*/
mdelay(120);
}
return 0;
}
static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
u8 *data, u16 len)
{
u8 cmd;
switch (data_type) {
case DSI_DT_DCS_SHORT_WRITE_0:
case DSI_DT_DCS_SHORT_WRITE_1:
case DSI_DT_DCS_LONG_WRITE:
cmd = *data;
break;
default:
return 0;
}
/*update panel status*/
if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) {
sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP;
/*TODO: replace it with msleep later*/
mdelay(120);
} else if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) {
sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP;
/*TODO: replace it with msleep later*/
mdelay(120);
} else if (unlikely(cmd == DCS_SOFT_RESET)) {
/*TODO: replace it with msleep later*/
mdelay(5);
}
sender->status = MDFLD_DSI_PKG_SENDER_FREE;
return 0;
}
static int send_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
u8 *data, u16 len, bool hs)
{
int ret;
/*handle DSI error*/
ret = dsi_error_handler(sender);
if (ret) {
DRM_ERROR("Error handling failed\n");
return -EAGAIN;
}
/* send pkg */
if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) {
DRM_ERROR("sender is busy\n");
return -EAGAIN;
}
ret = send_pkg_prepare(sender, data_type, data, len);
if (ret) {
DRM_ERROR("send_pkg_prepare error\n");
return ret;
}
switch (data_type) {
case DSI_DT_GENERIC_SHORT_WRITE_0:
case DSI_DT_GENERIC_SHORT_WRITE_1:
case DSI_DT_GENERIC_SHORT_WRITE_2:
case DSI_DT_GENERIC_READ_0:
case DSI_DT_GENERIC_READ_1:
case DSI_DT_GENERIC_READ_2:
case DSI_DT_DCS_SHORT_WRITE_0:
case DSI_DT_DCS_SHORT_WRITE_1:
case DSI_DT_DCS_READ:
ret = send_short_pkg(sender, data_type, data[0], data[1], hs);
break;
case DSI_DT_GENERIC_LONG_WRITE:
case DSI_DT_DCS_LONG_WRITE:
ret = send_long_pkg(sender, data_type, data, len, hs);
break;
}
send_pkg_done(sender, data_type, data, len);
/*FIXME: should I query complete and fifo empty here?*/
return ret;
}
int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
u32 len, bool hs)
{
unsigned long flags;
if (!sender || !data || !len) {
DRM_ERROR("Invalid parameters\n");
return -EINVAL;
}
spin_lock_irqsave(&sender->lock, flags);
send_pkg(sender, DSI_DT_DCS_LONG_WRITE, data, len, hs);
spin_unlock_irqrestore(&sender->lock, flags);
return 0;
}
int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
u8 param, u8 param_num, bool hs)
{
u8 data[2];
unsigned long flags;
u8 data_type;
if (!sender) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
data[0] = cmd;
if (param_num) {
data_type = DSI_DT_DCS_SHORT_WRITE_1;
data[1] = param;
} else {
data_type = DSI_DT_DCS_SHORT_WRITE_0;
data[1] = 0;
}
spin_lock_irqsave(&sender->lock, flags);
send_pkg(sender, data_type, data, sizeof(data), hs);
spin_unlock_irqrestore(&sender->lock, flags);
return 0;
}
int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0,
u8 param1, u8 param_num, bool hs)
{
u8 data[2];
unsigned long flags;
u8 data_type;
if (!sender || param_num < 0 || param_num > 2) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
switch (param_num) {
case 0:
data_type = DSI_DT_GENERIC_SHORT_WRITE_0;
data[0] = 0;
data[1] = 0;
break;
case 1:
data_type = DSI_DT_GENERIC_SHORT_WRITE_1;
data[0] = param0;
data[1] = 0;
break;
case 2:
data_type = DSI_DT_GENERIC_SHORT_WRITE_2;
data[0] = param0;
data[1] = param1;
break;
}
spin_lock_irqsave(&sender->lock, flags);
send_pkg(sender, data_type, data, sizeof(data), hs);
spin_unlock_irqrestore(&sender->lock, flags);
return 0;
}
int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
u32 len, bool hs)
{
unsigned long flags;
if (!sender || !data || !len) {
DRM_ERROR("Invalid parameters\n");
return -EINVAL;
}
spin_lock_irqsave(&sender->lock, flags);
send_pkg(sender, DSI_DT_GENERIC_LONG_WRITE, data, len, hs);
spin_unlock_irqrestore(&sender->lock, flags);
return 0;
}
static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
u8 *data, u16 len, u32 *data_out, u16 len_out, bool hs)
{
unsigned long flags;
struct drm_device *dev = sender->dev;
int i;
u32 gen_data_reg;
int retry = MDFLD_DSI_READ_MAX_COUNT;
if (!sender || !data_out || !len_out) {
DRM_ERROR("Invalid parameters\n");
return -EINVAL;
}
/**
* do reading.
* 0) send out generic read request
* 1) polling read data avail interrupt
* 2) read data
*/
spin_lock_irqsave(&sender->lock, flags);
REG_WRITE(sender->mipi_intr_stat_reg, BIT(29));
if ((REG_READ(sender->mipi_intr_stat_reg) & BIT(29)))
DRM_ERROR("Can NOT clean read data valid interrupt\n");
/*send out read request*/
send_pkg(sender, data_type, data, len, hs);
/*polling read data avail interrupt*/
while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) {
udelay(100);
retry--;
}
if (!retry) {
spin_unlock_irqrestore(&sender->lock, flags);
return -ETIMEDOUT;
}
REG_WRITE(sender->mipi_intr_stat_reg, BIT(29));
/*read data*/
if (hs)
gen_data_reg = sender->mipi_hs_gen_data_reg;
else
gen_data_reg = sender->mipi_lp_gen_data_reg;
for (i = 0; i < len_out; i++)
*(data_out + i) = REG_READ(gen_data_reg);
spin_unlock_irqrestore(&sender->lock, flags);
return 0;
}
int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
u32 *data, u16 len, bool hs)
{
if (!sender || !data || !len) {
DRM_ERROR("Invalid parameters\n");
return -EINVAL;
}
return __read_panel_data(sender, DSI_DT_DCS_READ, &cmd, 1,
data, len, hs);
}
int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector,
int pipe)
{
struct mdfld_dsi_pkg_sender *pkg_sender;
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_get_config(dsi_connector);
struct drm_device *dev = dsi_config->dev;
u32 mipi_val = 0;
if (!dsi_connector) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
pkg_sender = dsi_connector->pkg_sender;
if (!pkg_sender || IS_ERR(pkg_sender)) {
pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender),
GFP_KERNEL);
if (!pkg_sender) {
DRM_ERROR("Create DSI pkg sender failed\n");
return -ENOMEM;
}
dsi_connector->pkg_sender = (void *)pkg_sender;
}
pkg_sender->dev = dev;
pkg_sender->dsi_connector = dsi_connector;
pkg_sender->pipe = pipe;
pkg_sender->pkg_num = 0;
pkg_sender->panel_mode = 0;
pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE;
/*init regs*/
if (pipe == 0) {
pkg_sender->dpll_reg = MRST_DPLL_A;
pkg_sender->dspcntr_reg = DSPACNTR;
pkg_sender->pipeconf_reg = PIPEACONF;
pkg_sender->dsplinoff_reg = DSPALINOFF;
pkg_sender->dspsurf_reg = DSPASURF;
pkg_sender->pipestat_reg = PIPEASTAT;
} else if (pipe == 2) {
pkg_sender->dpll_reg = MRST_DPLL_A;
pkg_sender->dspcntr_reg = DSPCCNTR;
pkg_sender->pipeconf_reg = PIPECCONF;
pkg_sender->dsplinoff_reg = DSPCLINOFF;
pkg_sender->dspsurf_reg = DSPCSURF;
pkg_sender->pipestat_reg = PIPECSTAT;
}
pkg_sender->mipi_intr_stat_reg = MIPI_INTR_STAT_REG(pipe);
pkg_sender->mipi_lp_gen_data_reg = MIPI_LP_GEN_DATA_REG(pipe);
pkg_sender->mipi_hs_gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe);
pkg_sender->mipi_lp_gen_ctrl_reg = MIPI_LP_GEN_CTRL_REG(pipe);
pkg_sender->mipi_hs_gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe);
pkg_sender->mipi_gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
pkg_sender->mipi_data_addr_reg = MIPI_DATA_ADD_REG(pipe);
pkg_sender->mipi_data_len_reg = MIPI_DATA_LEN_REG(pipe);
pkg_sender->mipi_cmd_addr_reg = MIPI_CMD_ADD_REG(pipe);
pkg_sender->mipi_cmd_len_reg = MIPI_CMD_LEN_REG(pipe);
/*init lock*/
spin_lock_init(&pkg_sender->lock);
if (mdfld_get_panel_type(dev, pipe) != TC35876X) {
/**
* For video mode, don't enable DPI timing output here,
* will init the DPI timing output during mode setting.
*/
mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
if (pipe == 0)
mipi_val |= 0x2;
REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi_val);
REG_READ(MIPI_PORT_CONTROL(pipe));
/* do dsi controller init */
mdfld_dsi_controller_init(dsi_config, pipe);
}
return 0;
}
void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender)
{
if (!sender || IS_ERR(sender))
return;
/*free*/
kfree(sender);
}

View File

@ -0,0 +1,92 @@
/*
* 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:
* Jackie Li<yaodong.li@intel.com>
*/
#ifndef __MDFLD_DSI_PKG_SENDER_H__
#define __MDFLD_DSI_PKG_SENDER_H__
#include <linux/kthread.h>
#define MDFLD_MAX_DCS_PARAM 8
struct mdfld_dsi_pkg_sender {
struct drm_device *dev;
struct mdfld_dsi_connector *dsi_connector;
u32 status;
u32 panel_mode;
int pipe;
spinlock_t lock;
u32 pkg_num;
/* Registers */
u32 dpll_reg;
u32 dspcntr_reg;
u32 pipeconf_reg;
u32 pipestat_reg;
u32 dsplinoff_reg;
u32 dspsurf_reg;
u32 mipi_intr_stat_reg;
u32 mipi_lp_gen_data_reg;
u32 mipi_hs_gen_data_reg;
u32 mipi_lp_gen_ctrl_reg;
u32 mipi_hs_gen_ctrl_reg;
u32 mipi_gen_fifo_stat_reg;
u32 mipi_data_addr_reg;
u32 mipi_data_len_reg;
u32 mipi_cmd_addr_reg;
u32 mipi_cmd_len_reg;
};
/* DCS definitions */
#define DCS_SOFT_RESET 0x01
#define DCS_ENTER_SLEEP_MODE 0x10
#define DCS_EXIT_SLEEP_MODE 0x11
#define DCS_SET_DISPLAY_OFF 0x28
#define DCS_SET_DISPLAY_ON 0x29
#define DCS_SET_COLUMN_ADDRESS 0x2a
#define DCS_SET_PAGE_ADDRESS 0x2b
#define DCS_WRITE_MEM_START 0x2c
#define DCS_SET_TEAR_OFF 0x34
#define DCS_SET_TEAR_ON 0x35
extern int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector,
int pipe);
extern void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender);
int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
u8 param, u8 param_num, bool hs);
int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
u32 len, bool hs);
int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0,
u8 param1, u8 param_num, bool hs);
int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
u32 len, bool hs);
/* Read interfaces */
int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
u32 *data, u16 len, bool hs);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 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, sublicensen
* 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:
* Thomas Eaton <thomas.g.eaton@intel.com>
* Scott Rowe <scott.m.rowe@intel.com>
*/
#include "mdfld_output.h"
#include "mdfld_dsi_dpi.h"
#include "mdfld_dsi_output.h"
#include "tc35876x-dsi-lvds.h"
int mdfld_get_panel_type(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
return dev_priv->mdfld_panel_id;
}
static void mdfld_init_panel(struct drm_device *dev, int mipi_pipe,
int p_type)
{
switch (p_type) {
case TPO_VID:
mdfld_dsi_output_init(dev, mipi_pipe, NULL,
&mdfld_tpo_vid_funcs);
break;
case TC35876X:
tc35876x_init(dev);
mdfld_dsi_output_init(dev, mipi_pipe, NULL,
&mdfld_tc35876x_funcs);
break;
case TMD_VID:
mdfld_dsi_output_init(dev, mipi_pipe, NULL,
&mdfld_tmd_vid_funcs);
break;
case HDMI:
/* if (dev_priv->mdfld_hdmi_present)
mdfld_hdmi_init(dev, &dev_priv->mode_dev); */
break;
}
}
int mdfld_output_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
/* FIXME: hardcoded for now */
dev_priv->mdfld_panel_id = TC35876X;
/* MIPI panel 1 */
mdfld_init_panel(dev, 0, dev_priv->mdfld_panel_id);
/* HDMI panel */
mdfld_init_panel(dev, 1, HDMI);
return 0;
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 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, sublicensen
* 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:
* Thomas Eaton <thomas.g.eaton@intel.com>
* Scott Rowe <scott.m.rowe@intel.com>
*/
#ifndef MDFLD_OUTPUT_H
#define MDFLD_OUTPUT_H
#include "psb_drv.h"
#define TPO_PANEL_WIDTH 84
#define TPO_PANEL_HEIGHT 46
#define TMD_PANEL_WIDTH 39
#define TMD_PANEL_HEIGHT 71
struct mdfld_dsi_config;
enum panel_type {
TPO_VID,
TMD_VID,
HDMI,
TC35876X,
};
struct panel_info {
u32 width_mm;
u32 height_mm;
/* Other info */
};
struct panel_funcs {
const struct drm_encoder_funcs *encoder_funcs;
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
struct drm_display_mode * (*get_config_mode)(struct drm_device *);
int (*get_panel_info)(struct drm_device *, int, struct panel_info *);
int (*reset)(int pipe);
void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe);
};
int mdfld_output_init(struct drm_device *dev);
struct backlight_device *mdfld_get_backlight_device(void);
int mdfld_set_brightness(struct backlight_device *bd);
int mdfld_get_panel_type(struct drm_device *dev, int pipe);
extern const struct drm_crtc_helper_funcs mdfld_helper_funcs;
extern const struct panel_funcs mdfld_tmd_vid_funcs;
extern const struct panel_funcs mdfld_tpo_vid_funcs;
extern void mdfld_disable_crtc(struct drm_device *dev, int pipe);
extern void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe);
extern void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe);
#endif

View File

@ -0,0 +1,201 @@
/*
* 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:
* Jim Liu <jim.liu@intel.com>
* Jackie Li<yaodong.li@intel.com>
* Gideon Eaton <eaton.
* Scott Rowe <scott.m.rowe@intel.com>
*/
#include "mdfld_dsi_dpi.h"
#include "mdfld_dsi_pkg_sender.h"
static struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev)
{
struct drm_display_mode *mode;
struct drm_psb_private *dev_priv = dev->dev_private;
struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
bool use_gct = false; /*Disable GCT for now*/
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode)
return NULL;
if (use_gct) {
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 << 8) | \
ti->vsync_offset_lo);
mode->vsync_end = \
mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \
ti->vsync_pulse_width_lo);
mode->vtotal = mode->vdisplay + \
((ti->vblank_hi << 8) | ti->vblank_lo);
mode->clock = ti->pixel_clock * 10;
dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
dev_dbg(dev->dev, "clock is %d\n", mode->clock);
} else {
mode->hdisplay = 480;
mode->vdisplay = 854;
mode->hsync_start = 487;
mode->hsync_end = 490;
mode->htotal = 499;
mode->vsync_start = 861;
mode->vsync_end = 865;
mode->vtotal = 873;
mode->clock = 33264;
}
drm_mode_set_name(mode);
drm_mode_set_crtcinfo(mode, 0);
mode->type |= DRM_MODE_TYPE_PREFERRED;
return mode;
}
static int tmd_vid_get_panel_info(struct drm_device *dev,
int pipe,
struct panel_info *pi)
{
if (!dev || !pi)
return -EINVAL;
pi->width_mm = TMD_PANEL_WIDTH;
pi->height_mm = TMD_PANEL_HEIGHT;
return 0;
}
/* ************************************************************************* *\
* FUNCTION: mdfld_init_TMD_MIPI
*
* DESCRIPTION: This function is called only by mrst_dsi_mode_set and
* restore_display_registers. since this function does not
* acquire the mutex, it is important that the calling function
* does!
\* ************************************************************************* */
/* FIXME: make the below data u8 instead of u32; note byte order! */
static u32 tmd_cmd_mcap_off[] = {0x000000b2};
static u32 tmd_cmd_enable_lane_switch[] = {0x000101ef};
static u32 tmd_cmd_set_lane_num[] = {0x006360ef};
static u32 tmd_cmd_pushing_clock0[] = {0x00cc2fef};
static u32 tmd_cmd_pushing_clock1[] = {0x00dd6eef};
static u32 tmd_cmd_set_mode[] = {0x000000b3};
static u32 tmd_cmd_set_sync_pulse_mode[] = {0x000961ef};
static u32 tmd_cmd_set_column[] = {0x0100002a, 0x000000df};
static u32 tmd_cmd_set_page[] = {0x0300002b, 0x00000055};
static u32 tmd_cmd_set_video_mode[] = {0x00000153};
/*no auto_bl,need add in furture*/
static u32 tmd_cmd_enable_backlight[] = {0x00005ab4};
static u32 tmd_cmd_set_backlight_dimming[] = {0x00000ebd};
static void mdfld_dsi_tmd_drv_ic_init(struct mdfld_dsi_config *dsi_config,
int pipe)
{
struct mdfld_dsi_pkg_sender *sender
= mdfld_dsi_get_pkg_sender(dsi_config);
DRM_INFO("Enter mdfld init TMD MIPI display.\n");
if (!sender) {
DRM_ERROR("Cannot get sender\n");
return;
}
if (dsi_config->dvr_ic_inited)
return;
msleep(3);
/* FIXME: make the below data u8 instead of u32; note byte order! */
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_mcap_off,
sizeof(tmd_cmd_mcap_off), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_enable_lane_switch,
sizeof(tmd_cmd_enable_lane_switch), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_lane_num,
sizeof(tmd_cmd_set_lane_num), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_pushing_clock0,
sizeof(tmd_cmd_pushing_clock0), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_pushing_clock1,
sizeof(tmd_cmd_pushing_clock1), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_mode,
sizeof(tmd_cmd_set_mode), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_sync_pulse_mode,
sizeof(tmd_cmd_set_sync_pulse_mode), false);
mdfld_dsi_send_mcs_long(sender, (u8 *) tmd_cmd_set_column,
sizeof(tmd_cmd_set_column), false);
mdfld_dsi_send_mcs_long(sender, (u8 *) tmd_cmd_set_page,
sizeof(tmd_cmd_set_page), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_video_mode,
sizeof(tmd_cmd_set_video_mode), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_enable_backlight,
sizeof(tmd_cmd_enable_backlight), false);
mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_backlight_dimming,
sizeof(tmd_cmd_set_backlight_dimming), false);
dsi_config->dvr_ic_inited = 1;
}
/*TPO DPI encoder helper funcs*/
static const struct drm_encoder_helper_funcs
mdfld_tpo_dpi_encoder_helper_funcs = {
.dpms = mdfld_dsi_dpi_dpms,
.mode_fixup = mdfld_dsi_dpi_mode_fixup,
.prepare = mdfld_dsi_dpi_prepare,
.mode_set = mdfld_dsi_dpi_mode_set,
.commit = mdfld_dsi_dpi_commit,
};
/*TPO DPI encoder funcs*/
static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
const struct panel_funcs mdfld_tmd_vid_funcs = {
.encoder_funcs = &mdfld_tpo_dpi_encoder_funcs,
.encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs,
.get_config_mode = &tmd_vid_get_config_mode,
.get_panel_info = tmd_vid_get_panel_info,
.reset = mdfld_dsi_panel_reset,
.drv_ic_init = mdfld_dsi_tmd_drv_ic_init,
};

View File

@ -0,0 +1,124 @@
/*
* 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:
* jim liu <jim.liu@intel.com>
* Jackie Li<yaodong.li@intel.com>
*/
#include "mdfld_dsi_dpi.h"
static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev)
{
struct drm_display_mode *mode;
struct drm_psb_private *dev_priv = dev->dev_private;
struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
bool use_gct = false;
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode)
return NULL;
if (use_gct) {
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 << 8) |
ti->vsync_offset_lo);
mode->vsync_end =
mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) |
ti->vsync_pulse_width_lo);
mode->vtotal = mode->vdisplay +
((ti->vblank_hi << 8) | ti->vblank_lo);
mode->clock = ti->pixel_clock * 10;
dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
dev_dbg(dev->dev, "clock is %d\n", mode->clock);
} else {
mode->hdisplay = 864;
mode->vdisplay = 480;
mode->hsync_start = 873;
mode->hsync_end = 876;
mode->htotal = 887;
mode->vsync_start = 487;
mode->vsync_end = 490;
mode->vtotal = 499;
mode->clock = 33264;
}
drm_mode_set_name(mode);
drm_mode_set_crtcinfo(mode, 0);
mode->type |= DRM_MODE_TYPE_PREFERRED;
return mode;
}
static int tpo_vid_get_panel_info(struct drm_device *dev,
int pipe,
struct panel_info *pi)
{
if (!dev || !pi)
return -EINVAL;
pi->width_mm = TPO_PANEL_WIDTH;
pi->height_mm = TPO_PANEL_HEIGHT;
return 0;
}
/*TPO DPI encoder helper funcs*/
static const struct drm_encoder_helper_funcs
mdfld_tpo_dpi_encoder_helper_funcs = {
.dpms = mdfld_dsi_dpi_dpms,
.mode_fixup = mdfld_dsi_dpi_mode_fixup,
.prepare = mdfld_dsi_dpi_prepare,
.mode_set = mdfld_dsi_dpi_mode_set,
.commit = mdfld_dsi_dpi_commit,
};
/*TPO DPI encoder funcs*/
static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
const struct panel_funcs mdfld_tpo_vid_funcs = {
.encoder_funcs = &mdfld_tpo_dpi_encoder_funcs,
.encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs,
.get_config_mode = &tpo_vid_get_config_mode,
.get_panel_info = tpo_vid_get_panel_info,
};

View File

@ -60,6 +60,16 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
/* Atom E620 */
{ 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
#endif
#if defined(CONFIG_DRM_MEDFIELD)
{0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
#endif
#if defined(CONFIG_DRM_GMA3600)
{ 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},

View File

@ -389,11 +389,79 @@ struct psb_state {
uint32_t savePWM_CONTROL_LOGIC;
};
struct medfield_state {
uint32_t saveDPLL_A;
uint32_t saveFPA0;
uint32_t savePIPEACONF;
uint32_t saveHTOTAL_A;
uint32_t saveHBLANK_A;
uint32_t saveHSYNC_A;
uint32_t saveVTOTAL_A;
uint32_t saveVBLANK_A;
uint32_t saveVSYNC_A;
uint32_t savePIPEASRC;
uint32_t saveDSPASTRIDE;
uint32_t saveDSPALINOFF;
uint32_t saveDSPATILEOFF;
uint32_t saveDSPASIZE;
uint32_t saveDSPAPOS;
uint32_t saveDSPASURF;
uint32_t saveDSPACNTR;
uint32_t saveDSPASTATUS;
uint32_t save_palette_a[256];
uint32_t saveMIPI;
uint32_t saveDPLL_B;
uint32_t saveFPB0;
uint32_t savePIPEBCONF;
uint32_t saveHTOTAL_B;
uint32_t saveHBLANK_B;
uint32_t saveHSYNC_B;
uint32_t saveVTOTAL_B;
uint32_t saveVBLANK_B;
uint32_t saveVSYNC_B;
uint32_t savePIPEBSRC;
uint32_t saveDSPBSTRIDE;
uint32_t saveDSPBLINOFF;
uint32_t saveDSPBTILEOFF;
uint32_t saveDSPBSIZE;
uint32_t saveDSPBPOS;
uint32_t saveDSPBSURF;
uint32_t saveDSPBCNTR;
uint32_t saveDSPBSTATUS;
uint32_t save_palette_b[256];
uint32_t savePIPECCONF;
uint32_t saveHTOTAL_C;
uint32_t saveHBLANK_C;
uint32_t saveHSYNC_C;
uint32_t saveVTOTAL_C;
uint32_t saveVBLANK_C;
uint32_t saveVSYNC_C;
uint32_t savePIPECSRC;
uint32_t saveDSPCSTRIDE;
uint32_t saveDSPCLINOFF;
uint32_t saveDSPCTILEOFF;
uint32_t saveDSPCSIZE;
uint32_t saveDSPCPOS;
uint32_t saveDSPCSURF;
uint32_t saveDSPCCNTR;
uint32_t saveDSPCSTATUS;
uint32_t save_palette_c[256];
uint32_t saveMIPI_C;
uint32_t savePFIT_CONTROL;
uint32_t savePFIT_PGM_RATIOS;
uint32_t saveHDMIPHYMISCCTL;
uint32_t saveHDMIB_CONTROL;
};
struct psb_save_area {
uint32_t saveBSM;
uint32_t saveVBT;
union {
struct psb_state psb;
struct medfield_state mdfld;
};
uint32_t saveBLC_PWM_CTL2;
uint32_t saveBLC_PWM_CTL;
@ -563,6 +631,24 @@ struct drm_psb_private {
/* 2D acceleration */
spinlock_t lock_2d;
/*
* Panel brightness
*/
int brightness;
int brightness_adjusted;
bool dsr_enable;
u32 dsr_fb_update;
bool dpi_panel_on[3];
void *dsi_configs[2];
u32 bpp;
u32 bpp2;
u32 pipeconf[3];
u32 dspcntr[3];
int mdfld_panel_id;
};
@ -758,6 +844,9 @@ extern const struct psb_ops psb_chip_ops;
/* oaktrail_device.c */
extern const struct psb_ops oaktrail_chip_ops;
/* mdlfd_device.c */
extern const struct psb_ops mdfld_chip_ops;
/* cdv_device.c */
extern const struct psb_ops cdv_chip_ops;

View File

@ -27,6 +27,8 @@
#include "psb_reg.h"
#include "psb_intel_reg.h"
#include "power.h"
#include "psb_irq.h"
#include "mdfld_output.h"
/*
* inline functions
@ -453,6 +455,11 @@ int psb_enable_vblank(struct drm_device *dev, int pipe)
uint32_t reg_val = 0;
uint32_t pipeconf_reg = mid_pipeconf(pipe);
/* Medfield is different - we should perhaps extract out vblank
and blacklight etc ops */
if (IS_MFLD(dev))
return mdfld_enable_te(dev, pipe);
if (gma_power_begin(dev, false)) {
reg_val = REG_READ(pipeconf_reg);
gma_power_end(dev);
@ -485,6 +492,8 @@ void psb_disable_vblank(struct drm_device *dev, int pipe)
struct drm_psb_private *dev_priv = dev->dev_private;
unsigned long irqflags;
if (IS_MFLD(dev))
mdfld_disable_te(dev, pipe);
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
if (pipe == 0)
@ -499,6 +508,55 @@ void psb_disable_vblank(struct drm_device *dev, int pipe)
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
}
/*
* It is used to enable TE interrupt
*/
int mdfld_enable_te(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) dev->dev_private;
unsigned long irqflags;
uint32_t reg_val = 0;
uint32_t pipeconf_reg = mid_pipeconf(pipe);
if (gma_power_begin(dev, false)) {
reg_val = REG_READ(pipeconf_reg);
gma_power_end(dev);
}
if (!(reg_val & PIPEACONF_ENABLE))
return -EINVAL;
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
mid_enable_pipe_event(dev_priv, pipe);
psb_enable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE);
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
return 0;
}
/*
* It is used to disable TE interrupt
*/
void mdfld_disable_te(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) dev->dev_private;
unsigned long irqflags;
if (!dev_priv->dsr_enable)
return;
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
mid_disable_pipe_event(dev_priv, pipe);
psb_disable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE);
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
}
/* Called from drm generic code, passed a 'crtc', which
* we use as a pipe index
*/

View File

@ -42,4 +42,6 @@ int psb_enable_vblank(struct drm_device *dev, int pipe);
void psb_disable_vblank(struct drm_device *dev, int pipe);
u32 psb_get_vblank_counter(struct drm_device *dev, int pipe);
int mdfld_enable_te(struct drm_device *dev, int pipe);
void mdfld_disable_te(struct drm_device *dev, int pipe);
#endif /* _SYSIRQ_H_ */

View File

@ -0,0 +1,829 @@
/*
* Copyright © 2011 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.
*
*/
#include "mdfld_dsi_dpi.h"
#include "mdfld_output.h"
#include "mdfld_dsi_pkg_sender.h"
#include "tc35876x-dsi-lvds.h"
#include <linux/i2c/tc35876x.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/intel_scu_ipc.h>
static struct i2c_client *tc35876x_client;
static struct i2c_client *cmi_lcd_i2c_client;
#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
/* DSI D-PHY Layer Registers */
#define D0W_DPHYCONTTX 0x0004
#define CLW_DPHYCONTRX 0x0020
#define D0W_DPHYCONTRX 0x0024
#define D1W_DPHYCONTRX 0x0028
#define D2W_DPHYCONTRX 0x002C
#define D3W_DPHYCONTRX 0x0030
#define COM_DPHYCONTRX 0x0038
#define CLW_CNTRL 0x0040
#define D0W_CNTRL 0x0044
#define D1W_CNTRL 0x0048
#define D2W_CNTRL 0x004C
#define D3W_CNTRL 0x0050
#define DFTMODE_CNTRL 0x0054
/* DSI PPI Layer Registers */
#define PPI_STARTPPI 0x0104
#define PPI_BUSYPPI 0x0108
#define PPI_LINEINITCNT 0x0110
#define PPI_LPTXTIMECNT 0x0114
#define PPI_LANEENABLE 0x0134
#define PPI_TX_RX_TA 0x013C
#define PPI_CLS_ATMR 0x0140
#define PPI_D0S_ATMR 0x0144
#define PPI_D1S_ATMR 0x0148
#define PPI_D2S_ATMR 0x014C
#define PPI_D3S_ATMR 0x0150
#define PPI_D0S_CLRSIPOCOUNT 0x0164
#define PPI_D1S_CLRSIPOCOUNT 0x0168
#define PPI_D2S_CLRSIPOCOUNT 0x016C
#define PPI_D3S_CLRSIPOCOUNT 0x0170
#define CLS_PRE 0x0180
#define D0S_PRE 0x0184
#define D1S_PRE 0x0188
#define D2S_PRE 0x018C
#define D3S_PRE 0x0190
#define CLS_PREP 0x01A0
#define D0S_PREP 0x01A4
#define D1S_PREP 0x01A8
#define D2S_PREP 0x01AC
#define D3S_PREP 0x01B0
#define CLS_ZERO 0x01C0
#define D0S_ZERO 0x01C4
#define D1S_ZERO 0x01C8
#define D2S_ZERO 0x01CC
#define D3S_ZERO 0x01D0
#define PPI_CLRFLG 0x01E0
#define PPI_CLRSIPO 0x01E4
#define HSTIMEOUT 0x01F0
#define HSTIMEOUTENABLE 0x01F4
/* DSI Protocol Layer Registers */
#define DSI_STARTDSI 0x0204
#define DSI_BUSYDSI 0x0208
#define DSI_LANEENABLE 0x0210
#define DSI_LANESTATUS0 0x0214
#define DSI_LANESTATUS1 0x0218
#define DSI_INTSTATUS 0x0220
#define DSI_INTMASK 0x0224
#define DSI_INTCLR 0x0228
#define DSI_LPTXTO 0x0230
/* DSI General Registers */
#define DSIERRCNT 0x0300
/* DSI Application Layer Registers */
#define APLCTRL 0x0400
#define RDPKTLN 0x0404
/* Video Path Registers */
#define VPCTRL 0x0450
#define HTIM1 0x0454
#define HTIM2 0x0458
#define VTIM1 0x045C
#define VTIM2 0x0460
#define VFUEN 0x0464
/* LVDS Registers */
#define LVMX0003 0x0480
#define LVMX0407 0x0484
#define LVMX0811 0x0488
#define LVMX1215 0x048C
#define LVMX1619 0x0490
#define LVMX2023 0x0494
#define LVMX2427 0x0498
#define LVCFG 0x049C
#define LVPHY0 0x04A0
#define LVPHY1 0x04A4
/* System Registers */
#define SYSSTAT 0x0500
#define SYSRST 0x0504
/* GPIO Registers */
/*#define GPIOC 0x0520*/
#define GPIOO 0x0524
#define GPIOI 0x0528
/* I2C Registers */
#define I2CTIMCTRL 0x0540
#define I2CMADDR 0x0544
#define WDATAQ 0x0548
#define RDATAQ 0x054C
/* Chip/Rev Registers */
#define IDREG 0x0580
/* Debug Registers */
#define DEBUG00 0x05A0
#define DEBUG01 0x05A4
/* Panel CABC registers */
#define PANEL_PWM_CONTROL 0x90
#define PANEL_FREQ_DIVIDER_HI 0x91
#define PANEL_FREQ_DIVIDER_LO 0x92
#define PANEL_DUTY_CONTROL 0x93
#define PANEL_MODIFY_RGB 0x94
#define PANEL_FRAMERATE_CONTROL 0x96
#define PANEL_PWM_MIN 0x97
#define PANEL_PWM_REF 0x98
#define PANEL_PWM_MAX 0x99
#define PANEL_ALLOW_DISTORT 0x9A
#define PANEL_BYPASS_PWMI 0x9B
/* Panel color management registers */
#define PANEL_CM_ENABLE 0x700
#define PANEL_CM_HUE 0x701
#define PANEL_CM_SATURATION 0x702
#define PANEL_CM_INTENSITY 0x703
#define PANEL_CM_BRIGHTNESS 0x704
#define PANEL_CM_CE_ENABLE 0x705
#define PANEL_CM_PEAK_EN 0x710
#define PANEL_CM_GAIN 0x711
#define PANEL_CM_HUETABLE_START 0x730
#define PANEL_CM_HUETABLE_END 0x747 /* inclusive */
/* Input muxing for registers LVMX0003...LVMX2427 */
enum {
INPUT_R0, /* 0 */
INPUT_R1,
INPUT_R2,
INPUT_R3,
INPUT_R4,
INPUT_R5,
INPUT_R6,
INPUT_R7,
INPUT_G0, /* 8 */
INPUT_G1,
INPUT_G2,
INPUT_G3,
INPUT_G4,
INPUT_G5,
INPUT_G6,
INPUT_G7,
INPUT_B0, /* 16 */
INPUT_B1,
INPUT_B2,
INPUT_B3,
INPUT_B4,
INPUT_B5,
INPUT_B6,
INPUT_B7,
INPUT_HSYNC, /* 24 */
INPUT_VSYNC,
INPUT_DE,
LOGIC_0,
/* 28...31 undefined */
};
#define INPUT_MUX(lvmx03, lvmx02, lvmx01, lvmx00) \
(FLD_VAL(lvmx03, 29, 24) | FLD_VAL(lvmx02, 20, 16) | \
FLD_VAL(lvmx01, 12, 8) | FLD_VAL(lvmx00, 4, 0))
/**
* tc35876x_regw - Write DSI-LVDS bridge register using I2C
* @client: struct i2c_client to use
* @reg: register address
* @value: value to write
*
* Returns 0 on success, or a negative error value.
*/
static int tc35876x_regw(struct i2c_client *client, u16 reg, u32 value)
{
int r;
u8 tx_data[] = {
/* NOTE: Register address big-endian, data little-endian. */
(reg >> 8) & 0xff,
reg & 0xff,
value & 0xff,
(value >> 8) & 0xff,
(value >> 16) & 0xff,
(value >> 24) & 0xff,
};
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.buf = tx_data,
.len = ARRAY_SIZE(tx_data),
},
};
r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (r < 0) {
dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x error %d\n",
__func__, reg, value, r);
return r;
}
if (r < ARRAY_SIZE(msgs)) {
dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x msgs %d\n",
__func__, reg, value, r);
return -EAGAIN;
}
dev_dbg(&client->dev, "%s: reg 0x%04x val 0x%08x\n",
__func__, reg, value);
return 0;
}
/**
* tc35876x_regr - Read DSI-LVDS bridge register using I2C
* @client: struct i2c_client to use
* @reg: register address
* @value: pointer for storing the value
*
* Returns 0 on success, or a negative error value.
*/
static int tc35876x_regr(struct i2c_client *client, u16 reg, u32 *value)
{
int r;
u8 tx_data[] = {
(reg >> 8) & 0xff,
reg & 0xff,
};
u8 rx_data[4];
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.buf = tx_data,
.len = ARRAY_SIZE(tx_data),
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.buf = rx_data,
.len = ARRAY_SIZE(rx_data),
},
};
r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (r < 0) {
dev_err(&client->dev, "%s: reg 0x%04x error %d\n", __func__,
reg, r);
return r;
}
if (r < ARRAY_SIZE(msgs)) {
dev_err(&client->dev, "%s: reg 0x%04x msgs %d\n", __func__,
reg, r);
return -EAGAIN;
}
*value = rx_data[0] << 24 | rx_data[1] << 16 |
rx_data[2] << 8 | rx_data[3];
dev_dbg(&client->dev, "%s: reg 0x%04x value 0x%08x\n", __func__,
reg, *value);
return 0;
}
void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state)
{
struct tc35876x_platform_data *pdata;
if (WARN(!tc35876x_client, "%s called before probe", __func__))
return;
dev_dbg(&tc35876x_client->dev, "%s: state %d\n", __func__, state);
pdata = dev_get_platdata(&tc35876x_client->dev);
if (pdata->gpio_bridge_reset == -1)
return;
if (state) {
gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0);
mdelay(10);
} else {
/* Pull MIPI Bridge reset pin to Low */
gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0);
mdelay(20);
/* Pull MIPI Bridge reset pin to High */
gpio_set_value_cansleep(pdata->gpio_bridge_reset, 1);
mdelay(40);
}
}
void tc35876x_configure_lvds_bridge(struct drm_device *dev)
{
struct i2c_client *i2c = tc35876x_client;
u32 ppi_lptxtimecnt;
u32 txtagocnt;
u32 txtasurecnt;
u32 id;
if (WARN(!tc35876x_client, "%s called before probe", __func__))
return;
dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
if (!tc35876x_regr(i2c, IDREG, &id))
dev_info(&tc35876x_client->dev, "tc35876x ID 0x%08x\n", id);
else
dev_err(&tc35876x_client->dev, "Cannot read ID\n");
ppi_lptxtimecnt = 4;
txtagocnt = (5 * ppi_lptxtimecnt - 3) / 4;
txtasurecnt = 3 * ppi_lptxtimecnt / 2;
tc35876x_regw(i2c, PPI_TX_RX_TA, FLD_VAL(txtagocnt, 26, 16) |
FLD_VAL(txtasurecnt, 10, 0));
tc35876x_regw(i2c, PPI_LPTXTIMECNT, FLD_VAL(ppi_lptxtimecnt, 10, 0));
tc35876x_regw(i2c, PPI_D0S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
tc35876x_regw(i2c, PPI_D1S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
tc35876x_regw(i2c, PPI_D2S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
tc35876x_regw(i2c, PPI_D3S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
/* Enabling MIPI & PPI lanes, Enable 4 lanes */
tc35876x_regw(i2c, PPI_LANEENABLE,
BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0));
tc35876x_regw(i2c, DSI_LANEENABLE,
BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0));
tc35876x_regw(i2c, PPI_STARTPPI, BIT(0));
tc35876x_regw(i2c, DSI_STARTDSI, BIT(0));
/* Setting LVDS output frequency */
tc35876x_regw(i2c, LVPHY0, FLD_VAL(1, 20, 16) |
FLD_VAL(2, 15, 14) | FLD_VAL(6, 4, 0)); /* 0x00048006 */
/* Setting video panel control register,0x00000120 VTGen=ON ?!?!? */
tc35876x_regw(i2c, VPCTRL, BIT(8) | BIT(5));
/* Horizontal back porch and horizontal pulse width. 0x00280028 */
tc35876x_regw(i2c, HTIM1, FLD_VAL(40, 24, 16) | FLD_VAL(40, 8, 0));
/* Horizontal front porch and horizontal active video size. 0x00500500*/
tc35876x_regw(i2c, HTIM2, FLD_VAL(80, 24, 16) | FLD_VAL(1280, 10, 0));
/* Vertical back porch and vertical sync pulse width. 0x000e000a */
tc35876x_regw(i2c, VTIM1, FLD_VAL(14, 23, 16) | FLD_VAL(10, 7, 0));
/* Vertical front porch and vertical display size. 0x000e0320 */
tc35876x_regw(i2c, VTIM2, FLD_VAL(14, 23, 16) | FLD_VAL(800, 10, 0));
/* Set above HTIM1, HTIM2, VTIM1, and VTIM2 at next VSYNC. */
tc35876x_regw(i2c, VFUEN, BIT(0));
/* Soft reset LCD controller. */
tc35876x_regw(i2c, SYSRST, BIT(2));
/* LVDS-TX input muxing */
tc35876x_regw(i2c, LVMX0003,
INPUT_MUX(INPUT_R5, INPUT_R4, INPUT_R3, INPUT_R2));
tc35876x_regw(i2c, LVMX0407,
INPUT_MUX(INPUT_G2, INPUT_R7, INPUT_R1, INPUT_R6));
tc35876x_regw(i2c, LVMX0811,
INPUT_MUX(INPUT_G1, INPUT_G0, INPUT_G4, INPUT_G3));
tc35876x_regw(i2c, LVMX1215,
INPUT_MUX(INPUT_B2, INPUT_G7, INPUT_G6, INPUT_G5));
tc35876x_regw(i2c, LVMX1619,
INPUT_MUX(INPUT_B4, INPUT_B3, INPUT_B1, INPUT_B0));
tc35876x_regw(i2c, LVMX2023,
INPUT_MUX(LOGIC_0, INPUT_B7, INPUT_B6, INPUT_B5));
tc35876x_regw(i2c, LVMX2427,
INPUT_MUX(INPUT_R0, INPUT_DE, INPUT_VSYNC, INPUT_HSYNC));
/* Enable LVDS transmitter. */
tc35876x_regw(i2c, LVCFG, BIT(0));
/* Clear notifications. Don't write reserved bits. Was write 0xffffffff
* to 0x0288, must be in error?! */
tc35876x_regw(i2c, DSI_INTCLR, FLD_MASK(31, 30) | FLD_MASK(22, 0));
}
#define GPIOPWMCTRL 0x38F
#define PWM0CLKDIV0 0x62 /* low byte */
#define PWM0CLKDIV1 0x61 /* high byte */
#define SYSTEMCLK 19200000UL /* 19.2 MHz */
#define PWM_FREQUENCY 9600 /* Hz */
/* f = baseclk / (clkdiv + 1) => clkdiv = (baseclk - f) / f */
static inline u16 calc_clkdiv(unsigned long baseclk, unsigned int f)
{
return (baseclk - f) / f;
}
static void tc35876x_brightness_init(struct drm_device *dev)
{
int ret;
u8 pwmctrl;
u16 clkdiv;
/* Make sure the PWM reference is the 19.2 MHz system clock. Read first
* instead of setting directly to catch potential conflicts between PWM
* users. */
ret = intel_scu_ipc_ioread8(GPIOPWMCTRL, &pwmctrl);
if (ret || pwmctrl != 0x01) {
if (ret)
dev_err(&dev->pdev->dev, "GPIOPWMCTRL read failed\n");
else
dev_warn(&dev->pdev->dev, "GPIOPWMCTRL was not set to system clock (pwmctrl = 0x%02x)\n", pwmctrl);
ret = intel_scu_ipc_iowrite8(GPIOPWMCTRL, 0x01);
if (ret)
dev_err(&dev->pdev->dev, "GPIOPWMCTRL set failed\n");
}
clkdiv = calc_clkdiv(SYSTEMCLK, PWM_FREQUENCY);
ret = intel_scu_ipc_iowrite8(PWM0CLKDIV1, (clkdiv >> 8) & 0xff);
if (!ret)
ret = intel_scu_ipc_iowrite8(PWM0CLKDIV0, clkdiv & 0xff);
if (ret)
dev_err(&dev->pdev->dev, "PWM0CLKDIV set failed\n");
else
dev_dbg(&dev->pdev->dev, "PWM0CLKDIV set to 0x%04x (%d Hz)\n",
clkdiv, PWM_FREQUENCY);
}
#define PWM0DUTYCYCLE 0x67
void tc35876x_brightness_control(struct drm_device *dev, int level)
{
int ret;
u8 duty_val;
u8 panel_duty_val;
level = clamp(level, 0, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
/* PWM duty cycle 0x00...0x63 corresponds to 0...99% */
duty_val = level * 0x63 / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL;
/* I won't pretend to understand this formula. The panel spec is quite
* bad engrish.
*/
panel_duty_val = (2 * level - 100) * 0xA9 /
MDFLD_DSI_BRIGHTNESS_MAX_LEVEL + 0x56;
ret = intel_scu_ipc_iowrite8(PWM0DUTYCYCLE, duty_val);
if (ret)
dev_err(&tc35876x_client->dev, "%s: ipc write fail\n",
__func__);
if (cmi_lcd_i2c_client) {
ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
PANEL_PWM_MAX, panel_duty_val);
if (ret < 0)
dev_err(&cmi_lcd_i2c_client->dev, "%s: i2c write failed\n",
__func__);
}
}
void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev)
{
struct tc35876x_platform_data *pdata;
if (WARN(!tc35876x_client, "%s called before probe", __func__))
return;
dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
pdata = dev_get_platdata(&tc35876x_client->dev);
if (pdata->gpio_panel_bl_en != -1)
gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 0);
if (pdata->gpio_panel_vadd != -1)
gpio_set_value_cansleep(pdata->gpio_panel_vadd, 0);
}
void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev)
{
struct tc35876x_platform_data *pdata;
struct drm_psb_private *dev_priv = dev->dev_private;
if (WARN(!tc35876x_client, "%s called before probe", __func__))
return;
dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
pdata = dev_get_platdata(&tc35876x_client->dev);
if (pdata->gpio_panel_vadd != -1) {
gpio_set_value_cansleep(pdata->gpio_panel_vadd, 1);
msleep(260);
}
if (cmi_lcd_i2c_client) {
int ret;
dev_dbg(&cmi_lcd_i2c_client->dev, "setting TCON\n");
/* Bit 4 is average_saving. Setting it to 1, the brightness is
* referenced to the average of the frame content. 0 means
* reference to the maximum of frame contents. Bits 3:0 are
* allow_distort. When set to a nonzero value, all color values
* between 255-allow_distort*2 and 255 are mapped to the
* 255-allow_distort*2 value.
*/
ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
PANEL_ALLOW_DISTORT, 0x10);
if (ret < 0)
dev_err(&cmi_lcd_i2c_client->dev,
"i2c write failed (%d)\n", ret);
ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
PANEL_BYPASS_PWMI, 0);
if (ret < 0)
dev_err(&cmi_lcd_i2c_client->dev,
"i2c write failed (%d)\n", ret);
/* Set minimum brightness value - this is tunable */
ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
PANEL_PWM_MIN, 0x35);
if (ret < 0)
dev_err(&cmi_lcd_i2c_client->dev,
"i2c write failed (%d)\n", ret);
}
if (pdata->gpio_panel_bl_en != -1)
gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 1);
tc35876x_brightness_control(dev, dev_priv->brightness_adjusted);
}
static struct drm_display_mode *tc35876x_get_config_mode(struct drm_device *dev)
{
struct drm_display_mode *mode;
dev_dbg(&dev->pdev->dev, "%s\n", __func__);
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode)
return NULL;
/* FIXME: do this properly. */
mode->hdisplay = 1280;
mode->vdisplay = 800;
mode->hsync_start = 1360;
mode->hsync_end = 1400;
mode->htotal = 1440;
mode->vsync_start = 814;
mode->vsync_end = 824;
mode->vtotal = 838;
mode->clock = 33324 << 1;
dev_info(&dev->pdev->dev, "hdisplay(w) = %d\n", mode->hdisplay);
dev_info(&dev->pdev->dev, "vdisplay(h) = %d\n", mode->vdisplay);
dev_info(&dev->pdev->dev, "HSS = %d\n", mode->hsync_start);
dev_info(&dev->pdev->dev, "HSE = %d\n", mode->hsync_end);
dev_info(&dev->pdev->dev, "htotal = %d\n", mode->htotal);
dev_info(&dev->pdev->dev, "VSS = %d\n", mode->vsync_start);
dev_info(&dev->pdev->dev, "VSE = %d\n", mode->vsync_end);
dev_info(&dev->pdev->dev, "vtotal = %d\n", mode->vtotal);
dev_info(&dev->pdev->dev, "clock = %d\n", mode->clock);
drm_mode_set_name(mode);
drm_mode_set_crtcinfo(mode, 0);
mode->type |= DRM_MODE_TYPE_PREFERRED;
return mode;
}
/* DV1 Active area 216.96 x 135.6 mm */
#define DV1_PANEL_WIDTH 217
#define DV1_PANEL_HEIGHT 136
static int tc35876x_get_panel_info(struct drm_device *dev, int pipe,
struct panel_info *pi)
{
if (!dev || !pi)
return -EINVAL;
pi->width_mm = DV1_PANEL_WIDTH;
pi->height_mm = DV1_PANEL_HEIGHT;
return 0;
}
static int tc35876x_bridge_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tc35876x_platform_data *pdata;
dev_info(&client->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "%s: i2c_check_functionality() failed\n",
__func__);
return -ENODEV;
}
pdata = dev_get_platdata(&client->dev);
if (!pdata) {
dev_err(&client->dev, "%s: no platform data\n", __func__);
return -ENODEV;
}
if (pdata->gpio_bridge_reset != -1) {
gpio_request(pdata->gpio_bridge_reset, "tc35876x bridge reset");
gpio_direction_output(pdata->gpio_bridge_reset, 0);
}
if (pdata->gpio_panel_bl_en != -1) {
gpio_request(pdata->gpio_panel_bl_en, "tc35876x panel bl en");
gpio_direction_output(pdata->gpio_panel_bl_en, 0);
}
if (pdata->gpio_panel_vadd != -1) {
gpio_request(pdata->gpio_panel_vadd, "tc35876x panel vadd");
gpio_direction_output(pdata->gpio_panel_vadd, 0);
}
tc35876x_client = client;
return 0;
}
static int tc35876x_bridge_remove(struct i2c_client *client)
{
struct tc35876x_platform_data *pdata = dev_get_platdata(&client->dev);
dev_dbg(&client->dev, "%s\n", __func__);
if (pdata->gpio_bridge_reset != -1)
gpio_free(pdata->gpio_bridge_reset);
if (pdata->gpio_panel_bl_en != -1)
gpio_free(pdata->gpio_panel_bl_en);
if (pdata->gpio_panel_vadd != -1)
gpio_free(pdata->gpio_panel_vadd);
tc35876x_client = NULL;
return 0;
}
static const struct i2c_device_id tc35876x_bridge_id[] = {
{ "i2c_disp_brig", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc35876x_bridge_id);
static struct i2c_driver tc35876x_bridge_i2c_driver = {
.driver = {
.name = "i2c_disp_brig",
},
.id_table = tc35876x_bridge_id,
.probe = tc35876x_bridge_probe,
.remove = __devexit_p(tc35876x_bridge_remove),
};
/* LCD panel I2C */
static int cmi_lcd_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
dev_info(&client->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "%s: i2c_check_functionality() failed\n",
__func__);
return -ENODEV;
}
cmi_lcd_i2c_client = client;
return 0;
}
static int cmi_lcd_i2c_remove(struct i2c_client *client)
{
dev_dbg(&client->dev, "%s\n", __func__);
cmi_lcd_i2c_client = NULL;
return 0;
}
static const struct i2c_device_id cmi_lcd_i2c_id[] = {
{ "cmi-lcd", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cmi_lcd_i2c_id);
static struct i2c_driver cmi_lcd_i2c_driver = {
.driver = {
.name = "cmi-lcd",
},
.id_table = cmi_lcd_i2c_id,
.probe = cmi_lcd_i2c_probe,
.remove = __devexit_p(cmi_lcd_i2c_remove),
};
/* HACK to create I2C device while it's not created by platform code */
#define CMI_LCD_I2C_ADAPTER 2
#define CMI_LCD_I2C_ADDR 0x60
static int cmi_lcd_hack_create_device(void)
{
struct i2c_adapter *adapter;
struct i2c_client *client;
struct i2c_board_info info = {
.type = "cmi-lcd",
.addr = CMI_LCD_I2C_ADDR,
};
pr_debug("%s\n", __func__);
adapter = i2c_get_adapter(CMI_LCD_I2C_ADAPTER);
if (!adapter) {
pr_err("%s: i2c_get_adapter(%d) failed\n", __func__,
CMI_LCD_I2C_ADAPTER);
return -EINVAL;
}
client = i2c_new_device(adapter, &info);
if (!client) {
pr_err("%s: i2c_new_device() failed\n", __func__);
i2c_put_adapter(adapter);
return -EINVAL;
}
return 0;
}
static const struct drm_encoder_helper_funcs tc35876x_encoder_helper_funcs = {
.dpms = mdfld_dsi_dpi_dpms,
.mode_fixup = mdfld_dsi_dpi_mode_fixup,
.prepare = mdfld_dsi_dpi_prepare,
.mode_set = mdfld_dsi_dpi_mode_set,
.commit = mdfld_dsi_dpi_commit,
};
static const struct drm_encoder_funcs tc35876x_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
const struct panel_funcs mdfld_tc35876x_funcs = {
.encoder_funcs = &tc35876x_encoder_funcs,
.encoder_helper_funcs = &tc35876x_encoder_helper_funcs,
.get_config_mode = tc35876x_get_config_mode,
.get_panel_info = tc35876x_get_panel_info,
};
void tc35876x_init(struct drm_device *dev)
{
int r;
dev_dbg(&dev->pdev->dev, "%s\n", __func__);
cmi_lcd_hack_create_device();
r = i2c_add_driver(&cmi_lcd_i2c_driver);
if (r < 0)
dev_err(&dev->pdev->dev,
"%s: i2c_add_driver() for %s failed (%d)\n",
__func__, cmi_lcd_i2c_driver.driver.name, r);
r = i2c_add_driver(&tc35876x_bridge_i2c_driver);
if (r < 0)
dev_err(&dev->pdev->dev,
"%s: i2c_add_driver() for %s failed (%d)\n",
__func__, tc35876x_bridge_i2c_driver.driver.name, r);
tc35876x_brightness_init(dev);
}
void tc35876x_exit(void)
{
pr_debug("%s\n", __func__);
i2c_del_driver(&tc35876x_bridge_i2c_driver);
if (cmi_lcd_i2c_client)
i2c_del_driver(&cmi_lcd_i2c_driver);
}

View File

@ -0,0 +1,38 @@
/*
* Copyright © 2011 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.
*
*/
#ifndef __MDFLD_DSI_LVDS_BRIDGE_H__
#define __MDFLD_DSI_LVDS_BRIDGE_H__
void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state);
void tc35876x_configure_lvds_bridge(struct drm_device *dev);
void tc35876x_brightness_control(struct drm_device *dev, int level);
void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev);
void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev);
void tc35876x_init(struct drm_device *dev);
void tc35876x_exit(void);
extern const struct panel_funcs mdfld_tc35876x_funcs;
#endif /*__MDFLD_DSI_LVDS_BRIDGE_H__*/

View File

@ -0,0 +1,11 @@
#ifndef _TC35876X_H
#define _TC35876X_H
struct tc35876x_platform_data {
int gpio_bridge_reset;
int gpio_panel_bl_en;
int gpio_panel_vadd;
};
#endif /* _TC35876X_H */