Archived
14
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
linux-2.6/drivers/staging/gma500/mdfld_dsi_output.c
Paul Gortmaker 3f934e00a4 staging: Add moduleparam.h to drivers/staging files as required
These files are using moduleparam infrastructure and were getting
that via the implicit module.h presence that we are phasing out.
So they'll need to include moduleparam.h explicitly in advance.

Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
2011-10-31 19:32:09 -04:00

1015 lines
27 KiB
C

/*
* 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_output.h"
#include "mdfld_dsi_dbi.h"
#include "mdfld_dsi_dpi.h"
#include "mdfld_output.h"
#include <asm/intel_scu_ipc.h>
#include "mdfld_dsi_pkg_sender.h"
#include <linux/pm_runtime.h>
#include <linux/moduleparam.h>
#define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100
static int CABC_control = 1;
static int LABC_control = 1;
module_param (CABC_control, int, 0644);
module_param (LABC_control, int, 0644);
/**
* make these MCS command global
* we don't need 'movl' everytime we send them.
* FIXME: these datas were provided by OEM, we should get them from GCT.
**/
static u32 mdfld_dbi_mcs_hysteresis[] = {
0x42000f57, 0x8c006400, 0xff00bf00, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0x38000aff, 0x82005000, 0xff00ab00, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0x000000ff,
};
static u32 mdfld_dbi_mcs_display_profile[] = {
0x50281450, 0x0000c882, 0x00000000, 0x00000000,
0x00000000,
};
static u32 mdfld_dbi_mcs_kbbc_profile[] = {
0x00ffcc60, 0x00000000, 0x00000000, 0x00000000,
};
static u32 mdfld_dbi_mcs_gamma_profile[] = {
0x81111158, 0x88888888, 0x88888888,
};
/*
* write hysteresis values.
*/
static void mdfld_dsi_write_hysteresis (struct mdfld_dsi_config *dsi_config,
int pipe)
{
struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config);
if(!sender) {
WARN_ON(1);
return;
}
mdfld_dsi_send_mcs_long_hs(sender,
mdfld_dbi_mcs_hysteresis,
17,
MDFLD_DSI_SEND_PACKAGE);
}
/*
* write display profile values.
*/
static void mdfld_dsi_write_display_profile(struct mdfld_dsi_config *dsi_config, int pipe)
{
struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config);
if(!sender) {
WARN_ON(1);
return;
}
mdfld_dsi_send_mcs_long_hs(sender,
mdfld_dbi_mcs_display_profile,
5,
MDFLD_DSI_SEND_PACKAGE);
}
/*
* write KBBC profile values.
*/
static void mdfld_dsi_write_kbbc_profile (struct mdfld_dsi_config * dsi_config, int pipe)
{
struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config);
if(!sender) {
WARN_ON(1);
return;
}
mdfld_dsi_send_mcs_long_hs(sender,
mdfld_dbi_mcs_kbbc_profile,
4,
MDFLD_DSI_SEND_PACKAGE);
}
/*
* write gamma setting.
*/
static void mdfld_dsi_write_gamma_setting (struct mdfld_dsi_config *dsi_config, int pipe)
{
struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config);
if(!sender) {
WARN_ON(1);
return;
}
mdfld_dsi_send_mcs_long_hs(sender,
mdfld_dbi_mcs_gamma_profile,
3,
MDFLD_DSI_SEND_PACKAGE);
}
/*
* 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 = 0;
/* 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)
dev_err(dev->dev,
"mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x. \n",
gen_fifo_stat_reg);
}
/*
* Manage the DSI MIPI keyboard and display brightness.
* FIXME: this is exported to OSPM code. should work out an specific
* display interface to OSPM.
*/
void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe)
{
struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config);
struct drm_device *dev = sender->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
u32 gen_ctrl_val;
if(!sender) {
WARN_ON(1);
return;
}
/* Set default display backlight value to 85% (0xd8)*/
mdfld_dsi_send_mcs_short_hs(sender,
write_display_brightness,
0xd8,
1,
MDFLD_DSI_SEND_PACKAGE);
/* Set minimum brightness setting of CABC function to 20% (0x33)*/
mdfld_dsi_send_mcs_short_hs(sender,
write_cabc_min_bright,
0x33,
1,
MDFLD_DSI_SEND_PACKAGE);
mdfld_dsi_write_hysteresis(dsi_config, pipe);
mdfld_dsi_write_display_profile (dsi_config, pipe);
mdfld_dsi_write_kbbc_profile (dsi_config, pipe);
mdfld_dsi_write_gamma_setting (dsi_config, pipe);
/* Enable backlight or/and LABC */
gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON| BACKLIGHT_ON;
if (LABC_control == 1 || CABC_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_hs(sender,
write_ctrl_display,
(u8)gen_ctrl_val,
1,
MDFLD_DSI_SEND_PACKAGE);
if (CABC_control == 0)
return;
mdfld_dsi_send_mcs_short_hs(sender,
write_ctrl_cabc,
UI_IMAGE,
1,
MDFLD_DSI_SEND_PACKAGE);
}
/*
* Manage the mipi display brightness.
* TODO: refine this interface later
*/
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;
int p_type;
if (!dev || (pipe != 0 && pipe != 2)) {
dev_err(dev->dev, "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) {
WARN_ON(1);
return;
}
gen_ctrl_val = ((level * 0xff) / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
dev_dbg(dev->dev,
"pipe = %d, gen_ctrl_val = %d. \n", pipe, gen_ctrl_val);
if(p_type == TMD_VID || p_type == TMD_CMD){
/* Set display backlight value */
mdfld_dsi_send_mcs_short_hs(sender,
tmd_write_display_brightness,
(u8)gen_ctrl_val,
1,
MDFLD_DSI_SEND_PACKAGE);
} else {
/* Set display backlight value */
mdfld_dsi_send_mcs_short_hs(sender,
write_display_brightness,
(u8)gen_ctrl_val,
1,
MDFLD_DSI_SEND_PACKAGE);
/* Enable backlight control */
if (level == 0)
gen_ctrl_val = 0;
else
gen_ctrl_val = dev_priv->mipi_ctrl_display;
mdfld_dsi_send_mcs_short_hs(sender,
write_ctrl_display,
(u8)gen_ctrl_val,
1,
MDFLD_DSI_SEND_PACKAGE);
}
}
/*
* shut down DSI controller
*/
void mdfld_dsi_controller_shutdown(struct mdfld_dsi_config * dsi_config, int pipe)
{
struct drm_device * dev;
u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
int retry = 100;
if (!dsi_config) {
WARN_ON(1);
return;
}
dev = dsi_config->dev;
if (!gma_power_begin(dev, true)) {
dev_err(dev->dev, "hw begin failed\n");
return;
}
if(!(REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & DSI_DEVICE_READY))
goto shutdown_out;
/* Send shut down package, clean packet send bit first */
if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) {
REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset),
(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) | DSI_INTR_STATE_SPL_PKG_SENT));
}
/*send shut down package in HS*/
REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_SHUTDOWN);
/*
* make sure shut down is sent.
* FIXME: add max retry counter
*/
while(!(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)) {
retry--;
if(!retry) {
dev_err(dev->dev, "timeout\n");
break;
}
}
/*sleep 1 ms to ensure shutdown finished*/
msleep(100);
/*un-ready device*/
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset),
(REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & ~DSI_DEVICE_READY));
shutdown_out:
gma_power_end(dev);
}
void mdfld_dsi_controller_startup(struct mdfld_dsi_config * dsi_config, int pipe)
{
struct drm_device * dev;
u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
int retry = 100;
if (!dsi_config) {
WARN_ON(1);
return;
}
dev = dsi_config->dev;
dev_dbg(dev->dev, "starting up DSI controller on pipe %d...\n", pipe);
if (!gma_power_begin(dev, true)) {
dev_err(dev->dev, "hw begin failed\n");
return;
}
if((REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & DSI_DEVICE_READY))
goto startup_out;
/*if config DPI, turn on DPI interface*/
if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) {
if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) {
REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT);
}
REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_TURN_ON);
/*
* make sure shut down is sent.
* FIXME: add max retry counter
*/
while(!(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)) {
retry--;
if(!retry) {
dev_err(dev->dev, "timeout\n");
break;
}
}
msleep(100);
}
/*set device ready*/
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset),
(REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) | DSI_DEVICE_READY));
startup_out:
gma_power_end(dev);
}
static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
u8 dcs,
u32 *data,
u8 transmission)
{
struct mdfld_dsi_pkg_sender *sender
= mdfld_dsi_get_pkg_sender(dsi_config);
if (!sender || !data) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
if (transmission == MDFLD_DSI_HS_TRANSMISSION)
return mdfld_dsi_read_mcs_hs(sender, dcs, data, 1);
else if (transmission == MDFLD_DSI_LP_TRANSMISSION)
return mdfld_dsi_read_mcs_lp(sender, dcs, data, 1);
else
return -EINVAL;
}
int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config,
u32 *mode,
u8 transmission)
{
if (!dsi_config || !mode) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, transmission);
}
int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config,
u32 *result,
u8 transmission)
{
if (!dsi_config || !result) {
DRM_ERROR("Invalid parameter\n");
return -EINVAL;
}
return mdfld_dsi_get_panel_status(dsi_config, 0x0f, result,
transmission);
}
/*
* 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))) {
WARN_ON(1);
return;
}
if(dsi_config->type)
mdfld_dsi_dpi_controller_init(dsi_config, pipe);
else
mdfld_dsi_controller_dbi_init(dsi_config, pipe);
}
static void mdfld_dsi_connector_save(struct drm_connector * connector)
{
}
static void mdfld_dsi_connector_restore(struct drm_connector * connector)
{
}
static enum drm_connector_status mdfld_dsi_connector_detect(struct drm_connector * connector, bool force)
{
struct psb_intel_output *psb_output
= to_psb_intel_output(connector);
struct mdfld_dsi_connector *dsi_connector
= MDFLD_DSI_CONNECTOR(psb_output);
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 psb_intel_crtc * psb_crtc = to_psb_intel_crtc(encoder->crtc);
bool bTransitionFromToCentered;
uint64_t curValue;
if (!psb_crtc)
goto set_prop_error;
switch (value) {
case DRM_MODE_SCALE_FULLSCREEN:
break;
case DRM_MODE_SCALE_NO_SCALE:
break;
case DRM_MODE_SCALE_ASPECT:
break;
default:
goto set_prop_error;
}
if (drm_connector_property_get_value(connector, property, &curValue))
goto set_prop_error;
if (curValue == value)
goto set_prop_done;
if (drm_connector_property_set_value(connector, property, value))
goto set_prop_error;
bTransitionFromToCentered = (curValue == DRM_MODE_SCALE_NO_SCALE) ||
(value == DRM_MODE_SCALE_NO_SCALE);
if (psb_crtc->saved_mode.hdisplay != 0 &&
psb_crtc->saved_mode.vdisplay != 0) {
if (bTransitionFromToCentered) {
if (!drm_crtc_helper_set_mode(encoder->crtc, &psb_crtc->saved_mode,
encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb))
goto set_prop_error;
} else {
struct drm_encoder_helper_funcs *pEncHFuncs = encoder->helper_private;
pEncHFuncs->mode_set(encoder, &psb_crtc->saved_mode,
&psb_crtc->saved_adjusted_mode);
}
}
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
} else if (!strcmp(property->name, "backlight") && encoder) {
struct drm_psb_private *dev_priv = encoder->dev->dev_private;
struct backlight_device *psb_bd = dev_priv->backlight_device;
dev_dbg(encoder->dev->dev, "backlight level = %d\n", (int)value);
if (drm_connector_property_set_value(connector, property, value))
goto set_prop_error;
else {
dev_dbg(encoder->dev->dev,
"set brightness to %d", (int)value);
if (psb_bd) {
psb_bd->props.brightness = value;
backlight_update_status(psb_bd);
}
}
#endif
}
set_prop_done:
return 0;
set_prop_error:
return -1;
}
static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
{
struct psb_intel_output * psb_output = to_psb_intel_output(connector);
struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output);
struct mdfld_dsi_pkg_sender * sender;
if(!dsi_connector)
return;
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
sender = dsi_connector->pkg_sender;
mdfld_dsi_pkg_sender_destroy(sender);
kfree(dsi_connector);
}
static int mdfld_dsi_connector_get_modes(struct drm_connector * connector)
{
struct psb_intel_output * psb_output = to_psb_intel_output(connector);
struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output);
struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector);
struct drm_display_mode * fixed_mode = dsi_config->fixed_mode;
struct drm_display_mode * dup_mode = NULL;
struct drm_device * dev = connector->dev;
connector->display_info.min_vfreq = 0;
connector->display_info.max_vfreq = 200;
connector->display_info.min_hfreq = 0;
connector->display_info.max_hfreq = 200;
if(fixed_mode) {
dev_dbg(dev->dev, "fixed_mode %dx%d\n",
fixed_mode->hdisplay, fixed_mode->vdisplay);
dup_mode = drm_mode_duplicate(dev, fixed_mode);
drm_mode_probed_add(connector, dup_mode);
return 1;
}
dev_err(dev->dev, "Didn't get any modes!\n");
return 0;
}
static int mdfld_dsi_connector_mode_valid(struct drm_connector * connector, struct drm_display_mode * mode)
{
struct psb_intel_output * psb_output = to_psb_intel_output(connector);
struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output);
struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector);
struct drm_display_mode * fixed_mode = dsi_config->fixed_mode;
dev_dbg(connector->dev->dev, "mode %p, fixed mode %p\n",
mode, fixed_mode);
if(mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if(mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
/**
* FIXME: current DC has no fitting unit, reject any mode setting request
* will figure out a way to do up-scaling(pannel fitting) later.
**/
if(fixed_mode) {
if(mode->hdisplay != fixed_mode->hdisplay)
return MODE_PANEL;
if(mode->vdisplay != fixed_mode->vdisplay)
return MODE_PANEL;
}
dev_dbg(connector->dev->dev, "mode ok\n");
return MODE_OK;
}
static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
{
#ifdef CONFIG_PM_RUNTIME
struct drm_device * dev = connector->dev;
struct drm_psb_private * dev_priv = dev->dev_private;
bool panel_on, panel_on2;
#endif
/* First, execute DPMS */
drm_helper_connector_dpms(connector, mode);
#ifdef CONFIG_PM_RUNTIME
if(mdfld_panel_dpi(dev)) {
/* DPI panel */
panel_on = dev_priv->dpi_panel_on;
panel_on2 = dev_priv->dpi_panel_on2;
} else {
/* DBI panel */
panel_on = dev_priv->dbi_panel_on;
panel_on2 = dev_priv->dbi_panel_on2;
}
/* Then check all display panels + monitors status */
/* Make sure that the Display (B) sub-system status isn't i3 when
* R/W the DC register, otherwise "Fabric error" issue would occur
* during S0i3 state. */
if(!panel_on && !panel_on2 && !(REG_READ(HDMIB_CONTROL)
& HDMIB_PORT_EN)) {
/* Request rpm idle */
if(dev_priv->rpm_enabled)
pm_request_idle(&dev->pdev->dev);
}
/*
* if rpm wasn't enabled yet, try to allow it
* FIXME: won't enable rpm for DPI since DPI
* CRTC setting is a little messy now.
* Enable it later!
*/
#if 0
if(!dev_priv->rpm_enabled && !mdfld_panel_dpi(dev))
ospm_runtime_pm_allow(dev);
#endif
#endif
}
static struct drm_encoder *mdfld_dsi_connector_best_encoder(
struct drm_connector *connector)
{
struct psb_intel_output * psb_output = to_psb_intel_output(connector);
struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output);
struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector);
struct mdfld_dsi_encoder * encoder = NULL;
if(dsi_config->type == MDFLD_DSI_ENCODER_DBI)
encoder = dsi_config->encoders[MDFLD_DSI_ENCODER_DBI];
else if (dsi_config->type == MDFLD_DSI_ENCODER_DPI)
encoder = dsi_config->encoders[MDFLD_DSI_ENCODER_DPI];
dev_dbg(connector->dev->dev, "get encoder %p\n", encoder);
if(!encoder) {
dev_err(connector->dev->dev,
"Invalid encoder for type %d\n", dsi_config->type);
return NULL;
}
dsi_config->encoder = encoder;
return &encoder->base;
}
/* DSI connector funcs */
static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
.dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
.save = mdfld_dsi_connector_save,
.restore = mdfld_dsi_connector_restore,
.detect = mdfld_dsi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = mdfld_dsi_connector_set_property,
.destroy = mdfld_dsi_connector_destroy,
};
/* DSI connector helper funcs */
static const struct drm_connector_helper_funcs mdfld_dsi_connector_helper_funcs = {
.get_modes = mdfld_dsi_connector_get_modes,
.mode_valid = mdfld_dsi_connector_mode_valid,
.best_encoder = mdfld_dsi_connector_best_encoder,
};
static int mdfld_dsi_get_default_config(struct drm_device * dev,
struct mdfld_dsi_config * config, int pipe)
{
if(!dev || !config) {
WARN_ON(1);
return -EINVAL;
}
config->bpp = 24;
config->type = mdfld_panel_dpi(dev);
config->lane_count = 2;
config->channel_num = 0;
/*NOTE: video mode is ignored when type is MDFLD_DSI_ENCODER_DBI*/
if (mdfld_get_panel_type(dev, pipe) == TMD_VID) {
config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
} else {
config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
}
return 0;
}
/*
* Returns the panel fixed mode from configuration.
*/
struct drm_display_mode *
mdfld_dsi_get_configuration_mode(struct mdfld_dsi_config * dsi_config, int pipe)
{
struct drm_device *dev = dsi_config->dev;
struct drm_display_mode *mode;
struct drm_psb_private *dev_priv = dev->dev_private;
struct mrst_timing_info *ti = &dev_priv->gct_data.DTD;
bool use_gct = false;
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode) {
dev_err(dev->dev, "Out of memory for mode\n");
return NULL;
}
if (use_gct) {
dev_dbg(dev->dev, "gct find MIPI panel.\n");
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;
} else {
if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) {
if (mdfld_get_panel_type(dev, pipe) == TMD_VID) {
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;
} else {
mode->hdisplay = 864;
mode->vdisplay = 480;
mode->hsync_start = 873;
mode->hsync_end = 876;
mode->htotal = 887;
mode->vsync_start = 487;
mode->vsync_end = 490;
mode->vtotal = 499;
mode->clock = 33264;
}
} else if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) {
mode->hdisplay = 864;
mode->vdisplay = 480;
mode->hsync_start = 872;
mode->hsync_end = 876;
mode->htotal = 884;
mode->vsync_start = 482;
mode->vsync_end = 494;
mode->vtotal = 486;
mode->clock = 25777;
}
}
drm_mode_set_name(mode);
drm_mode_set_crtcinfo(mode, 0);
mode->type |= DRM_MODE_TYPE_PREFERRED;
return mode;
}
int mdfld_dsi_panel_reset(int pipe)
{
unsigned gpio;
int ret = 0;
switch (pipe) {
case 0:
gpio = 128;
break;
case 2:
gpio = 34;
break;
default:
DRM_ERROR("Invalid output\n");
return -EINVAL;
}
ret = gpio_request(gpio, "gfx");
if (ret) {
DRM_ERROR("gpio_rqueset failed\n");
return ret;
}
ret = gpio_direction_output(gpio, 1);
if (ret) {
DRM_ERROR("gpio_direction_output failed\n");
goto gpio_error;
}
gpio_get_value(128);
gpio_error:
if (gpio_is_valid(gpio))
gpio_free(gpio);
return ret;
}
/*
* MIPI output init
* @dev drm device
* @pipe pipe number. 0 or 2
* @config
*
* Do the initialization of a MIPI output, including create DRM mode objects
* initialization of DSI output on @pipe
*/
void mdfld_dsi_output_init(struct drm_device *dev,
int pipe,
struct mdfld_dsi_config *config,
struct panel_funcs* p_cmd_funcs,
struct panel_funcs* p_vid_funcs)
{
struct mdfld_dsi_config * dsi_config;
struct mdfld_dsi_connector * dsi_connector;
struct psb_intel_output * psb_output;
struct drm_connector * connector;
struct mdfld_dsi_encoder * encoder;
struct drm_psb_private * dev_priv = dev->dev_private;
struct panel_info dsi_panel_info;
u32 width_mm, height_mm;
dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
if(!dev || ((pipe != 0) && (pipe != 2))) {
WARN_ON(1);
return;
}
/*create a new connetor*/
dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
if(!dsi_connector) {
DRM_ERROR("No memory");
return;
}
dsi_connector->pipe = pipe;
/*set DSI config*/
if(config) {
dsi_config = config;
} else {
dsi_config = kzalloc(sizeof(struct mdfld_dsi_config), GFP_KERNEL);
if(!dsi_config) {
dev_err(dev->dev,
"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;
/* Init fixed mode basing on DSI config type */
if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) {
dsi_config->fixed_mode = p_cmd_funcs->get_config_mode(dev);
if(p_cmd_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
goto dsi_init_err0;
} else if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) {
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) {
dev_err(dev->dev, "No pannel fixed mode was found\n");
goto dsi_init_err0;
}
if(pipe && dev_priv->dsi_configs[0]) {
dsi_config->dvr_ic_inited = 0;
dev_priv->dsi_configs[1] = dsi_config;
} else if(pipe == 0) {
dsi_config->dvr_ic_inited = 1;
dev_priv->dsi_configs[0] = dsi_config;
} else {
dev_err(dev->dev, "Trying to init MIPI1 before MIPI0\n");
goto dsi_init_err0;
}
/*init drm connector object*/
psb_output = &dsi_connector->base;
psb_output->type = (pipe == 0) ? INTEL_OUTPUT_MIPI : INTEL_OUTPUT_MIPI2;
connector = &psb_output->base;
/* Revisit type if MIPI/HDMI bridges ever appear on Medfield */
drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
connector->display_info.width_mm = width_mm;
connector->display_info.height_mm = height_mm;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
/* Attach properties */
drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN);
drm_connector_attach_property(connector, dev_priv->backlight_property, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
/* Init DSI package sender on this output */
if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
DRM_ERROR("Package Sender initialization failed on pipe %d\n", pipe);
goto dsi_init_err0;
}
/* Init DBI & DPI encoders */
if (p_cmd_funcs) {
encoder = mdfld_dsi_dbi_init(dev, dsi_connector, p_cmd_funcs);
if(!encoder) {
dev_err(dev->dev, "Create DBI encoder failed\n");
goto dsi_init_err1;
}
encoder->private = dsi_config;
dsi_config->encoders[MDFLD_DSI_ENCODER_DBI] = encoder;
}
if(p_vid_funcs) {
encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
if(!encoder) {
dev_err(dev->dev, "Create DPI encoder failed\n");
goto dsi_init_err1;
}
encoder->private = dsi_config;
dsi_config->encoders[MDFLD_DSI_ENCODER_DPI] = encoder;
}
drm_sysfs_connector_add(connector);
return;
/*TODO: add code to destroy outputs on error*/
dsi_init_err1:
/*destroy sender*/
mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
drm_connector_cleanup(connector);
kfree(dsi_config->fixed_mode);
kfree(dsi_config);
dsi_init_err0:
kfree(dsi_connector);
}