Commit ef23d500 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-misc-next-fixes-2021-02-11' of...

Merge tag 'drm-misc-next-fixes-2021-02-11' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next-fixes cherry picked from drm-misc-next for v5.12:
- Assorted small fixes.
- Disable and remove gma3600 support.
- Fix CEC for vc4/hdmi.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/dac2ae30-c5d9-4222-39e2-f64067310491@linux.intel.com
parents ac35d19f e2183fb1
......@@ -53,6 +53,24 @@ properties:
- const: audio
- const: cec
interrupts:
items:
- description: CEC TX interrupt
- description: CEC RX interrupt
- description: CEC stuck at low interrupt
- description: Wake-up interrupt
- description: Hotplug connected interrupt
- description: Hotplug removed interrupt
interrupt-names:
items:
- const: cec-tx
- const: cec-rx
- const: cec-low
- const: wakeup
- const: hpd-connected
- const: hpd-removed
ddc:
allOf:
- $ref: /schemas/types.yaml#/definitions/phandle
......@@ -90,7 +108,7 @@ required:
- resets
- ddc
additionalProperties: false
unevaluatedProperties: false
examples:
- |
......
......@@ -23,6 +23,9 @@ Advanced: Tricky tasks that need fairly good understanding of the DRM subsystem
and graphics topics. Generally need the relevant hardware for development and
testing.
Expert: Only attempt these if you've successfully completed some tricky
refactorings already and are an expert in the specific area
Subsystem-wide refactorings
===========================
......@@ -168,6 +171,22 @@ Contact: Daniel Vetter, respective driver maintainers
Level: Advanced
Move Buffer Object Locking to dma_resv_lock()
---------------------------------------------
Many drivers have their own per-object locking scheme, usually using
mutex_lock(). This causes all kinds of trouble for buffer sharing, since
depending which driver is the exporter and importer, the locking hierarchy is
reversed.
To solve this we need one standard per-object locking mechanism, which is
dma_resv_lock(). This lock needs to be called as the outermost lock, with all
other driver specific per-object locks removed. The problem is tha rolling out
the actual change to the locking contract is a flag day, due to struct dma_buf
buffer sharing.
Level: Expert
Convert logging to drm_* functions with drm_device paramater
------------------------------------------------------------
......
......@@ -471,8 +471,11 @@ static int thread_signal_callback(void *arg)
dma_fence_signal(f1);
smp_store_mb(cb.seen, false);
if (!f2 || dma_fence_add_callback(f2, &cb.cb, simple_callback))
miss++, cb.seen = true;
if (!f2 ||
dma_fence_add_callback(f2, &cb.cb, simple_callback)) {
miss++;
cb.seen = true;
}
if (!t->before)
dma_fence_signal(f1);
......
......@@ -7,6 +7,7 @@
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
*/
#include "drm/drm_modeset_lock.h"
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
......@@ -1181,9 +1182,11 @@ static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dp
struct drm_device *dev = client->dev;
struct drm_connector *connector;
struct drm_mode_set *modeset;
struct drm_modeset_acquire_ctx ctx;
int j;
int ret;
drm_modeset_lock_all(dev);
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
drm_client_for_each_modeset(modeset, client) {
if (!modeset->crtc->enabled)
continue;
......@@ -1195,7 +1198,7 @@ static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dp
dev->mode_config.dpms_property, dpms_mode);
}
}
drm_modeset_unlock_all(dev);
DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
}
/**
......
......@@ -2302,7 +2302,8 @@ drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
}
if (port->pdt != DP_PEER_DEVICE_NONE &&
drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
drm_dp_mst_is_end_device(port->pdt, port->mcs) &&
port->port_num >= DP_MST_LOGICAL_PORT_0) {
port->cached_edid = drm_get_edid(port->connector,
&port->aux.ddc);
drm_connector_set_tile_property(port->connector);
......
# SPDX-License-Identifier: GPL-2.0-only
config DRM_GMA500
tristate "Intel GMA5/600 KMS Framebuffer"
tristate "Intel GMA500/600/3600/3650 KMS Framebuffer"
depends on DRM && PCI && X86 && MMU
select DRM_KMS_HELPER
select DRM_TTM
# GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915
select ACPI_VIDEO if ACPI
select BACKLIGHT_CLASS_DEVICE if ACPI
......@@ -19,17 +18,3 @@ config DRM_GMA600
help
Say yes to include support for GMA600 (Intel Moorestown/Oaktrail)
platforms with LVDS ports. MIPI is not currently supported.
config DRM_GMA3600
bool "Intel GMA3600/3650 support (Experimental)"
depends on DRM_GMA500
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.
......@@ -6,36 +6,35 @@
gma500_gfx-y += \
accel_2d.o \
backlight.o \
blitter.o \
cdv_device.o \
cdv_intel_crt.o \
cdv_intel_display.o \
cdv_intel_dp.o \
cdv_intel_hdmi.o \
cdv_intel_lvds.o \
framebuffer.o \
gem.o \
gma_device.o \
gma_display.o \
gtt.o \
intel_bios.o \
intel_i2c.o \
intel_gmbus.o \
intel_i2c.o \
mid_bios.o \
mmu.o \
blitter.o \
power.o \
psb_device.o \
psb_drv.o \
gma_display.o \
gma_device.o \
psb_intel_display.o \
psb_intel_lvds.o \
psb_intel_modes.o \
psb_intel_sdvo.o \
psb_lid.o \
psb_irq.o \
psb_device.o \
mid_bios.o
psb_irq.o
gma500_gfx-$(CONFIG_ACPI) += opregion.o \
gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \
cdv_intel_crt.o \
cdv_intel_display.o \
cdv_intel_hdmi.o \
cdv_intel_lvds.o \
cdv_intel_dp.o
gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \
oaktrail_crtc.o \
oaktrail_lvds.o \
......@@ -43,14 +42,4 @@ 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
......@@ -22,9 +22,6 @@
*
* Authors:
* jim liu <jim.liu@intel.com>
*
* FIXME:
* We should probably make this generic and share it with Medfield
*/
#include <linux/pm_runtime.h>
......@@ -56,7 +53,6 @@ struct mid_intel_hdmi_priv {
bool has_hdmi_audio;
/* Should set this when detect hotplug */
bool hdmi_device_connected;
struct mdfld_hdmi_i2c *i2c_bus;
struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */
struct drm_device *dev;
};
......
// SPDX-License-Identifier: GPL-2.0-only
/**************************************************************************
* Copyright (c) 2011, Intel Corporation.
* All Rights Reserved.
*
**************************************************************************/
#include <linux/delay.h>
#include <linux/gpio/machine.h>
#include <asm/intel_scu_ipc.h>
#include "mdfld_dsi_output.h"
#include "mdfld_output.h"
#include "mid_bios.h"
#include "psb_drv.h"
#include "tc35876x-dsi-lvds.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;
}
static 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;
}
static 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 pipenum)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct medfield_state *regs = &dev_priv->regs.mdfld;
struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum];
const struct psb_offset *map = &dev_priv->regmap[pipenum];
int i;
u32 *mipi_val;
/* register */
u32 mipi_reg = MIPI;
switch (pipenum) {
case 0:
mipi_val = &regs->saveMIPI;
break;
case 1:
mipi_val = &regs->saveMIPI;
break;
case 2:
/* register */
mipi_reg = MIPI_C;
/* pointer to values */
mipi_val = &regs->saveMIPI_C;
break;
default:
DRM_ERROR("%s, invalid pipe number.\n", __func__);
return -EINVAL;
}
/* Pipe & plane A info */
pipe->dpll = PSB_RVDC32(map->dpll);
pipe->fp0 = PSB_RVDC32(map->fp0);
pipe->conf = PSB_RVDC32(map->conf);
pipe->htotal = PSB_RVDC32(map->htotal);
pipe->hblank = PSB_RVDC32(map->hblank);
pipe->hsync = PSB_RVDC32(map->hsync);
pipe->vtotal = PSB_RVDC32(map->vtotal);
pipe->vblank = PSB_RVDC32(map->vblank);
pipe->vsync = PSB_RVDC32(map->vsync);
pipe->src = PSB_RVDC32(map->src);
pipe->stride = PSB_RVDC32(map->stride);
pipe->linoff = PSB_RVDC32(map->linoff);
pipe->tileoff = PSB_RVDC32(map->tileoff);
pipe->size = PSB_RVDC32(map->size);
pipe->pos = PSB_RVDC32(map->pos);
pipe->surf = PSB_RVDC32(map->surf);
pipe->cntr = PSB_RVDC32(map->cntr);
pipe->status = PSB_RVDC32(map->status);
/*save palette (gamma) */
for (i = 0; i < 256; i++)
pipe->palette[i] = PSB_RVDC32(map->palette + (i << 2));
if (pipenum == 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 pipenum)
{
/* 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;
struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum];
const struct psb_offset *map = &dev_priv->regmap[pipenum];
u32 i;
u32 dpll;
u32 timeout = 0;
/* register */
u32 mipi_reg = MIPI;
/* values */
u32 dpll_val = pipe->dpll;
u32 mipi_val = regs->saveMIPI;
switch (pipenum) {
case 0:
dpll_val &= ~DPLL_VCO_ENABLE;
dsi_config = dev_priv->dsi_configs[0];
break;
case 1:
dpll_val &= ~DPLL_VCO_ENABLE;
break;
case 2:
mipi_reg = MIPI_C;
mipi_val = regs->saveMIPI_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 (pipenum == 1) {
PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, map->dpll);
PSB_RVDC32(map->dpll);
PSB_WVDC32(pipe->fp0, map->fp0);
} else {
dpll = PSB_RVDC32(map->dpll);
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, map->dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
}
PSB_WVDC32(pipe->fp0, map->fp0);
PSB_WVDC32(dpll_val, map->dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
dpll_val |= DPLL_VCO_ENABLE;
PSB_WVDC32(dpll_val, map->dpll);
PSB_RVDC32(map->dpll);
/* wait for DSI PLL to lock */
while (timeout < 20000 &&
!(PSB_RVDC32(map->conf) & PIPECONF_DSIPLL_LOCK)) {
udelay(150);
timeout++;
}
if (timeout == 20000) {
DRM_ERROR("%s, can't lock DSIPLL.\n",
__func__);
return -EINVAL;
}
}
}
/* Restore mode */
PSB_WVDC32(pipe->htotal, map->htotal);
PSB_WVDC32(pipe->hblank, map->hblank);
PSB_WVDC32(pipe->hsync, map->hsync);
PSB_WVDC32(pipe->vtotal, map->vtotal);
PSB_WVDC32(pipe->vblank, map->vblank);
PSB_WVDC32(pipe->vsync, map->vsync);
PSB_WVDC32(pipe->src, map->src);
PSB_WVDC32(pipe->status, map->status);
/*set up the plane*/
PSB_WVDC32(pipe->stride, map->stride);
PSB_WVDC32(pipe->linoff, map->linoff);
PSB_WVDC32(pipe->tileoff, map->tileoff);
PSB_WVDC32(pipe->size, map->size);
PSB_WVDC32(pipe->pos, map->pos);
PSB_WVDC32(pipe->surf, map->surf);
if (pipenum == 1) {
/* restore palette (gamma) */
/* udelay(50000); */
for (i = 0; i < 256; i++)
PSB_WVDC32(pipe->palette[i], map->palette + (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(pipe->cntr & ~DISPLAY_PLANE_ENABLE, map->cntr);
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, pipenum);
if (in_atomic() || in_interrupt())
mdelay(20);
else
msleep(20);
/*enable the plane*/
PSB_WVDC32(pipe->cntr, map->cntr);
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(pipe->conf, map->conf);
/* restore palette (gamma) */
/* udelay(50000); */
for (i = 0; i < 256; i++)
PSB_WVDC32(pipe->palette[i], map->palette + (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;
}
/* Medfield */
static const struct psb_offset mdfld_regmap[3] = {
{
.fp0 = MRST_FPA0,
.fp1 = MRST_FPA1,
.cntr = DSPACNTR,
.conf = PIPEACONF,
.src = PIPEASRC,
.dpll = MRST_DPLL_A,
.htotal = HTOTAL_A,
.hblank = HBLANK_A,
.hsync = HSYNC_A,
.vtotal = VTOTAL_A,
.vblank = VBLANK_A,
.vsync = VSYNC_A,
.stride = DSPASTRIDE,
.size = DSPASIZE,
.pos = DSPAPOS,
.surf = DSPASURF,
.addr = MRST_DSPABASE,
.status = PIPEASTAT,
.linoff = DSPALINOFF,
.tileoff = DSPATILEOFF,
.palette = PALETTE_A,
},
{
.fp0 = MDFLD_DPLL_DIV0,
.cntr = DSPBCNTR,
.conf = PIPEBCONF,
.src = PIPEBSRC,
.dpll = MDFLD_DPLL_B,
.htotal = HTOTAL_B,
.hblank = HBLANK_B,
.hsync = HSYNC_B,
.vtotal = VTOTAL_B,
.vblank = VBLANK_B,
.vsync = VSYNC_B,
.stride = DSPBSTRIDE,
.size = DSPBSIZE,
.pos = DSPBPOS,
.surf = DSPBSURF,
.addr = MRST_DSPBBASE,
.status = PIPEBSTAT,
.linoff = DSPBLINOFF,
.tileoff = DSPBTILEOFF,
.palette = PALETTE_B,
},
{
.fp0 = MRST_FPA0, /* This is what the old code did ?? */
.cntr = DSPCCNTR,
.conf = PIPECCONF,
.src = PIPECSRC,
/* No DPLL_C */
.dpll = MRST_DPLL_A,
.htotal = HTOTAL_C,
.hblank = HBLANK_C,
.hsync = HSYNC_C,
.vtotal = VTOTAL_C,
.vblank = VBLANK_C,
.vsync = VSYNC_C,
.stride = DSPCSTRIDE,
.size = DSPBSIZE,
.pos = DSPCPOS,
.surf = DSPCSURF,
.addr = MDFLD_DSPCBASE,
.status = PIPECSTAT,
.linoff = DSPCLINOFF,
.tileoff = DSPCTILEOFF,
.palette = PALETTE_C,
},
};
/*
* The GPIO lines for resetting DSI pipe 0 and 2 are available in the
* PCI device 0000:00:0c.0 on the Medfield.
*/
static struct gpiod_lookup_table mdfld_dsi_pipe_gpio_table = {
.table = {
GPIO_LOOKUP("0000:00:0c.0", 128, "dsi-pipe0-reset",
GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("0000:00:0c.0", 34, "dsi-pipe2-reset",
GPIO_ACTIVE_HIGH),
{ },
},
};
static int mdfld_chip_setup(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct pci_dev *pdev = to_pci_dev(dev->dev);
if (pci_enable_msi(pdev))
dev_warn(dev->dev, "Enabling MSI failed!\n");
dev_priv->regmap = mdfld_regmap;
/* Associate the GPIO lines with the DRM device */
mdfld_dsi_pipe_gpio_table.dev_id = dev_name(dev->dev);
gpiod_add_lookup_table(&mdfld_dsi_pipe_gpio_table);
return mid_chip_setup(dev);
}
const struct psb_ops mdfld_chip_ops = {
.name = "mdfld",
.pipes = 3,
.crtcs = 3,
.lvds_mask = (1 << 1),
.hdmi_mask = (1 << 1),
.cursor_needs_phys = 0,
.sgx_offset = MRST_SGX_OFFSET,
.chip_setup = mdfld_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,
.save_crtc = gma_crtc_save,
.restore_crtc = gma_crtc_restore,
.power_down = mdfld_power_down,
.power_up = mdfld_power_up,
};
/*
* 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/delay.h>
#include <drm/drm_simple_kms_helper.h>
#include "mdfld_dsi_dpi.h"
#include "mdfld_dsi_pkg_sender.h"
#include "mdfld_output.h"
#include "psb_drv.h"
#include "tc35876x-dsi-lvds.h"
static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output,
int pipe);
static void mdfld_wait_for_HS_DATA_FIFO(struct drm_device *dev, u32 pipe)
{
u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
int timeout = 0;
udelay(500);
/* This will time out after approximately 2+ seconds */
while ((timeout < 20000) &&
(REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_DATA_FULL)) {
udelay(100);
timeout++;
}
if (timeout == 20000)
DRM_INFO("MIPI: HS Data FIFO was never cleared!\n");
}
static void mdfld_wait_for_HS_CTRL_FIFO(struct drm_device *dev, u32 pipe)
{
u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
int timeout = 0;
udelay(500);
/* This will time out after approximately 2+ seconds */
while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg)
& DSI_FIFO_GEN_HS_CTRL_FULL)) {
udelay(100);
timeout++;
}
if (timeout == 20000)
DRM_INFO("MIPI: HS CMD FIFO was never cleared!\n");
}
static void mdfld_wait_for_DPI_CTRL_FIFO(struct drm_device *dev, u32 pipe)
{
u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
int timeout = 0;
udelay(500);
/* This will time out after approximately 2+ seconds */
while ((timeout < 20000) && ((REG_READ(gen_fifo_stat_reg) &
DPI_FIFO_EMPTY) != DPI_FIFO_EMPTY)) {
udelay(100);
timeout++;
}
if (timeout == 20000)
DRM_ERROR("MIPI: DPI FIFO was never cleared\n");
}
static void mdfld_wait_for_SPL_PKG_SENT(struct drm_device *dev, u32 pipe)
{
u32 intr_stat_reg = MIPI_INTR_STAT_REG(pipe);
int timeout = 0;
udelay(500);
/* This will time out after approximately 2+ seconds */
while ((timeout < 20000) && (!(REG_READ(intr_stat_reg)
& DSI_INTR_STATE_SPL_PKG_SENT))) {
udelay(100);
timeout++;
}
if (timeout == 20000)
DRM_ERROR("MIPI: SPL_PKT_SENT_INTERRUPT was not sent successfully!\n");
}
/* For TC35876X */
static void dsi_set_device_ready_state(struct drm_device *dev, int state,
int pipe)
{
REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), !!state, 0, 0);
}
static void dsi_set_pipe_plane_enable_state(struct drm_device *dev,
int state, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
u32 pipeconf_reg = PIPEACONF;
u32 dspcntr_reg = DSPACNTR;
u32 dspcntr = dev_priv->dspcntr[pipe];
u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
if (pipe) {
pipeconf_reg = PIPECCONF;
dspcntr_reg = DSPCCNTR;
} else
mipi &= (~0x03);
if (state) {
/*Set up pipe */
REG_WRITE(pipeconf_reg, BIT(31));
if (REG_BIT_WAIT(pipeconf_reg, 1, 30))
dev_err(dev->dev, "%s: Pipe enable timeout\n",
__func__);
/*Set up display plane */
REG_WRITE(dspcntr_reg, dspcntr);
} else {
u32 dspbase_reg = pipe ? MDFLD_DSPCBASE : MRST_DSPABASE;
/* Put DSI lanes to ULPS to disable pipe */
REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 2, 2, 1);
REG_READ(MIPI_DEVICE_READY_REG(pipe)); /* posted write? */
/* LP Hold */
REG_FLD_MOD(MIPI_PORT_CONTROL(pipe), 0, 16, 16);
REG_READ(MIPI_PORT_CONTROL(pipe)); /* posted write? */
/* Disable display plane */
REG_FLD_MOD(dspcntr_reg, 0, 31, 31);
/* Flush the plane changes ??? posted write? */
REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
REG_READ(dspbase_reg);
/* Disable PIPE */
REG_FLD_MOD(pipeconf_reg, 0, 31, 31);
if (REG_BIT_WAIT(pipeconf_reg, 0, 30))
dev_err(dev->dev, "%s: Pipe disable timeout\n",
__func__);
if (REG_BIT_WAIT(MIPI_GEN_FIFO_STAT_REG(pipe), 1, 28))
dev_err(dev->dev, "%s: FIFO not empty\n",
__func__);
}
}
static void mdfld_dsi_configure_down(struct mdfld_dsi_encoder *dsi_encoder,
int pipe)
{
struct mdfld_dsi_dpi_output *dpi_output =
MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
struct drm_device *dev = dsi_config->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
if (!dev_priv->dpi_panel_on[pipe]) {
dev_err(dev->dev, "DPI panel is already off\n");
return;
}
tc35876x_toshiba_bridge_panel_off(dev);
tc35876x_set_bridge_reset_state(dev, 1);
dsi_set_pipe_plane_enable_state(dev, 0, pipe);
mdfld_dsi_dpi_shut_down(dpi_output, pipe);
dsi_set_device_ready_state(dev, 0, pipe);
}
static void mdfld_dsi_configure_up(struct mdfld_dsi_encoder *dsi_encoder,
int pipe)
{
struct mdfld_dsi_dpi_output *dpi_output =
MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
struct drm_device *dev = dsi_config->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
if (dev_priv->dpi_panel_on[pipe]) {
dev_err(dev->dev, "DPI panel is already on\n");
return;
}
/* For resume path sequence */
mdfld_dsi_dpi_shut_down(dpi_output, pipe);
dsi_set_device_ready_state(dev, 0, pipe);
dsi_set_device_ready_state(dev, 1, pipe);
tc35876x_set_bridge_reset_state(dev, 0);
tc35876x_configure_lvds_bridge(dev);
mdfld_dsi_dpi_turn_on(dpi_output, pipe); /* Send turn on command */
dsi_set_pipe_plane_enable_state(dev, 1, pipe);
}
/* End for TC35876X */
/* ************************************************************************* *\
* FUNCTION: mdfld_dsi_tpo_ic_init
*
* 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!
\* ************************************************************************* */
static void mdfld_dsi_tpo_ic_init(struct mdfld_dsi_config *dsi_config, u32 pipe)
{
struct drm_device *dev = dsi_config->dev;
u32 dcsChannelNumber = dsi_config->channel_num;
u32 gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe);
u32 gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe);
u32 gen_ctrl_val = GEN_LONG_WRITE;
DRM_INFO("Enter mrst init TPO MIPI display.\n");
gen_ctrl_val |= dcsChannelNumber << DCS_CHANNEL_NUMBER_POS;
/* Flip page order */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00008036);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS));
/* 0xF0 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x005a5af0);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
/* Write protection key */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x005a5af1);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
/* 0xFC */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x005a5afc);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
/* 0xB7 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x770000b7);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00000044);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x05 << WORD_COUNTS_POS));
/* 0xB6 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x000a0ab6);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
/* 0xF2 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x081010f2);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x4a070708);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x000000c5);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS));
/* 0xF8 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x024003f8);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x01030a04);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x0e020220);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00000004);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x0d << WORD_COUNTS_POS));
/* 0xE2 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x398fc3e2);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x0000916f);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x06 << WORD_COUNTS_POS));
/* 0xB0 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x000000b0);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS));
/* 0xF4 */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x240242f4);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x78ee2002);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x2a071050);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x507fee10);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x10300710);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x14 << WORD_COUNTS_POS));
/* 0xBA */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x19fe07ba);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x101c0a31);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00000010);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS));
/* 0xBB */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x28ff07bb);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x24280a31);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00000034);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS));
/* 0xFB */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x535d05fb);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x1b1a2130);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x221e180e);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x131d2120);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x535d0508);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x1c1a2131);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x231f160d);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x111b2220);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x535c2008);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x1f1d2433);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x2c251a10);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x2c34372d);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00000023);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS));
/* 0xFA */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x525c0bfa);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x1c1c232f);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x2623190e);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x18212625);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x545d0d0e);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x1e1d2333);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x26231a10);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x1a222725);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x545d280f);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x21202635);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x31292013);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x31393d33);
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x00000029);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS));
/* Set DM */
mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
REG_WRITE(gen_data_reg, 0x000100f7);
mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
}
static u16 mdfld_dsi_dpi_to_byte_clock_count(int pixel_clock_count,
int num_lane, int bpp)
{
return (u16)((pixel_clock_count * bpp) / (num_lane * 8));
}
/*
* Calculate the dpi time basing on a given drm mode @mode
* return 0 on success.
* FIXME: I was using proposed mode value for calculation, may need to
* use crtc mode values later
*/
int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode,
struct mdfld_dsi_dpi_timing *dpi_timing,
int num_lane, int bpp)
{
int pclk_hsync, pclk_hfp, pclk_hbp, pclk_hactive;
int pclk_vsync, pclk_vfp, pclk_vbp;
pclk_hactive = mode->hdisplay;
pclk_hfp = mode->hsync_start - mode->hdisplay;
pclk_hsync = mode->hsync_end - mode->hsync_start;
pclk_hbp = mode->htotal - mode->hsync_end;
pclk_vfp = mode->vsync_start - mode->vdisplay;
pclk_vsync = mode->vsync_end - mode->vsync_start;
pclk_vbp = mode->vtotal - mode->vsync_end;
/*
* byte clock counts were calculated by following formula
* bclock_count = pclk_count * bpp / num_lane / 8
*/
dpi_timing->hsync_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_hsync, num_lane, bpp);
dpi_timing->hbp_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_hbp, num_lane, bpp);
dpi_timing->hfp_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_hfp, num_lane, bpp);
dpi_timing->hactive_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_hactive, num_lane, bpp);
dpi_timing->vsync_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_vsync, num_lane, bpp);
dpi_timing->vbp_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_vbp, num_lane, bpp);
dpi_timing->vfp_count = mdfld_dsi_dpi_to_byte_clock_count(
pclk_vfp, num_lane, bpp);
return 0;
}
void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config,
int pipe)
{
struct drm_device *dev = dsi_config->dev;
int lane_count = dsi_config->lane_count;
struct mdfld_dsi_dpi_timing dpi_timing;
struct drm_display_mode *mode = dsi_config->mode;
u32 val;
/*un-ready device*/
REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 0, 0, 0);
/*init dsi adapter before kicking off*/
REG_WRITE(MIPI_CTRL_REG(pipe), 0x00000018);
/*enable all interrupts*/
REG_WRITE(MIPI_INTR_EN_REG(pipe), 0xffffffff);
/*set up func_prg*/
val = lane_count;
val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET;
switch (dsi_config->bpp) {
case 16:
val |= DSI_DPI_COLOR_FORMAT_RGB565;
break;
case 18:
val |= DSI_DPI_COLOR_FORMAT_RGB666;
break;
case 24:
val |= DSI_DPI_COLOR_FORMAT_RGB888;
break;
default:
DRM_ERROR("unsupported color format, bpp = %d\n",
dsi_config->bpp);
}
REG_WRITE(MIPI_DSI_FUNC_PRG_REG(pipe), val);
REG_WRITE(MIPI_HS_TX_TIMEOUT_REG(pipe),
(mode->vtotal * mode->htotal * dsi_config->bpp /
(8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK);
REG_WRITE(MIPI_LP_RX_TIMEOUT_REG(pipe),
0xffff & DSI_LP_RX_TIMEOUT_MASK);
/*max value: 20 clock cycles of txclkesc*/
REG_WRITE(MIPI_TURN_AROUND_TIMEOUT_REG(pipe),
0x14 & DSI_TURN_AROUND_TIMEOUT_MASK);
/*min 21 txclkesc, max: ffffh*/
REG_WRITE(MIPI_DEVICE_RESET_TIMER_REG(pipe),
0xffff & DSI_RESET_TIMER_MASK);
REG_WRITE(MIPI_DPI_RESOLUTION_REG(pipe),
mode->vdisplay << 16 | mode->hdisplay);
/*set DPI timing registers*/
mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing,
dsi_config->lane_count, dsi_config->bpp);
REG_WRITE(MIPI_HSYNC_COUNT_REG(pipe),
dpi_timing.hsync_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HBP_COUNT_REG(pipe),
dpi_timing.hbp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HFP_COUNT_REG(pipe),
dpi_timing.hfp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HACTIVE_COUNT_REG(pipe),
dpi_timing.hactive_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_VSYNC_COUNT_REG(pipe),
dpi_timing.vsync_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_VBP_COUNT_REG(pipe),
dpi_timing.vbp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_VFP_COUNT_REG(pipe),
dpi_timing.vfp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe), 0x46);
/*min: 7d0 max: 4e20*/
REG_WRITE(MIPI_INIT_COUNT_REG(pipe), 0x000007d0);
/*set up video mode*/
val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE;
REG_WRITE(MIPI_VIDEO_MODE_FORMAT_REG(pipe), val);
REG_WRITE(MIPI_EOT_DISABLE_REG(pipe), 0x00000000);
REG_WRITE(MIPI_LP_BYTECLK_REG(pipe), 0x00000004);
/*TODO: figure out how to setup these registers*/
if (mdfld_get_panel_type(dev, pipe) == TC35876X)
REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x2A0c6008);
else
REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x150c3408);
REG_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe), (0xa << 16) | 0x14);
if (mdfld_get_panel_type(dev, pipe) == TC35876X)
tc35876x_set_bridge_reset_state(dev, 0); /*Pull High Reset */
/*set device ready*/
REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 1, 0, 0);
}
void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, int pipe)
{
struct drm_device *dev = output->dev;
/* clear special packet sent bit */
if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT)
REG_WRITE(MIPI_INTR_STAT_REG(pipe),
DSI_INTR_STATE_SPL_PKG_SENT);
/*send turn on package*/
REG_WRITE(MIPI_DPI_CONTROL_REG(pipe), DSI_DPI_CTRL_HS_TURN_ON);
/*wait for SPL_PKG_SENT interrupt*/
mdfld_wait_for_SPL_PKG_SENT(dev, pipe);
if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT)
REG_WRITE(MIPI_INTR_STAT_REG(pipe),
DSI_INTR_STATE_SPL_PKG_SENT);
output->panel_on = 1;
/* FIXME the following is disabled to WA the X slow start issue
for TMD panel
if (pipe == 2)
dev_priv->dpi_panel_on2 = true;
else if (pipe == 0)
dev_priv->dpi_panel_on = true; */
}
static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output,
int pipe)
{
struct drm_device *dev = output->dev;
/*if output is on, or mode setting didn't happen, ignore this*/
if ((!output->panel_on) || output->first_boot) {
output->first_boot = 0;
return;
}
/* Wait for dpi fifo to empty */
mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe);
/* Clear the special packet interrupt bit if set */
if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT)
REG_WRITE(MIPI_INTR_STAT_REG(pipe),
DSI_INTR_STATE_SPL_PKG_SENT);
if (REG_READ(MIPI_DPI_CONTROL_REG(pipe)) == DSI_DPI_CTRL_HS_SHUTDOWN)
goto shutdown_out;
REG_WRITE(MIPI_DPI_CONTROL_REG(pipe), DSI_DPI_CTRL_HS_SHUTDOWN);
shutdown_out:
output->panel_on = 0;
output->first_boot = 0;
/* FIXME the following is disabled to WA the X slow start issue
for TMD panel
if (pipe == 2)
dev_priv->dpi_panel_on2 = false;
else if (pipe == 0)
dev_priv->dpi_panel_on = false; */
}
static void mdfld_dsi_dpi_set_power(struct drm_encoder *encoder, bool on)
{
struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder);
struct mdfld_dsi_dpi_output *dpi_output =
MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder);
struct drm_device *dev = dsi_config->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
/*start up display island if it was shutdown*/
if (!gma_power_begin(dev, true))
return;
if (on) {
if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
mdfld_dsi_dpi_turn_on(dpi_output, pipe);
else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
mdfld_dsi_configure_up(dsi_encoder, pipe);
else {
/*enable mipi port*/
REG_WRITE(MIPI_PORT_CONTROL(pipe),
REG_READ(MIPI_PORT_CONTROL(pipe)) | BIT(31));
REG_READ(MIPI_PORT_CONTROL(pipe));
mdfld_dsi_dpi_turn_on(dpi_output, pipe);
mdfld_dsi_tpo_ic_init(dsi_config, pipe);
}
dev_priv->dpi_panel_on[pipe] = true;
} else {
if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
mdfld_dsi_dpi_shut_down(dpi_output, pipe);
else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
mdfld_dsi_configure_down(dsi_encoder, pipe);
else {
mdfld_dsi_dpi_shut_down(dpi_output, pipe);
/*disable mipi port*/
REG_WRITE(MIPI_PORT_CONTROL(pipe),
REG_READ(MIPI_PORT_CONTROL(pipe)) & ~BIT(31));
REG_READ(MIPI_PORT_CONTROL(pipe));
}
dev_priv->dpi_panel_on[pipe] = false;
}
gma_power_end(dev);
}
void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode)
{
mdfld_dsi_dpi_set_power(encoder, mode == DRM_MODE_DPMS_ON);
}
bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
if (fixed_mode) {
adjusted_mode->hdisplay = fixed_mode->hdisplay;
adjusted_mode->hsync_start = fixed_mode->hsync_start;
adjusted_mode->hsync_end = fixed_mode->hsync_end;
adjusted_mode->htotal = fixed_mode->htotal;
adjusted_mode->vdisplay = fixed_mode->vdisplay;
adjusted_mode->vsync_start = fixed_mode->vsync_start;
adjusted_mode->vsync_end = fixed_mode->vsync_end;
adjusted_mode->vtotal = fixed_mode->vtotal;
adjusted_mode->clock = fixed_mode->clock;
drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
}
return true;
}
void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder)
{
mdfld_dsi_dpi_set_power(encoder, false);
}
void mdfld_dsi_dpi_commit(struct drm_encoder *encoder)
{
mdfld_dsi_dpi_set_power(encoder, true);
}
/* For TC35876X */
/* This functionality was implemented in FW in iCDK */
/* But removed in DV0 and later. So need to add here. */
static void mipi_set_properties(struct mdfld_dsi_config *dsi_config, int pipe)
{
struct drm_device *dev = dsi_config->dev;
REG_WRITE(MIPI_CTRL_REG(pipe), 0x00000018);
REG_WRITE(MIPI_INTR_EN_REG(pipe), 0xffffffff);
REG_WRITE(MIPI_HS_TX_TIMEOUT_REG(pipe), 0xffffff);
REG_WRITE(MIPI_LP_RX_TIMEOUT_REG(pipe), 0xffffff);
REG_WRITE(MIPI_TURN_AROUND_TIMEOUT_REG(pipe), 0x14);
REG_WRITE(MIPI_DEVICE_RESET_TIMER_REG(pipe), 0xff);
REG_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe), 0x25);
REG_WRITE(MIPI_INIT_COUNT_REG(pipe), 0xf0);
REG_WRITE(MIPI_EOT_DISABLE_REG(pipe), 0x00000000);
REG_WRITE(MIPI_LP_BYTECLK_REG(pipe), 0x00000004);
REG_WRITE(MIPI_DBI_BW_CTRL_REG(pipe), 0x00000820);
REG_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe), (0xa << 16) | 0x14);
}
static void mdfld_mipi_set_video_timing(struct mdfld_dsi_config *dsi_config,
int pipe)
{
struct drm_device *dev = dsi_config->dev;
struct mdfld_dsi_dpi_timing dpi_timing;
struct drm_display_mode *mode = dsi_config->mode;
mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing,
dsi_config->lane_count,
dsi_config->bpp);
REG_WRITE(MIPI_DPI_RESOLUTION_REG(pipe),
mode->vdisplay << 16 | mode->hdisplay);
REG_WRITE(MIPI_HSYNC_COUNT_REG(pipe),
dpi_timing.hsync_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HBP_COUNT_REG(pipe),
dpi_timing.hbp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HFP_COUNT_REG(pipe),
dpi_timing.hfp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_HACTIVE_COUNT_REG(pipe),
dpi_timing.hactive_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_VSYNC_COUNT_REG(pipe),
dpi_timing.vsync_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_VBP_COUNT_REG(pipe),
dpi_timing.vbp_count & DSI_DPI_TIMING_MASK);
REG_WRITE(MIPI_VFP_COUNT_REG(pipe),
dpi_timing.vfp_count & DSI_DPI_TIMING_MASK);
}
static void mdfld_mipi_config(struct mdfld_dsi_config *dsi_config, int pipe)
{
struct drm_device *dev = dsi_config->dev;
int lane_count = dsi_config->lane_count;
if (pipe) {
REG_WRITE(MIPI_PORT_CONTROL(0), 0x00000002);
REG_WRITE(MIPI_PORT_CONTROL(2), 0x80000000);
} else {
REG_WRITE(MIPI_PORT_CONTROL(0), 0x80010000);
REG_WRITE(MIPI_PORT_CONTROL(2), 0x00);
}
REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x150A600F);
REG_WRITE(MIPI_VIDEO_MODE_FORMAT_REG(pipe), 0x0000000F);
/* lane_count = 3 */
REG_WRITE(MIPI_DSI_FUNC_PRG_REG(pipe), 0x00000200 | lane_count);
mdfld_mipi_set_video_timing(dsi_config, pipe);
}
static void mdfld_set_pipe_timing(struct mdfld_dsi_config *dsi_config, int pipe)
{
struct drm_device *dev = dsi_config->dev;
struct drm_display_mode *mode = dsi_config->mode;
REG_WRITE(HTOTAL_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1));
REG_WRITE(HBLANK_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1));
REG_WRITE(HSYNC_A,
((mode->hsync_end - 1) << 16) | (mode->hsync_start - 1));
REG_WRITE(VTOTAL_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1));
REG_WRITE(VBLANK_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1));
REG_WRITE(VSYNC_A,
((mode->vsync_end - 1) << 16) | (mode->vsync_start - 1));
REG_WRITE(PIPEASRC,
((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
}
/* End for TC35876X */
void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder);
struct mdfld_dsi_dpi_output *dpi_output =
MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
struct drm_device *dev = dsi_config->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder);
u32 pipeconf_reg = PIPEACONF;
u32 dspcntr_reg = DSPACNTR;
u32 pipeconf, dspcntr;
u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
if (WARN_ON(pipe < 0))
return;
pipeconf = dev_priv->pipeconf[pipe];
dspcntr = dev_priv->dspcntr[pipe];
if (pipe) {
pipeconf_reg = PIPECCONF;
dspcntr_reg = DSPCCNTR;
} else {
if (mdfld_get_panel_type(dev, pipe) == TC35876X)
mipi &= (~0x03); /* Use all four lanes */
else
mipi |= 2;
}
/*start up display island if it was shutdown*/
if (!gma_power_begin(dev, true))
return;
if (mdfld_get_panel_type(dev, pipe) == TC35876X) {
/*
* The following logic is required to reset the bridge and
* configure. This also starts the DSI clock at 200MHz.
*/
tc35876x_set_bridge_reset_state(dev, 0); /*Pull High Reset */
tc35876x_toshiba_bridge_panel_on(dev);
udelay(100);
/* Now start the DSI clock */
REG_WRITE(MRST_DPLL_A, 0x00);
REG_WRITE(MRST_FPA0, 0xC1);
REG_WRITE(MRST_DPLL_A, 0x00800000);
udelay(500);
REG_WRITE(MRST_DPLL_A, 0x80800000);
if (REG_BIT_WAIT(pipeconf_reg, 1, 29))
dev_err(dev->dev, "%s: DSI PLL lock timeout\n",
__func__);
REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x2A0c6008);
mipi_set_properties(dsi_config, pipe);
mdfld_mipi_config(dsi_config, pipe);
mdfld_set_pipe_timing(dsi_config, pipe);
REG_WRITE(DSPABASE, 0x00);
REG_WRITE(DSPASIZE,
((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
REG_WRITE(DSPACNTR, 0x98000000);
REG_WRITE(DSPASURF, 0x00);
REG_WRITE(VGACNTRL, 0x80000000);
REG_WRITE(DEVICE_READY_REG, 0x00000001);
REG_WRITE(MIPI_PORT_CONTROL(pipe), 0x80810000);
} else {
/*set up mipi port FIXME: do at init time */
REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi);
}
REG_READ(MIPI_PORT_CONTROL(pipe));
if (mdfld_get_panel_type(dev, pipe) == TMD_VID) {
/* NOP */
} else if (mdfld_get_panel_type(dev, pipe) == TC35876X) {
/* set up DSI controller DPI interface */
mdfld_dsi_dpi_controller_init(dsi_config, pipe);
/* Configure MIPI Bridge and Panel */
tc35876x_configure_lvds_bridge(dev);
dev_priv->dpi_panel_on[pipe] = true;
} else {
/*turn on DPI interface*/
mdfld_dsi_dpi_turn_on(dpi_output, pipe);
}
/*set up pipe*/
REG_WRITE(pipeconf_reg, pipeconf);
REG_READ(pipeconf_reg);
/*set up display plane*/
REG_WRITE(dspcntr_reg, dspcntr);
REG_READ(dspcntr_reg);
msleep(20); /* FIXME: this should wait for vblank */
if (mdfld_get_panel_type(dev, pipe) == TMD_VID) {
/* NOP */
} else if (mdfld_get_panel_type(dev, pipe) == TC35876X) {
mdfld_dsi_dpi_turn_on(dpi_output, pipe);
} else {
/* init driver ic */
mdfld_dsi_tpo_ic_init(dsi_config, pipe);
/*init backlight*/
mdfld_dsi_brightness_init(dsi_config, pipe);
}
gma_power_end(dev);
}
/*
* Init DSI DPI encoder.
* Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector
* return pointer of newly allocated DPI encoder, NULL on error
*/
struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
struct mdfld_dsi_connector *dsi_connector,
const struct panel_funcs *p_funcs)
{
struct mdfld_dsi_dpi_output *dpi_output = NULL;
struct mdfld_dsi_config *dsi_config;
struct drm_connector *connector = NULL;
struct drm_encoder *encoder = NULL;
int pipe;
u32 data;
int ret;
pipe = dsi_connector->pipe;
if (mdfld_get_panel_type(dev, pipe) != TC35876X) {
dsi_config = mdfld_dsi_get_config(dsi_connector);
/* panel hard-reset */
if (p_funcs->reset) {
ret = p_funcs->reset(dev, pipe);
if (ret) {
DRM_ERROR("Panel %d hard-reset failed\n", pipe);
return NULL;
}
}
/* panel drvIC init */
if (p_funcs->drv_ic_init)
p_funcs->drv_ic_init(dsi_config, pipe);
/* panel power mode detect */
ret = mdfld_dsi_get_power_mode(dsi_config, &data, false);
if (ret) {
DRM_ERROR("Panel %d get power mode failed\n", pipe);
dsi_connector->status = connector_status_disconnected;
} else {
DRM_INFO("pipe %d power mode 0x%x\n", pipe, data);
dsi_connector->status = connector_status_connected;
}
}
dpi_output = kzalloc(sizeof(struct mdfld_dsi_dpi_output), GFP_KERNEL);
if (!dpi_output) {
DRM_ERROR("No memory\n");
return NULL;
}
dpi_output->panel_on = 0;
dpi_output->dev = dev;
if (mdfld_get_panel_type(dev, pipe) != TC35876X)
dpi_output->p_funcs = p_funcs;
dpi_output->first_boot = 1;
/*get fixed mode*/
dsi_config = mdfld_dsi_get_config(dsi_connector);
/*create drm encoder object*/
connector = &dsi_connector->base.base;
encoder = &dpi_output->base.base.base;
drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
drm_encoder_helper_add(encoder,
p_funcs->encoder_helper_funcs);
/*attach to given connector*/
drm_connector_attach_encoder(connector, encoder);
/*set possible crtcs and clones*/
if (dsi_connector->pipe) {
encoder->possible_crtcs = (1 << 2);
encoder->possible_clones = 0;
} else {
encoder->possible_crtcs = (1 << 0);
encoder->possible_clones = 0;
}
dsi_connector->base.encoder = &dpi_output->base.base;
return &dpi_output->base;
}
/*
* 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,
const 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__*/
/*
* 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/delay.h>
#include <linux/moduleparam.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include <asm/intel_scu_ipc.h>
#include "mdfld_dsi_dpi.h"
#include "mdfld_dsi_output.h"
#include "mdfld_dsi_pkg_sender.h"
#include "mdfld_output.h"
#include "tc35876x-dsi-lvds.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;
struct drm_psb_private *dev_priv;
u32 gen_ctrl_val;
if (!sender) {
DRM_ERROR("No sender found\n");
return;
}
dev = sender->dev;
dev_priv = dev->dev_private;
/* 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);
}
/*
* 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;
if (!strcmp(property->name, "scaling mode") && encoder) {
struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc);
bool centerechange;
uint64_t val;
if (!gma_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_object_property_get_value(&connector->base, property, &val))
goto set_prop_error;
if (val == value)
goto set_prop_done;
if (drm_object_property_set_value(&connector->base,
property, value))
goto set_prop_error;
centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
(value == DRM_MODE_SCALE_NO_SCALE);
if (gma_crtc->saved_mode.hdisplay != 0 &&
gma_crtc->saved_mode.vdisplay != 0) {
if (centerechange) {
if (!drm_crtc_helper_set_mode(encoder->crtc,
&gma_crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
encoder->crtc->primary->fb))
goto set_prop_error;
} else {
const struct drm_encoder_helper_funcs *funcs =
encoder->helper_private;
funcs->mode_set(encoder,
&gma_crtc->saved_mode,
&gma_crtc->saved_adjusted_mode);
}
}
} else if (!strcmp(property->name, "backlight") && encoder) {
if (drm_object_property_set_value(&connector->base, property,
value))
goto set_prop_error;
else
gma_backlight_set(encoder->dev, value);
}
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_connector_unregister(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;
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 enum drm_mode_status 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(panel 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 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,
.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(struct drm_device *ddev, int pipe)
{
struct device *dev = ddev->dev;
struct gpio_desc *gpiod;
/*
* Raise the GPIO reset line for the corresponding pipe to HIGH,
* this is probably because it is active low so this takes the
* respective pipe out of reset. (We have no code to put it back
* into reset in this driver.)
*/
switch (pipe) {
case 0:
gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
break;
case 2:
gpiod = gpiod_get(dev, "dsi-pipe2-reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
break;
default:
DRM_DEV_ERROR(dev, "Invalid output pipe\n");
return -EINVAL;
}
gpiod_put(gpiod);
/* Flush posted writes on the device */
gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_ASIS);
if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
gpiod_get_value(gpiod);
gpiod_put(gpiod);
return 0;
}
/*
* 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,
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 (pipe != 0 && pipe != 2) {
DRM_ERROR("Invalid parameter\n");
return;
}
/*create a new connector*/
dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
if (!dsi_connector) {
DRM_ERROR("No memory");
return;
}
dsi_connector->pipe = pipe;
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 panel 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;
dsi_connector->base.save = mdfld_dsi_connector_save;
dsi_connector->base.restore = mdfld_dsi_connector_restore;
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_object_attach_property(&connector->base,
dev->mode_config.scaling_mode_property,
DRM_MODE_SCALE_FULLSCREEN);
drm_object_attach_property(&connector->base,
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_connector_register(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);
}
/*
* 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 <asm/intel-mid.h>
#include <drm/drm.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include "mdfld_output.h"
#include "psb_drv.h"
#include "psb_intel_drv.h"
#include "psb_intel_reg.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 {
struct gma_connector base;
int pipe;
void *private;
void *pkg_sender;
/* Connection status */
enum drm_connector_status status;
};
struct mdfld_dsi_encoder {
struct gma_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 gma_connector *gma_connector;
gma_connector = to_gma_connector(connector);
return container_of(gma_connector, struct mdfld_dsi_connector, base);
}
static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder(
struct drm_encoder *encoder)
{
struct gma_encoder *gma_encoder;
gma_encoder = to_gma_encoder(encoder);
return container_of(gma_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,
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_panel_reset(struct drm_device *dev, int pipe);
#endif /*__MDFLD_DSI_OUTPUT_H__*/
/*
* 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/delay.h>
#include <linux/freezer.h>
#include <video/mipi_display.h>
#include "mdfld_dsi_dpi.h"
#include "mdfld_dsi_output.h"
#include "mdfld_dsi_pkg_sender.h"
#define MDFLD_DSI_READ_MAX_COUNT 5000
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 MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_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 == MIPI_DCS_ENTER_SLEEP_MODE)) {
/*TODO: replace it with msleep later*/
mdelay(120);
}
if (unlikely(cmd == MIPI_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 MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_LONG_WRITE:
cmd = *data;
break;
default:
return 0;
}
/*update panel status*/
if (unlikely(cmd == MIPI_DCS_ENTER_SLEEP_MODE)) {
sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP;
/*TODO: replace it with msleep later*/
mdelay(120);
} else if (unlikely(cmd == MIPI_DCS_EXIT_SLEEP_MODE)) {
sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP;
/*TODO: replace it with msleep later*/
mdelay(120);
} else if (unlikely(cmd == MIPI_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 MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_READ:
ret = send_short_pkg(sender, data_type, data[0], data[1], hs);
break;
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_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, MIPI_DSI_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 = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
data[1] = param;
} else {
data_type = MIPI_DSI_DCS_SHORT_WRITE;
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 > 2) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
switch (param_num) {
case 0:
data_type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
data[0] = 0;
data[1] = 0;
break;
case 1:
data_type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
data[0] = param0;
data[1] = 0;
break;
case 2:
data_type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
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, MIPI_DSI_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;
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;
}
dev = sender->dev;
/**
* 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, MIPI_DSI_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;
struct drm_psb_private *dev_priv = dev->dev_private;
const struct psb_offset *map = &dev_priv->regmap[pipe];
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*/
/* FIXME: should just copy the regmap ptr ? */
pkg_sender->dpll_reg = map->dpll;
pkg_sender->dspcntr_reg = map->cntr;
pkg_sender->pipeconf_reg = map->conf;
pkg_sender->dsplinoff_reg = map->linoff;
pkg_sender->dspsurf_reg = map->surf;
pkg_sender->pipestat_reg = map->status;
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);
}
/*
* 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;
};
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
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2006-2007 Intel Corporation
*
* Authors:
* Eric Anholt <eric@anholt.net>
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fourcc.h>
#include "framebuffer.h"
#include "gma_display.h"
#include "mdfld_dsi_output.h"
#include "mdfld_output.h"
#include "psb_intel_reg.h"
/* Hardcoded currently */
static int ksel = KSEL_CRYSTAL_19;
struct psb_intel_range_t {
int min, max;
};
struct mrst_limit_t {
struct psb_intel_range_t dot, m, p1;
};
struct mrst_clock_t {
/* derived values */
int dot;
int m;
int p1;
};
#define COUNT_MAX 0x10000000
void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
const struct psb_offset *map = &dev_priv->regmap[pipe];
int count, temp;
switch (pipe) {
case 0:
case 1:
case 2:
break;
default:
DRM_ERROR("Illegal Pipe Number.\n");
return;
}
/* FIXME JLIU7_PO */
gma_wait_for_vblank(dev);
return;
/* Wait for for the pipe disable to take effect. */
for (count = 0; count < COUNT_MAX; count++) {
temp = REG_READ(map->conf);
if ((temp & PIPEACONF_PIPE_STATE) == 0)
break;
}
}
void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
const struct psb_offset *map = &dev_priv->regmap[pipe];
int count, temp;
switch (pipe) {
case 0:
case 1:
case 2:
break;
default:
DRM_ERROR("Illegal Pipe Number.\n");
return;
}
/* FIXME JLIU7_PO */
gma_wait_for_vblank(dev);
return;
/* Wait for for the pipe enable to take effect. */
for (count = 0; count < COUNT_MAX; count++) {
temp = REG_READ(map->conf);
if (temp & PIPEACONF_PIPE_STATE)
break;
}
}
/*
* Return the pipe currently connected to the panel fitter,
* or -1 if the panel fitter is not present or not in use
*/
static int psb_intel_panel_fitter_pipe(struct drm_device *dev)
{
u32 pfit_control;
pfit_control = REG_READ(PFIT_CONTROL);
/* See if the panel fitter is in use */
if ((pfit_control & PFIT_ENABLE) == 0)
return -1;
/* 965 can place panel fitter on either pipe */
return (pfit_control >> 29) & 0x3;
}
static int check_fb(struct drm_framebuffer *fb)
{
if (!fb)
return 0;
switch (fb->format->cpp[0] * 8) {
case 8:
case 16:
case 24:
case 32:
return 0;
default:
DRM_ERROR("Unknown color depth\n");
return -EINVAL;
}
}
static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct drm_framebuffer *fb = crtc->primary->fb;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
u32 dspcntr;
int ret;
dev_dbg(dev->dev, "pipe = 0x%x.\n", pipe);
/* no fb bound */
if (!fb) {
dev_dbg(dev->dev, "No FB bound\n");
return 0;
}
ret = check_fb(fb);
if (ret)
return ret;
if (pipe > 2) {
DRM_ERROR("Illegal Pipe Number.\n");
return -EINVAL;
}
if (!gma_power_begin(dev, true))
return 0;
start = to_gtt_range(fb->obj[0])->offset;
offset = y * fb->pitches[0] + x * fb->format->cpp[0];
REG_WRITE(map->stride, fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (fb->format->cpp[0] * 8) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
if (fb->format->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;
break;
case 24:
case 32:
dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
break;
}
REG_WRITE(map->cntr, dspcntr);
dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n",
start, offset, x, y);
REG_WRITE(map->linoff, offset);
REG_READ(map->linoff);
REG_WRITE(map->surf, start);
REG_READ(map->surf);
gma_power_end(dev);
return 0;
}
/*
* Disable the pipe, plane and pll.
*
*/
void mdfld_disable_crtc(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
const struct psb_offset *map = &dev_priv->regmap[pipe];
u32 temp;
dev_dbg(dev->dev, "pipe = %d\n", pipe);
if (pipe != 1)
mdfld_dsi_gen_fifo_ready(dev, MIPI_GEN_FIFO_STAT_REG(pipe),
HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY);
/* Disable display plane */
temp = REG_READ(map->cntr);
if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
REG_WRITE(map->cntr,
temp & ~DISPLAY_PLANE_ENABLE);
/* Flush the plane changes */
REG_WRITE(map->base, REG_READ(map->base));
REG_READ(map->base);
}
/* FIXME_JLIU7 MDFLD_PO revisit */
/* Next, disable display pipes */
temp = REG_READ(map->conf);
if ((temp & PIPEACONF_ENABLE) != 0) {
temp &= ~PIPEACONF_ENABLE;
temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF;
REG_WRITE(map->conf, temp);
REG_READ(map->conf);
/* Wait for for the pipe disable to take effect. */
mdfldWaitForPipeDisable(dev, pipe);
}
temp = REG_READ(map->dpll);
if (temp & DPLL_VCO_ENABLE) {
if ((pipe != 1 &&
!((REG_READ(PIPEACONF) | REG_READ(PIPECCONF))
& PIPEACONF_ENABLE)) || pipe == 1) {
temp &= ~(DPLL_VCO_ENABLE);
REG_WRITE(map->dpll, temp);
REG_READ(map->dpll);
/* Wait for the clocks to turn off. */
/* FIXME_MDFLD PO may need more delay */
udelay(500);
if (!(temp & MDFLD_PWR_GATE_EN)) {
/* gating power of DPLL */
REG_WRITE(map->dpll, temp | MDFLD_PWR_GATE_EN);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(5000);
}
}
}
}
/*
* Sets the power management mode of the pipe and plane.
*
* This code should probably grow support for turning the cursor off and back
* on appropriately at the same time as we're turning the pipe off/on.
*/
static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
u32 pipeconf = dev_priv->pipeconf[pipe];
u32 temp;
int timeout = 0;
dev_dbg(dev->dev, "mode = %d, pipe = %d\n", mode, pipe);
/* Note: Old code uses pipe a stat for pipe b but that appears
to be a bug */
if (!gma_power_begin(dev, true))
return;
/* XXX: When our outputs are all unaware of DPMS modes other than off
* and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
*/
switch (mode) {
case DRM_MODE_DPMS_ON:
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
/* Enable the DPLL */
temp = REG_READ(map->dpll);
if ((temp & DPLL_VCO_ENABLE) == 0) {
/* When ungating power of DPLL, needs to wait 0.5us
before enable the VCO */
if (temp & MDFLD_PWR_GATE_EN) {
temp &= ~MDFLD_PWR_GATE_EN;
REG_WRITE(map->dpll, temp);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
}
REG_WRITE(map->dpll, temp);
REG_READ(map->dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE);
REG_READ(map->dpll);
/**
* wait for DSI PLL to lock
* NOTE: only need to poll status of pipe 0 and pipe 1,
* since both MIPI pipes share the same PLL.
*/
while ((pipe != 2) && (timeout < 20000) &&
!(REG_READ(map->conf) & PIPECONF_DSIPLL_LOCK)) {
udelay(150);
timeout++;
}
}
/* Enable the plane */
temp = REG_READ(map->cntr);
if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
REG_WRITE(map->cntr,
temp | DISPLAY_PLANE_ENABLE);
/* Flush the plane changes */
REG_WRITE(map->base, REG_READ(map->base));
}
/* Enable the pipe */
temp = REG_READ(map->conf);
if ((temp & PIPEACONF_ENABLE) == 0) {
REG_WRITE(map->conf, pipeconf);
/* Wait for for the pipe enable to take effect. */
mdfldWaitForPipeEnable(dev, pipe);
}
/*workaround for sighting 3741701 Random X blank display*/
/*perform w/a in video mode only on pipe A or C*/
if (pipe == 0 || pipe == 2) {
REG_WRITE(map->status, REG_READ(map->status));
msleep(100);
if (PIPE_VBLANK_STATUS & REG_READ(map->status))
dev_dbg(dev->dev, "OK");
else {
dev_dbg(dev->dev, "STUCK!!!!");
/*shutdown controller*/
temp = REG_READ(map->cntr);
REG_WRITE(map->cntr,
temp & ~DISPLAY_PLANE_ENABLE);
REG_WRITE(map->base, REG_READ(map->base));
/*mdfld_dsi_dpi_shut_down(dev, pipe);*/
REG_WRITE(0xb048, 1);
msleep(100);
temp = REG_READ(map->conf);
temp &= ~PIPEACONF_ENABLE;
REG_WRITE(map->conf, temp);
msleep(100); /*wait for pipe disable*/
REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 0);
msleep(100);
REG_WRITE(0xb004, REG_READ(0xb004));
/* try to bring the controller back up again*/
REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 1);
temp = REG_READ(map->cntr);
REG_WRITE(map->cntr,
temp | DISPLAY_PLANE_ENABLE);
REG_WRITE(map->base, REG_READ(map->base));
/*mdfld_dsi_dpi_turn_on(dev, pipe);*/
REG_WRITE(0xb048, 2);
msleep(100);
temp = REG_READ(map->conf);
temp |= PIPEACONF_ENABLE;
REG_WRITE(map->conf, temp);
}
}
gma_crtc_load_lut(crtc);
/* Give the overlay scaler a chance to enable
if it's on this pipe */
/* psb_intel_crtc_dpms_video(crtc, true); TODO */
break;
case DRM_MODE_DPMS_OFF:
/* Give the overlay scaler a chance to disable
* if it's on this pipe */
/* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */
if (pipe != 1)
mdfld_dsi_gen_fifo_ready(dev,
MIPI_GEN_FIFO_STAT_REG(pipe),
HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY);
/* Disable the VGA plane that we never use */
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
/* Disable display plane */
temp = REG_READ(map->cntr);
if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
REG_WRITE(map->cntr,
temp & ~DISPLAY_PLANE_ENABLE);
/* Flush the plane changes */
REG_WRITE(map->base, REG_READ(map->base));
REG_READ(map->base);
}
/* Next, disable display pipes */
temp = REG_READ(map->conf);
if ((temp & PIPEACONF_ENABLE) != 0) {
temp &= ~PIPEACONF_ENABLE;
temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF;
REG_WRITE(map->conf, temp);
REG_READ(map->conf);
/* Wait for for the pipe disable to take effect. */
mdfldWaitForPipeDisable(dev, pipe);
}
temp = REG_READ(map->dpll);
if (temp & DPLL_VCO_ENABLE) {
if ((pipe != 1 && !((REG_READ(PIPEACONF)
| REG_READ(PIPECCONF)) & PIPEACONF_ENABLE))
|| pipe == 1) {
temp &= ~(DPLL_VCO_ENABLE);
REG_WRITE(map->dpll, temp);
REG_READ(map->dpll);
/* Wait for the clocks to turn off. */
/* FIXME_MDFLD PO may need more delay */
udelay(500);
}
}
break;
}
gma_power_end(dev);
}
#define MDFLD_LIMT_DPLL_19 0
#define MDFLD_LIMT_DPLL_25 1
#define MDFLD_LIMT_DPLL_83 2
#define MDFLD_LIMT_DPLL_100 3
#define MDFLD_LIMT_DSIPLL_19 4
#define MDFLD_LIMT_DSIPLL_25 5
#define MDFLD_LIMT_DSIPLL_83 6
#define MDFLD_LIMT_DSIPLL_100 7
#define MDFLD_DOT_MIN 19750
#define MDFLD_DOT_MAX 120000
#define MDFLD_DPLL_M_MIN_19 113
#define MDFLD_DPLL_M_MAX_19 155
#define MDFLD_DPLL_P1_MIN_19 2
#define MDFLD_DPLL_P1_MAX_19 10
#define MDFLD_DPLL_M_MIN_25 101
#define MDFLD_DPLL_M_MAX_25 130
#define MDFLD_DPLL_P1_MIN_25 2
#define MDFLD_DPLL_P1_MAX_25 10
#define MDFLD_DPLL_M_MIN_83 64
#define MDFLD_DPLL_M_MAX_83 64
#define MDFLD_DPLL_P1_MIN_83 2
#define MDFLD_DPLL_P1_MAX_83 2
#define MDFLD_DPLL_M_MIN_100 64
#define MDFLD_DPLL_M_MAX_100 64
#define MDFLD_DPLL_P1_MIN_100 2
#define MDFLD_DPLL_P1_MAX_100 2
#define MDFLD_DSIPLL_M_MIN_19 131
#define MDFLD_DSIPLL_M_MAX_19 175
#define MDFLD_DSIPLL_P1_MIN_19 3
#define MDFLD_DSIPLL_P1_MAX_19 8
#define MDFLD_DSIPLL_M_MIN_25 97
#define MDFLD_DSIPLL_M_MAX_25 140
#define MDFLD_DSIPLL_P1_MIN_25 3
#define MDFLD_DSIPLL_P1_MAX_25 9
#define MDFLD_DSIPLL_M_MIN_83 33
#define MDFLD_DSIPLL_M_MAX_83 92
#define MDFLD_DSIPLL_P1_MIN_83 2
#define MDFLD_DSIPLL_P1_MAX_83 3
#define MDFLD_DSIPLL_M_MIN_100 97
#define MDFLD_DSIPLL_M_MAX_100 140
#define MDFLD_DSIPLL_P1_MIN_100 3
#define MDFLD_DSIPLL_P1_MAX_100 9
static const struct mrst_limit_t mdfld_limits[] = {
{ /* MDFLD_LIMT_DPLL_19 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DPLL_M_MIN_19, .max = MDFLD_DPLL_M_MAX_19},
.p1 = {.min = MDFLD_DPLL_P1_MIN_19, .max = MDFLD_DPLL_P1_MAX_19},
},
{ /* MDFLD_LIMT_DPLL_25 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DPLL_M_MIN_25, .max = MDFLD_DPLL_M_MAX_25},
.p1 = {.min = MDFLD_DPLL_P1_MIN_25, .max = MDFLD_DPLL_P1_MAX_25},
},
{ /* MDFLD_LIMT_DPLL_83 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DPLL_M_MIN_83, .max = MDFLD_DPLL_M_MAX_83},
.p1 = {.min = MDFLD_DPLL_P1_MIN_83, .max = MDFLD_DPLL_P1_MAX_83},
},
{ /* MDFLD_LIMT_DPLL_100 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DPLL_M_MIN_100, .max = MDFLD_DPLL_M_MAX_100},
.p1 = {.min = MDFLD_DPLL_P1_MIN_100, .max = MDFLD_DPLL_P1_MAX_100},
},
{ /* MDFLD_LIMT_DSIPLL_19 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DSIPLL_M_MIN_19, .max = MDFLD_DSIPLL_M_MAX_19},
.p1 = {.min = MDFLD_DSIPLL_P1_MIN_19, .max = MDFLD_DSIPLL_P1_MAX_19},
},
{ /* MDFLD_LIMT_DSIPLL_25 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DSIPLL_M_MIN_25, .max = MDFLD_DSIPLL_M_MAX_25},
.p1 = {.min = MDFLD_DSIPLL_P1_MIN_25, .max = MDFLD_DSIPLL_P1_MAX_25},
},
{ /* MDFLD_LIMT_DSIPLL_83 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DSIPLL_M_MIN_83, .max = MDFLD_DSIPLL_M_MAX_83},
.p1 = {.min = MDFLD_DSIPLL_P1_MIN_83, .max = MDFLD_DSIPLL_P1_MAX_83},
},
{ /* MDFLD_LIMT_DSIPLL_100 */
.dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
.m = {.min = MDFLD_DSIPLL_M_MIN_100, .max = MDFLD_DSIPLL_M_MAX_100},
.p1 = {.min = MDFLD_DSIPLL_P1_MIN_100, .max = MDFLD_DSIPLL_P1_MAX_100},
},
};
#define MDFLD_M_MIN 21
#define MDFLD_M_MAX 180
static const u32 mdfld_m_converts[] = {
/* M configuration table from 9-bit LFSR table */
224, 368, 440, 220, 366, 439, 219, 365, 182, 347, /* 21 - 30 */
173, 342, 171, 85, 298, 149, 74, 37, 18, 265, /* 31 - 40 */
388, 194, 353, 432, 216, 108, 310, 155, 333, 166, /* 41 - 50 */
83, 41, 276, 138, 325, 162, 337, 168, 340, 170, /* 51 - 60 */
341, 426, 469, 234, 373, 442, 221, 110, 311, 411, /* 61 - 70 */
461, 486, 243, 377, 188, 350, 175, 343, 427, 213, /* 71 - 80 */
106, 53, 282, 397, 354, 227, 113, 56, 284, 142, /* 81 - 90 */
71, 35, 273, 136, 324, 418, 465, 488, 500, 506, /* 91 - 100 */
253, 126, 63, 287, 399, 455, 483, 241, 376, 444, /* 101 - 110 */
478, 495, 503, 251, 381, 446, 479, 239, 375, 443, /* 111 - 120 */
477, 238, 119, 315, 157, 78, 295, 147, 329, 420, /* 121 - 130 */
210, 105, 308, 154, 77, 38, 275, 137, 68, 290, /* 131 - 140 */
145, 328, 164, 82, 297, 404, 458, 485, 498, 249, /* 141 - 150 */
380, 190, 351, 431, 471, 235, 117, 314, 413, 206, /* 151 - 160 */
103, 51, 25, 12, 262, 387, 193, 96, 48, 280, /* 161 - 170 */
396, 198, 99, 305, 152, 76, 294, 403, 457, 228, /* 171 - 180 */
};
static const struct mrst_limit_t *mdfld_limit(struct drm_crtc *crtc)
{
const struct mrst_limit_t *limit = NULL;
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
if (gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)
|| gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI2)) {
if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19))
limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_19];
else if (ksel == KSEL_BYPASS_25)
limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_25];
else if ((ksel == KSEL_BYPASS_83_100) &&
(dev_priv->core_freq == 166))
limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_83];
else if ((ksel == KSEL_BYPASS_83_100) &&
(dev_priv->core_freq == 100 ||
dev_priv->core_freq == 200))
limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_100];
} else if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) {
if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19))
limit = &mdfld_limits[MDFLD_LIMT_DPLL_19];
else if (ksel == KSEL_BYPASS_25)
limit = &mdfld_limits[MDFLD_LIMT_DPLL_25];
else if ((ksel == KSEL_BYPASS_83_100) &&
(dev_priv->core_freq == 166))
limit = &mdfld_limits[MDFLD_LIMT_DPLL_83];
else if ((ksel == KSEL_BYPASS_83_100) &&
(dev_priv->core_freq == 100 ||
dev_priv->core_freq == 200))
limit = &mdfld_limits[MDFLD_LIMT_DPLL_100];
} else {
limit = NULL;
dev_dbg(dev->dev, "mdfld_limit Wrong display type.\n");
}
return limit;
}
/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
static void mdfld_clock(int refclk, struct mrst_clock_t *clock)
{
clock->dot = (refclk * clock->m) / clock->p1;
}
/*
* Returns a set of divisors for the desired target clock with the given refclk,
* or FALSE. Divisor values are the actual divisors for
*/
static bool
mdfldFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
struct mrst_clock_t *best_clock)
{
struct mrst_clock_t clock;
const struct mrst_limit_t *limit = mdfld_limit(crtc);
int err = target;
memset(best_clock, 0, sizeof(*best_clock));
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
clock.p1++) {
int this_err;
mdfld_clock(refclk, &clock);
this_err = abs(clock.dot - target);
if (this_err < err) {
*best_clock = clock;
err = this_err;
}
}
}
return err != target;
}
static int mdfld_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct drm_psb_private *dev_priv = dev->dev_private;
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
int refclk = 0;
int clk_n = 0, clk_p2 = 0, clk_byte = 1, clk = 0, m_conv = 0,
clk_tmp = 0;
struct mrst_clock_t clock;
bool ok;
u32 dpll = 0, fp = 0;
bool is_mipi = false, is_mipi2 = false, is_hdmi = false;
struct drm_mode_config *mode_config = &dev->mode_config;
struct gma_encoder *gma_encoder = NULL;
uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
struct drm_encoder *encoder;
struct drm_connector *connector;
int timeout = 0;
int ret;
dev_dbg(dev->dev, "pipe = 0x%x\n", pipe);
ret = check_fb(crtc->primary->fb);
if (ret)
return ret;
dev_dbg(dev->dev, "adjusted_hdisplay = %d\n",
adjusted_mode->hdisplay);
dev_dbg(dev->dev, "adjusted_vdisplay = %d\n",
adjusted_mode->vdisplay);
dev_dbg(dev->dev, "adjusted_hsync_start = %d\n",
adjusted_mode->hsync_start);
dev_dbg(dev->dev, "adjusted_hsync_end = %d\n",
adjusted_mode->hsync_end);
dev_dbg(dev->dev, "adjusted_htotal = %d\n",
adjusted_mode->htotal);
dev_dbg(dev->dev, "adjusted_vsync_start = %d\n",
adjusted_mode->vsync_start);
dev_dbg(dev->dev, "adjusted_vsync_end = %d\n",
adjusted_mode->vsync_end);
dev_dbg(dev->dev, "adjusted_vtotal = %d\n",
adjusted_mode->vtotal);
dev_dbg(dev->dev, "adjusted_clock = %d\n",
adjusted_mode->clock);
dev_dbg(dev->dev, "hdisplay = %d\n",
mode->hdisplay);
dev_dbg(dev->dev, "vdisplay = %d\n",
mode->vdisplay);
if (!gma_power_begin(dev, true))
return 0;
memcpy(&gma_crtc->saved_mode, mode,
sizeof(struct drm_display_mode));
memcpy(&gma_crtc->saved_adjusted_mode, adjusted_mode,
sizeof(struct drm_display_mode));
list_for_each_entry(connector, &mode_config->connector_list, head) {
encoder = connector->encoder;
if (!encoder)
continue;
if (encoder->crtc != crtc)
continue;
gma_encoder = gma_attached_encoder(connector);
switch (gma_encoder->type) {
case INTEL_OUTPUT_MIPI:
is_mipi = true;
break;
case INTEL_OUTPUT_MIPI2:
is_mipi2 = true;
break;
case INTEL_OUTPUT_HDMI:
is_hdmi = true;
break;
}
}
/* Disable the VGA plane that we never use */
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
/* Disable the panel fitter if it was on our pipe */
if (psb_intel_panel_fitter_pipe(dev) == pipe)
REG_WRITE(PFIT_CONTROL, 0);
/* pipesrc and dspsize control the size that is scaled from,
* which should always be the user's requested size.
*/
if (pipe == 1) {
/* FIXME: To make HDMI display with 864x480 (TPO), 480x864
* (PYR) or 480x854 (TMD), set the sprite width/height and
* souce image size registers with the adjusted mode for
* pipe B.
*/
/*
* The defined sprite rectangle must always be completely
* contained within the displayable area of the screen image
* (frame buffer).
*/
REG_WRITE(map->size, ((min(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16)
| (min(mode->crtc_hdisplay, adjusted_mode->crtc_hdisplay) - 1));
/* Set the CRTC with encoder mode. */
REG_WRITE(map->src, ((mode->crtc_hdisplay - 1) << 16)
| (mode->crtc_vdisplay - 1));
} else {
REG_WRITE(map->size,
((mode->crtc_vdisplay - 1) << 16) |
(mode->crtc_hdisplay - 1));
REG_WRITE(map->src,
((mode->crtc_hdisplay - 1) << 16) |
(mode->crtc_vdisplay - 1));
}
REG_WRITE(map->pos, 0);
if (gma_encoder)
drm_object_property_get_value(&connector->base,
dev->mode_config.scaling_mode_property, &scalingType);
if (scalingType == DRM_MODE_SCALE_NO_SCALE) {
/* Medfield doesn't have register support for centering so we
* need to mess with the h/vblank and h/vsync start and ends
* to get centering
*/
int offsetX = 0, offsetY = 0;
offsetX = (adjusted_mode->crtc_hdisplay -
mode->crtc_hdisplay) / 2;
offsetY = (adjusted_mode->crtc_vdisplay -
mode->crtc_vdisplay) / 2;
REG_WRITE(map->htotal, (mode->crtc_hdisplay - 1) |
((adjusted_mode->crtc_htotal - 1) << 16));
REG_WRITE(map->vtotal, (mode->crtc_vdisplay - 1) |
((adjusted_mode->crtc_vtotal - 1) << 16));
REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start -
offsetX - 1) |
((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16));
REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start -
offsetX - 1) |
((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16));
REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start -
offsetY - 1) |
((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16));
REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start -
offsetY - 1) |
((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16));
} else {
REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) |
((adjusted_mode->crtc_htotal - 1) << 16));
REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) |
((adjusted_mode->crtc_vtotal - 1) << 16));
REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) |
((adjusted_mode->crtc_hblank_end - 1) << 16));
REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) |
((adjusted_mode->crtc_hsync_end - 1) << 16));
REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) |
((adjusted_mode->crtc_vblank_end - 1) << 16));
REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) |
((adjusted_mode->crtc_vsync_end - 1) << 16));
}
/* Flush the plane changes */
{
const struct drm_crtc_helper_funcs *crtc_funcs =
crtc->helper_private;
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
}
/* setup pipeconf */
dev_priv->pipeconf[pipe] = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */
/* Set up the display plane register */
dev_priv->dspcntr[pipe] = REG_READ(map->cntr);
dev_priv->dspcntr[pipe] |= pipe << DISPPLANE_SEL_PIPE_POS;
dev_priv->dspcntr[pipe] |= DISPLAY_PLANE_ENABLE;
if (is_mipi2)
goto mrst_crtc_mode_set_exit;
clk = adjusted_mode->clock;
if (is_hdmi) {
if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) {
refclk = 19200;
if (is_mipi || is_mipi2)
clk_n = 1, clk_p2 = 8;
else if (is_hdmi)
clk_n = 1, clk_p2 = 10;
} else if (ksel == KSEL_BYPASS_25) {
refclk = 25000;
if (is_mipi || is_mipi2)
clk_n = 1, clk_p2 = 8;
else if (is_hdmi)
clk_n = 1, clk_p2 = 10;
} else if ((ksel == KSEL_BYPASS_83_100) &&
dev_priv->core_freq == 166) {
refclk = 83000;
if (is_mipi || is_mipi2)
clk_n = 4, clk_p2 = 8;
else if (is_hdmi)
clk_n = 4, clk_p2 = 10;
} else if ((ksel == KSEL_BYPASS_83_100) &&
(dev_priv->core_freq == 100 ||
dev_priv->core_freq == 200)) {
refclk = 100000;
if (is_mipi || is_mipi2)
clk_n = 4, clk_p2 = 8;
else if (is_hdmi)
clk_n = 4, clk_p2 = 10;
}
if (is_mipi)
clk_byte = dev_priv->bpp / 8;
else if (is_mipi2)
clk_byte = dev_priv->bpp2 / 8;
clk_tmp = clk * clk_n * clk_p2 * clk_byte;
dev_dbg(dev->dev, "clk = %d, clk_n = %d, clk_p2 = %d.\n",
clk, clk_n, clk_p2);
dev_dbg(dev->dev, "adjusted_mode->clock = %d, clk_tmp = %d.\n",
adjusted_mode->clock, clk_tmp);
ok = mdfldFindBestPLL(crtc, clk_tmp, refclk, &clock);
if (!ok) {
DRM_ERROR
("mdfldFindBestPLL fail in mdfld_crtc_mode_set.\n");
} else {
m_conv = mdfld_m_converts[(clock.m - MDFLD_M_MIN)];
dev_dbg(dev->dev, "dot clock = %d,"
"m = %d, p1 = %d, m_conv = %d.\n",
clock.dot, clock.m,
clock.p1, m_conv);
}
dpll = REG_READ(map->dpll);
if (dpll & DPLL_VCO_ENABLE) {
dpll &= ~DPLL_VCO_ENABLE;
REG_WRITE(map->dpll, dpll);
REG_READ(map->dpll);
/* FIXME jliu7 check the DPLL lock bit PIPEACONF[29] */
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
/* reset M1, N1 & P1 */
REG_WRITE(map->fp0, 0);
dpll &= ~MDFLD_P1_MASK;
REG_WRITE(map->dpll, dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
}
/* 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;
REG_WRITE(map->dpll, dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
}
dpll = 0;
if (is_hdmi)
dpll |= MDFLD_VCO_SEL;
fp = (clk_n / 2) << 16;
fp |= m_conv;
/* compute bitmask from p1 value */
dpll |= (1 << (clock.p1 - 2)) << 17;
} else {
dpll = 0x00800000;
fp = 0x000000c1;
}
REG_WRITE(map->fp0, fp);
REG_WRITE(map->dpll, dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
dpll |= DPLL_VCO_ENABLE;
REG_WRITE(map->dpll, dpll);
REG_READ(map->dpll);
/* wait for DSI PLL to lock */
while (timeout < 20000 &&
!(REG_READ(map->conf) & PIPECONF_DSIPLL_LOCK)) {
udelay(150);
timeout++;
}
if (is_mipi)
goto mrst_crtc_mode_set_exit;
dev_dbg(dev->dev, "is_mipi = 0x%x\n", is_mipi);
REG_WRITE(map->conf, dev_priv->pipeconf[pipe]);
REG_READ(map->conf);
/* Wait for for the pipe enable to take effect. */
REG_WRITE(map->cntr, dev_priv->dspcntr[pipe]);
gma_wait_for_vblank(dev);
mrst_crtc_mode_set_exit:
gma_power_end(dev);
return 0;
}
const struct drm_crtc_helper_funcs mdfld_helper_funcs = {
.dpms = mdfld_crtc_dpms,
.mode_set = mdfld_crtc_mode_set,
.mode_set_base = mdfld__intel_pipe_set_base,
.prepare = gma_crtc_prepare,
.commit = gma_crtc_commit,
};
/*
* 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, &mdfld_tpo_vid_funcs);
break;
case TC35876X:
tc35876x_init(dev);
mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tc35876x_funcs);
break;
case TMD_VID:
mdfld_dsi_output_init(dev, mipi_pipe, &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;
}
/*
* 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_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)(struct drm_device *, int);
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
/*
* 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 <linux/delay.h>
#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,
};
const struct panel_funcs mdfld_tmd_vid_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,
};
/*
* 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;
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode)
return NULL;
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,
};
const struct panel_funcs mdfld_tpo_vid_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,
};
......@@ -48,7 +48,6 @@ static inline uint32_t psb_mmu_pd_index(uint32_t offset)
return offset >> PSB_PDE_SHIFT;
}
#if defined(CONFIG_X86)
static inline void psb_clflush(void *addr)
{
__asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory");
......@@ -63,13 +62,6 @@ static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
psb_clflush(addr);
mb();
}
#else
static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
{;
}
#endif
static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, int force)
{
......@@ -293,7 +285,6 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
*ptes++ = pd->invalid_pte;
#if defined(CONFIG_X86)
if (pd->driver->has_clflush && pd->hw_context != -1) {
mb();
for (i = 0; i < clflush_count; ++i) {
......@@ -302,7 +293,6 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
}
mb();
}
#endif
kunmap_atomic(v);
spin_unlock(lock);
......@@ -459,7 +449,6 @@ struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
driver->has_clflush = 0;
#if defined(CONFIG_X86)
if (boot_cpu_has(X86_FEATURE_CLFLUSH)) {
uint32_t tfms, misc, cap0, cap4, clflush_size;
......@@ -476,7 +465,6 @@ struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
driver->clflush_mask = driver->clflush_add - 1;
driver->clflush_mask = ~driver->clflush_mask;
}
#endif
up_write(&driver->sem);
return driver;
......@@ -486,7 +474,6 @@ struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
return NULL;
}
#if defined(CONFIG_X86)
static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
uint32_t num_pages, uint32_t desired_tile_stride,
uint32_t hw_tile_stride)
......@@ -534,14 +521,6 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
}
mb();
}
#else
static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
uint32_t num_pages, uint32_t desired_tile_stride,
uint32_t hw_tile_stride)
{
drm_ttm_cache_flush();
}
#endif
void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages)
......
......@@ -46,13 +46,12 @@ static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
* PowerVR SGX535 - Poulsbo - Intel GMA 500, Intel Atom Z5xx
* PowerVR SGX535 - Moorestown - Intel GMA 600
* PowerVR SGX535 - Oaktrail - Intel GMA 600, Intel Atom Z6xx, E6xx
* PowerVR SGX540 - Medfield - Intel Atom Z2460
* PowerVR SGX544MP2 - Medfield -
* PowerVR SGX545 - Cedartrail - Intel GMA 3600, Intel Atom D2500, N2600
* PowerVR SGX545 - Cedartrail - Intel GMA 3650, Intel Atom D2550, D2700,
* N2800
*/
static const struct pci_device_id pciidlist[] = {
/* Poulsbo */
{ 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
{ 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
#if defined(CONFIG_DRM_GMA600)
......@@ -66,17 +65,7 @@ static const struct pci_device_id pciidlist[] = {
{ 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 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)
/* Cedartrail */
{ 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 },
{ 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
......@@ -93,7 +82,6 @@ static const struct pci_device_id pciidlist[] = {
{ 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
#endif
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pciidlist);
......
......@@ -40,19 +40,16 @@ enum {
CHIP_PSB_8108 = 0, /* Poulsbo */
CHIP_PSB_8109 = 1, /* Poulsbo */
CHIP_MRST_4100 = 2, /* Moorestown/Oaktrail */
CHIP_MFLD_0130 = 3, /* Medfield */
};
#define IS_PSB(drm) ((to_pci_dev((drm)->dev)->device & 0xfffe) == 0x8108)
#define IS_MRST(drm) ((to_pci_dev((drm)->dev)->device & 0xfff0) == 0x4100)
#define IS_MFLD(drm) ((to_pci_dev((drm)->dev)->device & 0xfff8) == 0x0130)
#define IS_CDV(drm) ((to_pci_dev((drm)->dev)->device & 0xfff0) == 0x0be0)
/* Hardware offsets */
#define PSB_VDC_OFFSET 0x00000000
#define PSB_VDC_SIZE 0x000080000
#define MRST_MMIO_SIZE 0x0000C0000
#define MDFLD_MMIO_SIZE 0x000100000
#define PSB_SGX_SIZE 0x8000
#define PSB_SGX_OFFSET 0x00040000
#define MRST_SGX_OFFSET 0x00080000
......@@ -109,8 +106,6 @@ enum {
#define _PSB_DPST_PIPEA_FLAG (1<<6)
#define _PSB_PIPEA_EVENT_FLAG (1<<6)
#define _PSB_VSYNC_PIPEA_FLAG (1<<7)
#define _MDFLD_MIPIA_FLAG (1<<16)
#define _MDFLD_MIPIC_FLAG (1<<17)
#define _PSB_IRQ_DISP_HOTSYNC (1<<17)
#define _PSB_IRQ_SGX_FLAG (1<<18)
#define _PSB_IRQ_MSVDX_FLAG (1<<19)
......@@ -119,13 +114,6 @@ enum {
#define _PSB_PIPE_EVENT_FLAG (_PSB_VSYNC_PIPEA_FLAG | \
_PSB_VSYNC_PIPEB_FLAG)
/* This flag includes all the display IRQ bits excepts the vblank irqs. */
#define _MDFLD_DISP_ALL_IRQ_FLAG (_MDFLD_PIPEC_EVENT_FLAG | \
_MDFLD_PIPEB_EVENT_FLAG | \
_PSB_PIPEA_EVENT_FLAG | \
_PSB_VSYNC_PIPEA_FLAG | \
_MDFLD_MIPIA_FLAG | \
_MDFLD_MIPIC_FLAG)
#define PSB_INT_IDENTITY_R 0x20A4
#define PSB_INT_MASK_R 0x20A8
#define PSB_INT_ENABLE_R 0x20A0
......@@ -191,25 +179,6 @@ enum {
#define PSB_WATCHDOG_DELAY (HZ * 2)
#define PSB_LID_DELAY (HZ / 10)
#define MDFLD_PNW_B0 0x04
#define MDFLD_PNW_C0 0x08
#define MDFLD_DSR_2D_3D_0 (1 << 0)
#define MDFLD_DSR_2D_3D_2 (1 << 1)
#define MDFLD_DSR_CURSOR_0 (1 << 2)
#define MDFLD_DSR_CURSOR_2 (1 << 3)
#define MDFLD_DSR_OVERLAY_0 (1 << 4)
#define MDFLD_DSR_OVERLAY_2 (1 << 5)
#define MDFLD_DSR_MIPI_CONTROL (1 << 6)
#define MDFLD_DSR_DAMAGE_MASK_0 ((1 << 0) | (1 << 2) | (1 << 4))
#define MDFLD_DSR_DAMAGE_MASK_2 ((1 << 1) | (1 << 3) | (1 << 5))
#define MDFLD_DSR_2D_3D (MDFLD_DSR_2D_3D_0 | MDFLD_DSR_2D_3D_2)
#define MDFLD_DSR_RR 45
#define MDFLD_DPU_ENABLE (1 << 31)
#define MDFLD_DSR_FULLSCREEN (1 << 30)
#define MDFLD_DSR_DELAY (HZ / MDFLD_DSR_RR)
#define PSB_PWR_STATE_ON 1
#define PSB_PWR_STATE_OFF 2
......@@ -382,16 +351,6 @@ struct psb_state {
uint32_t savePWM_CONTROL_LOGIC;
};
struct medfield_state {
uint32_t saveMIPI;
uint32_t saveMIPI_C;
uint32_t savePFIT_CONTROL;
uint32_t savePFIT_PGM_RATIOS;
uint32_t saveHDMIPHYMISCCTL;
uint32_t saveHDMIB_CONTROL;
};
struct cdv_state {
uint32_t saveDSPCLK_GATE_D;
uint32_t saveRAMCLK_GATE_D;
......@@ -417,7 +376,6 @@ struct psb_save_area {
uint32_t saveVBT;
union {
struct psb_state psb;
struct medfield_state mdfld;
struct cdv_state cdv;
};
uint32_t saveBLC_PWM_CTL2;
......@@ -590,8 +548,6 @@ struct drm_psb_private {
u32 pipeconf[3];
u32 dspcntr[3];
int mdfld_panel_id;
bool dplla_96mhz; /* DPLL data from the VBT */
struct {
......@@ -737,9 +693,6 @@ 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;
......@@ -779,25 +732,6 @@ static inline void MRST_MSG_WRITE32(int domain, uint port, uint offset,
pci_write_config_dword(pci_root, 0xD0, mcr);
pci_dev_put(pci_root);
}
static inline u32 MDFLD_MSG_READ32(int domain, uint port, uint offset)
{
int mcr = (0x10<<24) | (port << 16) | (offset << 8);
uint32_t ret_val = 0;
struct pci_dev *pci_root = pci_get_domain_bus_and_slot(domain, 0, 0);
pci_write_config_dword(pci_root, 0xD0, mcr);
pci_read_config_dword(pci_root, 0xD4, &ret_val);
pci_dev_put(pci_root);
return ret_val;
}
static inline void MDFLD_MSG_WRITE32(int domain, uint port, uint offset,
u32 value)
{
int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0;
struct pci_dev *pci_root = pci_get_domain_bus_and_slot(domain, 0, 0);
pci_write_config_dword(pci_root, 0xD4, value);
pci_write_config_dword(pci_root, 0xD0, mcr);
pci_dev_put(pci_root);
}
static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg)
{
......
......@@ -789,17 +789,9 @@ struct dpst_guardband {
* MOORESTOWN delta registers
*/
#define MRST_DPLL_A 0x0f014
#define MDFLD_DPLL_B 0x0f018
#define MDFLD_INPUT_REF_SEL (1 << 14)
#define MDFLD_VCO_SEL (1 << 16)
#define DPLLA_MODE_LVDS (2 << 26) /* mrst */
#define MDFLD_PLL_LATCHEN (1 << 28)
#define MDFLD_PWR_GATE_EN (1 << 30)
#define MDFLD_P1_MASK (0x1FF << 17)
#define MRST_FPA0 0x0f040
#define MRST_FPA1 0x0f044
#define MDFLD_DPLL_DIV0 0x0f048
#define MDFLD_DPLL_DIV1 0x0f04c
#define MRST_PERF_MODE 0x020f4
/*
......@@ -848,7 +840,6 @@ struct dpst_guardband {
#define MRST_DSPABASE 0x7019c
#define MRST_DSPBBASE 0x7119c
#define MDFLD_DSPCBASE 0x7219c
/*
* Moorestown registers.
......@@ -930,7 +921,6 @@ struct dpst_guardband {
#define DEVICE_RESET_REG 0xb01C
#define DPI_RESOLUTION_REG 0xb020
#define RES_V_POS 0x10
#define DBI_RESOLUTION_REG 0xb024 /* Reserved for MDFLD */
#define HORIZ_SYNC_PAD_COUNT_REG 0xb028
#define HORIZ_BACK_PORCH_COUNT_REG 0xb02C
#define HORIZ_FRONT_PORCH_COUNT_REG 0xb030
......
......@@ -10,7 +10,6 @@
#include <drm/drm_vblank.h>
#include "mdfld_output.h"
#include "power.h"
#include "psb_drv.h"
#include "psb_intel_reg.h"
......@@ -164,8 +163,7 @@ static void mid_pipe_event_handler(struct drm_device *dev, int pipe)
"%s, can't clear status bits for pipe %d, its value = 0x%x.\n",
__func__, pipe, PSB_RVDC32(pipe_stat_reg));
if (pipe_stat_val & PIPE_VBLANK_STATUS ||
(IS_MFLD(dev) && pipe_stat_val & PIPE_TE_STATUS)) {
if (pipe_stat_val & PIPE_VBLANK_STATUS) {
struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
unsigned long flags;
......@@ -263,11 +261,6 @@ irqreturn_t psb_irq_handler(int irq, void *arg)
if (vdc_stat & (_PSB_PIPE_EVENT_FLAG|_PSB_IRQ_ASLE))
dsp_int = 1;
/* FIXME: Handle Medfield
if (vdc_stat & _MDFLD_DISP_ALL_IRQ_FLAG)
dsp_int = 1;
*/
if (vdc_stat & _PSB_IRQ_SGX_FLAG)
sgx_int = 1;
if (vdc_stat & _PSB_IRQ_DISP_HOTSYNC)
......@@ -325,13 +318,6 @@ void psb_irq_preinstall(struct drm_device *dev)
if (dev->vblank[1].enabled)
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
/* FIXME: Handle Medfield irq mask
if (dev->vblank[1].enabled)
dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG;
if (dev->vblank[2].enabled)
dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG;
*/
/* Revisit this area - want per device masks ? */
if (dev_priv->ops->hotplug)
dev_priv->vdc_irq_mask |= _PSB_IRQ_DISP_HOTSYNC;
......@@ -504,11 +490,6 @@ int psb_enable_vblank(struct drm_crtc *crtc)
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);
......@@ -543,8 +524,6 @@ void psb_disable_vblank(struct drm_crtc *crtc)
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)
......@@ -559,55 +538,6 @@ void psb_disable_vblank(struct drm_crtc *crtc)
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
*/
......
......@@ -31,6 +31,4 @@ int psb_enable_vblank(struct drm_crtc *crtc);
void psb_disable_vblank(struct drm_crtc *crtc);
u32 psb_get_vblank_counter(struct drm_crtc *crtc);
int mdfld_enable_te(struct drm_device *dev, int pipe);
void mdfld_disable_te(struct drm_device *dev, int pipe);
#endif /* _PSB_IRQ_H_ */
......@@ -550,21 +550,7 @@
#define PSB_PM_SSC 0x20
#define PSB_PM_SSS 0x30
#define PSB_PWRGT_DISPLAY_MASK 0xc /*on a different BA than video/gfx*/
#define MDFLD_PWRGT_DISPLAY_A_CNTR 0x0000000c
#define MDFLD_PWRGT_DISPLAY_B_CNTR 0x0000c000
#define MDFLD_PWRGT_DISPLAY_C_CNTR 0x00030000
#define MDFLD_PWRGT_DISP_MIPI_CNTR 0x000c0000
#define MDFLD_PWRGT_DISPLAY_CNTR (MDFLD_PWRGT_DISPLAY_A_CNTR | MDFLD_PWRGT_DISPLAY_B_CNTR | MDFLD_PWRGT_DISPLAY_C_CNTR | MDFLD_PWRGT_DISP_MIPI_CNTR) /* 0x000fc00c */
/* Display SSS register bits are different in A0 vs. B0 */
#define PSB_PWRGT_GFX_MASK 0x3
#define MDFLD_PWRGT_DISPLAY_A_STS 0x000000c0
#define MDFLD_PWRGT_DISPLAY_B_STS 0x00000300
#define MDFLD_PWRGT_DISPLAY_C_STS 0x00000c00
#define PSB_PWRGT_GFX_MASK_B0 0xc3
#define MDFLD_PWRGT_DISPLAY_A_STS_B0 0x0000000c
#define MDFLD_PWRGT_DISPLAY_B_STS_B0 0x0000c000
#define MDFLD_PWRGT_DISPLAY_C_STS_B0 0x00030000
#define MDFLD_PWRGT_DISP_MIPI_STS 0x000c0000
#define MDFLD_PWRGT_DISPLAY_STS_A0 (MDFLD_PWRGT_DISPLAY_A_STS | MDFLD_PWRGT_DISPLAY_B_STS | MDFLD_PWRGT_DISPLAY_C_STS | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */
#define MDFLD_PWRGT_DISPLAY_STS_B0 (MDFLD_PWRGT_DISPLAY_A_STS_B0 | MDFLD_PWRGT_DISPLAY_B_STS_B0 | MDFLD_PWRGT_DISPLAY_C_STS_B0 | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */
#endif
/*
* 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 <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
#include <asm/intel_scu_ipc.h>
#include "mdfld_dsi_dpi.h"
#include "mdfld_dsi_pkg_sender.h"
#include "mdfld_output.h"
#include "tc35876x-dsi-lvds.h"
static struct i2c_client *tc35876x_client;
static struct i2c_client *cmi_lcd_i2c_client;
/* Panel GPIOs */
static struct gpio_desc *bridge_reset;
static struct gpio_desc *bridge_bl_enable;
static struct gpio_desc *backlight_voltage;
#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)
{
if (WARN(!tc35876x_client, "%s called before probe", __func__))
return;
dev_dbg(&tc35876x_client->dev, "%s: state %d\n", __func__, state);
if (!bridge_reset)
return;
if (state) {
gpiod_set_value_cansleep(bridge_reset, 0);
mdelay(10);
} else {
/* Pull MIPI Bridge reset pin to Low */
gpiod_set_value_cansleep(bridge_reset, 0);
mdelay(20);
/* Pull MIPI Bridge reset pin to High */
gpiod_set_value_cansleep(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->dev, "GPIOPWMCTRL read failed\n");
else
dev_warn(dev->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->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->dev, "PWM0CLKDIV set failed\n");
else
dev_dbg(dev->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)
{
if (WARN(!tc35876x_client, "%s called before probe", __func__))
return;
dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
if (bridge_bl_enable)
gpiod_set_value_cansleep(bridge_bl_enable, 0);
if (backlight_voltage)
gpiod_set_value_cansleep(backlight_voltage, 0);
}
void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev)
{
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__);
if (backlight_voltage) {
gpiod_set_value_cansleep(backlight_voltage, 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 (bridge_bl_enable)
gpiod_set_value_cansleep(bridge_bl_enable, 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->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->dev, "hdisplay(w) = %d\n", mode->hdisplay);
dev_info(dev->dev, "vdisplay(h) = %d\n", mode->vdisplay);
dev_info(dev->dev, "HSS = %d\n", mode->hsync_start);
dev_info(dev->dev, "HSE = %d\n", mode->hsync_end);
dev_info(dev->dev, "htotal = %d\n", mode->htotal);
dev_info(dev->dev, "VSS = %d\n", mode->vsync_start);
dev_info(dev->dev, "VSE = %d\n", mode->vsync_end);
dev_info(dev->dev, "vtotal = %d\n", mode->vtotal);
dev_info(dev->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)
{
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;
}
bridge_reset = devm_gpiod_get_optional(&client->dev, "bridge-reset", GPIOD_OUT_LOW);
if (IS_ERR(bridge_reset))
return PTR_ERR(bridge_reset);
if (bridge_reset)
gpiod_set_consumer_name(bridge_reset, "tc35876x bridge reset");
bridge_bl_enable = devm_gpiod_get_optional(&client->dev, "bl-en", GPIOD_OUT_LOW);
if (IS_ERR(bridge_bl_enable))
return PTR_ERR(bridge_bl_enable);
if (bridge_bl_enable)
gpiod_set_consumer_name(bridge_bl_enable, "tc35876x panel bl en");
backlight_voltage = devm_gpiod_get_optional(&client->dev, "vadd", GPIOD_OUT_LOW);
if (IS_ERR(backlight_voltage))
return PTR_ERR(backlight_voltage);
if (backlight_voltage)
gpiod_set_consumer_name(backlight_voltage, "tc35876x panel vadd");
tc35876x_client = client;
return 0;
}
static int tc35876x_bridge_remove(struct i2c_client *client)
{
dev_dbg(&client->dev, "%s\n", __func__);
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 = 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 = 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_client_device(adapter, &info);
if (IS_ERR(client)) {
pr_err("%s: creating I2C device failed\n", __func__);
i2c_put_adapter(adapter);
return PTR_ERR(client);
}
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,
};
const struct panel_funcs mdfld_tc35876x_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->dev, "%s\n", __func__);
cmi_lcd_hack_create_device();
r = i2c_add_driver(&cmi_lcd_i2c_driver);
if (r < 0)
dev_err(dev->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->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);
}
/*
* 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__*/
......@@ -201,7 +201,7 @@ static int lima_pm_busy(struct lima_device *ldev)
int ret;
/* resume GPU if it has been suspended by runtime PM */
ret = pm_runtime_get_sync(ldev->dev);
ret = pm_runtime_resume_and_get(ldev->dev);
if (ret < 0)
return ret;
......
......@@ -2083,13 +2083,11 @@ nouveau_bios_init(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct nvbios *bios = &drm->vbios;
struct pci_dev *pdev;
int ret;
/* only relevant for PCI devices */
if (!dev_is_pci(dev->dev))
return 0;
pdev = to_pci_dev(dev->dev);
if (!NVInitVBIOS(dev))
return -ENODEV;
......
......@@ -1026,7 +1026,6 @@ int vc4_queue_seqno_cb(struct drm_device *dev,
void (*func)(struct vc4_seqno_cb *cb))
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
int ret = 0;
unsigned long irqflags;
cb->func = func;
......@@ -1041,7 +1040,7 @@ int vc4_queue_seqno_cb(struct drm_device *dev,
}
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
return ret;
return 0;
}
/* Scheduled when any job has been completed, this walks the list of
......
......@@ -132,24 +132,57 @@ static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL);
}
#ifdef CONFIG_DRM_VC4_HDMI_CEC
static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
{
u16 clk_cnt;
u32 value;
value = HDMI_READ(HDMI_CEC_CNTRL_1);
value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
/*
* Set the clock divider: the hsm_clock rate and this divider
* setting will give a 40 kHz CEC clock.
*/
clk_cnt = clk_get_rate(vc4_hdmi->cec_clock) / CEC_CLOCK_FREQ;
value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
}
#else
static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
#endif
static enum drm_connector_status
vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
bool connected = false;
if (vc4_hdmi->hpd_gpio) {
if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^
vc4_hdmi->hpd_active_low)
return connector_status_connected;
cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
return connector_status_disconnected;
connected = true;
} else if (drm_probe_ddc(vc4_hdmi->ddc)) {
connected = true;
} else if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) {
connected = true;
}
if (drm_probe_ddc(vc4_hdmi->ddc))
return connector_status_connected;
if (connected) {
if (connector->status != connector_status_connected) {
struct edid *edid = drm_get_edid(connector, vc4_hdmi->ddc);
if (edid) {
cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
vc4_hdmi->encoder.hdmi_monitor = drm_detect_hdmi_monitor(edid);
kfree(edid);
}
}
if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
return connector_status_connected;
}
cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
return connector_status_disconnected;
}
......@@ -758,6 +791,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
return;
}
vc4_hdmi_cec_update_clk_div(vc4_hdmi);
/*
* FIXME: When the pixel freq is 594MHz (4k60), this needs to be setup
* at 300MHz.
......@@ -779,9 +814,6 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
return;
}
if (vc4_hdmi->variant->reset)
vc4_hdmi->variant->reset(vc4_hdmi);
if (vc4_hdmi->variant->phy_init)
vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state);
......@@ -1423,15 +1455,22 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
}
#ifdef CONFIG_DRM_VC4_HDMI_CEC
static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
if (vc4_hdmi->cec_irq_was_rx) {
if (vc4_hdmi->cec_rx_msg.len)
cec_received_msg(vc4_hdmi->cec_adap,
&vc4_hdmi->cec_rx_msg);
} else if (vc4_hdmi->cec_tx_ok) {
return IRQ_HANDLED;
}
static irqreturn_t vc4_cec_irq_handler_tx_thread(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
if (vc4_hdmi->cec_tx_ok) {
cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
0, 0, 0, 0);
} else {
......@@ -1445,15 +1484,35 @@ static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
return IRQ_HANDLED;
}
static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
irqreturn_t ret;
if (vc4_hdmi->cec_irq_was_rx)
ret = vc4_cec_irq_handler_rx_thread(irq, priv);
else
ret = vc4_cec_irq_handler_tx_thread(irq, priv);
return ret;
}
static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1)
{
struct drm_device *dev = vc4_hdmi->connector.dev;
struct cec_msg *msg = &vc4_hdmi->cec_rx_msg;
unsigned int i;
msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
if (msg->len > 16) {
drm_err(dev, "Attempting to read too much data (%d)\n", msg->len);
return;
}
for (i = 0; i < msg->len; i += 4) {
u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i);
u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + (i >> 2));
msg->msg[i] = val & 0xff;
msg->msg[i + 1] = (val >> 8) & 0xff;
......@@ -1462,33 +1521,57 @@ static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1)
}
}
static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS);
u32 cntrl1, cntrl5;
u32 cntrl1;
cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
return IRQ_WAKE_THREAD;
}
static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
u32 cntrl1;
if (!(stat & VC4_HDMI_CPU_CEC))
return IRQ_NONE;
vc4_hdmi->cec_rx_msg.len = 0;
cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
if (vc4_hdmi->cec_irq_was_rx) {
vc4_cec_read_msg(vc4_hdmi, cntrl1);
cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
} else {
vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
}
HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
return IRQ_WAKE_THREAD;
}
static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS);
irqreturn_t ret;
u32 cntrl5;
if (!(stat & VC4_HDMI_CPU_CEC))
return IRQ_NONE;
cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
if (vc4_hdmi->cec_irq_was_rx)
ret = vc4_cec_irq_handler_rx_bare(irq, priv);
else
ret = vc4_cec_irq_handler_tx_bare(irq, priv);
HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
return ret;
}
static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
......@@ -1523,8 +1606,10 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
if (!vc4_hdmi->variant->external_irq_controller)
HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
} else {
if (!vc4_hdmi->variant->external_irq_controller)
HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
......@@ -1546,11 +1631,17 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
struct drm_device *dev = vc4_hdmi->connector.dev;
u32 val;
unsigned int i;
if (msg->len > 16) {
drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len);
return -ENOMEM;
}
for (i = 0; i < msg->len; i += 4)
HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i,
HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i >> 2),
(msg->msg[i]) |
(msg->msg[i + 1] << 8) |
(msg->msg[i + 2] << 16) |
......@@ -1577,11 +1668,14 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
{
struct cec_connector_info conn_info;
struct platform_device *pdev = vc4_hdmi->pdev;
struct device *dev = &pdev->dev;
u32 value;
int ret;
if (!vc4_hdmi->variant->cec_available)
if (!of_find_property(dev->of_node, "interrupts", NULL)) {
dev_warn(dev, "'interrupts' DT property is missing, no CEC\n");
return 0;
}
vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
vc4_hdmi, "vc4",
......@@ -1594,23 +1688,39 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
value = HDMI_READ(HDMI_CEC_CNTRL_1);
value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
/*
* Set the logical address to Unregistered and set the clock
* divider: the hsm_clock rate and this divider setting will
* give a 40 kHz CEC clock.
*/
value |= VC4_HDMI_CEC_ADDR_MASK |
(4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
/* Set the logical address to Unregistered */
value |= VC4_HDMI_CEC_ADDR_MASK;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
vc4_hdmi_cec_update_clk_div(vc4_hdmi);
if (vc4_hdmi->variant->external_irq_controller) {
ret = devm_request_threaded_irq(&pdev->dev,
platform_get_irq_byname(pdev, "cec-rx"),
vc4_cec_irq_handler_rx_bare,
vc4_cec_irq_handler_rx_thread, 0,
"vc4 hdmi cec rx", vc4_hdmi);
if (ret)
goto err_delete_cec_adap;
ret = devm_request_threaded_irq(&pdev->dev,
platform_get_irq_byname(pdev, "cec-tx"),
vc4_cec_irq_handler_tx_bare,
vc4_cec_irq_handler_tx_thread, 0,
"vc4 hdmi cec tx", vc4_hdmi);
if (ret)
goto err_delete_cec_adap;
} else {
HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
vc4_cec_irq_handler,
vc4_cec_irq_handler_thread, 0,
"vc4 hdmi cec", vc4_hdmi);
if (ret)
goto err_delete_cec_adap;
}
ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
if (ret < 0)
......@@ -1710,6 +1820,7 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
return PTR_ERR(vc4_hdmi->hsm_clock);
}
vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock;
vc4_hdmi->cec_clock = vc4_hdmi->hsm_clock;
return 0;
}
......@@ -1803,6 +1914,12 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
return PTR_ERR(vc4_hdmi->audio_clock);
}
vc4_hdmi->cec_clock = devm_clk_get(dev, "cec");
if (IS_ERR(vc4_hdmi->cec_clock)) {
DRM_ERROR("Failed to get CEC clock\n");
return PTR_ERR(vc4_hdmi->cec_clock);
}
vc4_hdmi->reset = devm_reset_control_get(dev, NULL);
if (IS_ERR(vc4_hdmi->reset)) {
DRM_ERROR("Failed to get HDMI reset line\n");
......@@ -1875,6 +1992,9 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
vc4_hdmi->disable_wifi_frequencies =
of_property_read_bool(dev->of_node, "wifi-2.4ghz-coexistence");
if (vc4_hdmi->variant->reset)
vc4_hdmi->variant->reset(vc4_hdmi);
pm_runtime_enable(dev);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
......@@ -1970,7 +2090,6 @@ static const struct vc4_hdmi_variant bcm2835_variant = {
.debugfs_name = "hdmi_regs",
.card_name = "vc4-hdmi",
.max_pixel_clock = 162000000,
.cec_available = true,
.registers = vc4_hdmi_fields,
.num_registers = ARRAY_SIZE(vc4_hdmi_fields),
......@@ -1999,6 +2118,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
PHY_LANE_CK,
},
.unsupported_odd_h_timings = true,
.external_irq_controller = true,
.init_resources = vc5_hdmi_init_resources,
.csc_setup = vc5_hdmi_csc_setup,
......@@ -2025,6 +2145,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
PHY_LANE_2,
},
.unsupported_odd_h_timings = true,
.external_irq_controller = true,
.init_resources = vc5_hdmi_init_resources,
.csc_setup = vc5_hdmi_csc_setup,
......
......@@ -42,9 +42,6 @@ struct vc4_hdmi_variant {
/* Filename to expose the registers in debugfs */
const char *debugfs_name;
/* Set to true when the CEC support is available */
bool cec_available;
/* Maximum pixel clock supported by the controller (in Hz) */
unsigned long long max_pixel_clock;
......@@ -64,6 +61,13 @@ struct vc4_hdmi_variant {
/* The BCM2711 cannot deal with odd horizontal pixel timings */
bool unsupported_odd_h_timings;
/*
* The BCM2711 CEC/hotplug IRQ controller is shared between the
* two HDMI controllers, and we have a proper irqchip driver for
* it.
*/
bool external_irq_controller;
/* Callback to get the resources (memory region, interrupts,
* clocks, etc) for that variant.
*/
......@@ -155,6 +159,7 @@ struct vc4_hdmi {
bool cec_tx_ok;
bool cec_irq_was_rx;
struct clk *cec_clock;
struct clk *pixel_clock;
struct clk *hsm_clock;
struct clk *audio_clock;
......
......@@ -29,6 +29,7 @@ enum vc4_hdmi_field {
HDMI_CEC_CPU_MASK_SET,
HDMI_CEC_CPU_MASK_STATUS,
HDMI_CEC_CPU_STATUS,
HDMI_CEC_CPU_SET,
/*
* Transmit data, first byte is low byte of the 32-bit reg.
......@@ -199,9 +200,10 @@ static const struct vc4_hdmi_register __maybe_unused vc4_hdmi_fields[] = {
VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0),
VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4),
VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340),
VC4_HDMI_REG(HDMI_CEC_CPU_SET, 0x0344),
VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348),
VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c),
VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c),
VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x0350),
VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354),
VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400),
};
......
......@@ -163,6 +163,7 @@ int virtio_gpu_init(struct drm_device *dev)
vgdev->host_visible_region.len,
dev_name(&vgdev->vdev->dev))) {
DRM_ERROR("Could not reserve host visible region\n");
ret = -EBUSY;
goto err_vqs;
}
......
......@@ -668,9 +668,10 @@ static int vmw_setup_pci_resources(struct vmw_private *dev,
fifo_size,
MEMREMAP_WB);
if (unlikely(dev->fifo_mem == NULL)) {
if (IS_ERR(dev->fifo_mem)) {
DRM_ERROR("Failed mapping FIFO memory.\n");
return -ENOMEM;
pci_release_regions(pdev);
return PTR_ERR(dev->fifo_mem);
}
/*
......@@ -716,7 +717,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
return ret;
ret = vmw_detect_version(dev_priv);
if (ret)
return ret;
goto out_no_pci_or_version;
mutex_init(&dev_priv->cmdbuf_mutex);
mutex_init(&dev_priv->release_mutex);
......@@ -1013,7 +1014,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
vmw_irq_uninstall(&dev_priv->drm);
out_no_irq:
pci_release_regions(pdev);
ttm_object_device_release(&dev_priv->tdev);
out_err0:
for (i = vmw_res_context; i < vmw_res_max; ++i)
......@@ -1021,7 +1021,8 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
if (dev_priv->ctx.staged_bindings)
vmw_binding_state_free(dev_priv->ctx.staged_bindings);
kfree(dev_priv);
out_no_pci_or_version:
pci_release_regions(pdev);
return ret;
}
......@@ -1059,7 +1060,6 @@ static void vmw_driver_unload(struct drm_device *dev)
vmw_fence_manager_takedown(dev_priv->fman);
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
vmw_irq_uninstall(&dev_priv->drm);
pci_release_regions(pdev);
ttm_object_device_release(&dev_priv->tdev);
if (dev_priv->ctx.staged_bindings)
......@@ -1068,7 +1068,7 @@ static void vmw_driver_unload(struct drm_device *dev)
for (i = vmw_res_context; i < vmw_res_max; ++i)
idr_destroy(&dev_priv->res_idr[i]);
kfree(dev_priv);
pci_release_regions(pdev);
}
static void vmw_postclose(struct drm_device *dev,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment