blob: 15055c85c1d47bf6a710440ae8f8e8c177e38732 [file] [log] [blame]
/*
* 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_dbi.h"
#include "mdfld_dsi_dbi_dpu.h"
#include "mdfld_dsi_pkg_sender.h"
#include "psb_powermgmt.h"
#include <linux/pm_runtime.h>
int enable_gfx_rtpm;
extern struct drm_device *gpDrmDevice;
extern int gfxrtdelay;
int enter_dsr;
struct mdfld_dsi_dbi_output *gdbi_output;
extern bool gbgfxsuspended;
extern int gfxrtdelay;
#ifdef CONFIG_GFX_RTPM
static void psb_runtimepm_wq_handler(struct work_struct *work);
DECLARE_DELAYED_WORK(rtpm_work, psb_runtimepm_wq_handler);
void psb_runtimepm_wq_handler(struct work_struct *work)
{
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
if (drm_psb_ospm && !enable_gfx_rtpm) {
pr_info("Enable GFX runtime_pm\n");
dev_priv->rpm_enabled = 1;
enable_gfx_rtpm = 1;
pm_runtime_enable(&gpDrmDevice->pdev->dev);
pm_runtime_set_active(&gpDrmDevice->pdev->dev);
pm_runtime_allow(&gpDrmDevice->pdev->dev);
}
}
#endif
/*
* set refreshing area
*/
int mdfld_dsi_dbi_update_area(struct mdfld_dsi_dbi_output *dbi_output,
u16 x1, u16 y1, u16 x2, u16 y2)
{
struct mdfld_dsi_pkg_sender *sender =
mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base);
u8 param[4];
u8 cmd;
int err;
if (!sender) {
WARN_ON(1);
return -EINVAL;
}
/*set column*/
cmd = set_column_address;
param[0] = x1 >> 8;
param[1] = x1;
param[2] = x2 >> 8;
param[3] = x2;
err = mdfld_dsi_send_dcs(sender,
cmd,
param,
4,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd);
goto err_out;
}
/*set page*/
cmd = set_page_addr;
param[0] = y1 >> 8;
param[1] = y1;
param[2] = y2 >> 8;
param[3] = y2;
err = mdfld_dsi_send_dcs(sender,
cmd,
param,
4,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd);
goto err_out;
}
/*update screen*/
err = mdfld_dsi_send_dcs(sender,
write_mem_start,
NULL,
0,
CMD_DATA_SRC_PIPE,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd);
goto err_out;
}
mdfld_dsi_cmds_kick_out(sender);
err_out:
return err;
}
/*
* set panel's power state
*/
int mdfld_dsi_dbi_update_power(struct mdfld_dsi_dbi_output *dbi_output,
int mode)
{
struct drm_device *dev = dbi_output->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dsi_pkg_sender *sender =
mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base);
u8 param = 0;
u32 err = 0;
if (!dev_priv->dispstatus && mode != DRM_MODE_DPMS_ON) {
dev_err(dev->dev, "%s: already OFF ignoring\n", __func__);
return 0;
}
if (dev_priv->dispstatus && mode == DRM_MODE_DPMS_ON) {
dev_err(dev->dev, "%s: already ON ignoring\n", __func__);
return 0;
}
if (!sender) {
WARN_ON(1);
return -EINVAL;
}
if (mode == DRM_MODE_DPMS_ON) {
/*exit sleep mode*/
err = mdfld_dsi_send_dcs(sender,
exit_sleep_mode,
NULL,
0,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
exit_sleep_mode);
goto power_err;
}
/*set display on*/
err = mdfld_dsi_send_dcs(sender,
set_display_on,
NULL,
0,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
set_display_on);
goto power_err;
}
/* set tear effect on */
err = mdfld_dsi_send_dcs(sender,
set_tear_on,
&param,
1,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
set_tear_on);
goto power_err;
}
/**
* FIXME: remove this later
*/
err = mdfld_dsi_send_dcs(sender,
write_mem_start,
NULL,
0,
CMD_DATA_SRC_PIPE,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
set_display_on);
goto power_err;
}
} else {
/*set tear effect off */
err = mdfld_dsi_send_dcs(sender,
set_tear_off,
NULL,
0,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
set_tear_off);
goto power_err;
}
/*set display off*/
err = mdfld_dsi_send_dcs(sender,
set_display_off,
NULL,
0,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
set_display_off);
goto power_err;
}
/*enter sleep mode*/
err = mdfld_dsi_send_dcs(sender,
enter_sleep_mode,
NULL,
0,
CMD_DATA_SRC_SYSTEM_MEM,
MDFLD_DSI_QUEUE_PACKAGE);
if (err) {
dev_err(dev->dev, "DCS 0x%x sent failed\n",
enter_sleep_mode);
goto power_err;
}
}
mdfld_dsi_cmds_kick_out(sender);
power_err:
return err;
}
/*
* send a generic DCS command with a parameter list
*/
int mdfld_dsi_dbi_send_dcs(struct mdfld_dsi_dbi_output *dbi_output,
u8 dcs, u8 *param, u32 num, u8 data_src)
{
struct mdfld_dsi_pkg_sender *sender =
mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base);
int ret;
if (!sender) {
WARN_ON(1);
return -EINVAL;
}
ret = mdfld_dsi_send_dcs(sender,
dcs,
param,
num,
data_src,
MDFLD_DSI_SEND_PACKAGE);
return ret;
}
/*
* Enter DSR
*/
void mdfld_dsi_dbi_enter_dsr(struct mdfld_dsi_dbi_output *dbi_output, int pipe)
{
u32 reg_val;
struct drm_device *dev = dbi_output->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dbi_output->base.base.crtc;
struct psb_intel_crtc *psb_crtc = (crtc) ?
to_psb_intel_crtc(crtc) : NULL;
u32 dpll_reg = MRST_DPLL_A;
u32 pipeconf_reg = PIPEACONF;
u32 dspcntr_reg = DSPACNTR;
dev_priv->is_in_idle = true;
if (!dbi_output)
return;
gdbi_output = dbi_output;
if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) ||
(psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING))
return;
if (pipe == 2) {
dpll_reg = MRST_DPLL_A;
pipeconf_reg = PIPECCONF;
dspcntr_reg = DSPCCNTR;
}
if (!gma_power_begin(dev, true)) {
dev_err(dev->dev, "hw begin failed\n");
return;
}
/*disable te interrupts. */
mdfld_disable_te(dev, pipe);
/*disable plane*/
reg_val = REG_READ(dspcntr_reg);
if (!(reg_val & DISPLAY_PLANE_ENABLE)) {
REG_WRITE(dspcntr_reg, reg_val & ~DISPLAY_PLANE_ENABLE);
REG_READ(dspcntr_reg);
}
/*disable pipe*/
reg_val = REG_READ(pipeconf_reg);
if (!(reg_val & DISPLAY_PLANE_ENABLE)) {
reg_val &= ~DISPLAY_PLANE_ENABLE;
reg_val |= (PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF);
REG_WRITE(pipeconf_reg, reg_val);
REG_READ(pipeconf_reg);
mdfldWaitForPipeDisable(dev, pipe);
}
/*disable DPLL*/
reg_val = REG_READ(dpll_reg);
if (!(reg_val & DPLL_VCO_ENABLE)) {
reg_val &= ~DPLL_VCO_ENABLE;
REG_WRITE(dpll_reg, reg_val);
REG_READ(dpll_reg);
udelay(500);
}
gma_power_end(dev);
dbi_output->mode_flags |= MODE_SETTING_IN_DSR;
if (pipe == 2) {
enter_dsr = 1;
/* pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay); */
}
}
#ifndef CONFIG_MDFLD_DSI_DPU
static void mdfld_dbi_output_exit_dsr(struct mdfld_dsi_dbi_output *dbi_output,
int pipe, void *p_surfaceAddr, bool check_hw_on_only)
{
struct drm_device *dev = dbi_output->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dbi_output->base.base.crtc;
struct psb_intel_crtc *psb_crtc = (crtc) ?
to_psb_intel_crtc(crtc) : NULL;
u32 reg_val;
u32 dpll_reg = MRST_DPLL_A;
u32 pipeconf_reg = PIPEACONF;
u32 dspcntr_reg = DSPACNTR;
u32 dspsurf_reg = DSPASURF;
u32 reg_offset = 0;
/*if mode setting on-going, back off*/
if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) ||
(psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING))
return;
if (pipe == 2) {
dpll_reg = MRST_DPLL_A;
pipeconf_reg = PIPECCONF;
dspcntr_reg = DSPCCNTR;
dspsurf_reg = DSPCSURF;
reg_offset = MIPIC_REG_OFFSET;
}
if (check_hw_on_only) {
if (0/* FIXME!ospm_power_is_hw_on(_DISPLAY_ISLAND)*/) {
dev_err(dev->dev, "hw begin failed\n");
return;
}
} else if (!gma_power_begin(dev, true)) {
dev_err(dev->dev, "hw begin failed\n");
return;
}
/*enable DPLL*/
reg_val = REG_READ(dpll_reg);
if (!(reg_val & DPLL_VCO_ENABLE)) {
if (reg_val & MDFLD_PWR_GATE_EN) {
reg_val &= ~MDFLD_PWR_GATE_EN;
REG_WRITE(dpll_reg, reg_val);
REG_READ(dpll_reg);
udelay(500);
}
reg_val |= DPLL_VCO_ENABLE;
REG_WRITE(dpll_reg, reg_val);
REG_READ(dpll_reg);
udelay(500);
/* Add timeout */
while (!(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK))
cpu_relax();
}
/*enable pipe*/
reg_val = REG_READ(pipeconf_reg);
if (!(reg_val & PIPEACONF_ENABLE)) {
reg_val |= PIPEACONF_ENABLE;
REG_WRITE(pipeconf_reg, reg_val);
REG_READ(pipeconf_reg);
udelay(500);
mdfldWaitForPipeEnable(dev, pipe);
}
/*enable plane*/
reg_val = REG_READ(dspcntr_reg);
if (!(reg_val & DISPLAY_PLANE_ENABLE)) {
reg_val |= DISPLAY_PLANE_ENABLE;
REG_WRITE(dspcntr_reg, reg_val);
REG_READ(dspcntr_reg);
udelay(500);
}
/* update the surface base address. */
if (p_surfaceAddr)
REG_WRITE(dspsurf_reg, *((u32 *)p_surfaceAddr));
if (!check_hw_on_only)
gma_power_end(dev);
/*enable TE interrupt on this pipe*/
mdfld_enable_te(dev, pipe);
/*clean IN_DSR flag*/
dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR;
}
/*
* Exit from DSR
*/
void mdfld_dsi_dbi_exit_dsr(struct drm_device *dev, u32 update_src,
void *p_surfaceAddr, bool check_hw_on_only)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info;
struct mdfld_dsi_dbi_output **dbi_output;
int i;
dev_priv->is_in_idle = false;
dbi_output = dsr_info->dbi_outputs;
#ifdef CONFIG_PM_RUNTIME
if (!enable_gfx_rtpm) {
/* pm_runtime_allow(&gpDrmDevice->pdev->dev); */
/* schedule_delayed_work(&rtpm_work, 120 * 1000); */
}
#endif
/*for each output, exit dsr*/
for (i = 0; i < dsr_info->dbi_output_num; i++) {
/*if panel has been turned off, skip*/
if (!dbi_output[i]->dbi_panel_on)
continue;
if (dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR) {
enter_dsr = 0;
mdfld_dbi_output_exit_dsr(dbi_output[i], dbi_output[i]->channel_num ? 2 : 0, p_surfaceAddr, check_hw_on_only);
}
}
dev_priv->dsr_fb_update |= update_src;
}
static bool mdfld_dbi_is_in_dsr(struct drm_device *dev)
{
if (REG_READ(MRST_DPLL_A) & DPLL_VCO_ENABLE)
return false;
if ((REG_READ(PIPEACONF) & PIPEACONF_ENABLE) ||
(REG_READ(PIPECCONF) & PIPEACONF_ENABLE))
return false;
if ((REG_READ(DSPACNTR) & DISPLAY_PLANE_ENABLE) ||
(REG_READ(DSPCCNTR) & DISPLAY_PLANE_ENABLE))
return false;
return true;
}
/* Perodically update dbi panel */
void mdfld_dbi_update_panel(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info;
struct mdfld_dsi_dbi_output **dbi_outputs;
struct mdfld_dsi_dbi_output *dbi_output;
int i;
int enter_dsr = 0;
u32 damage_mask = 0;
dbi_outputs = dsr_info->dbi_outputs;
dbi_output = pipe ? dbi_outputs[1] : dbi_outputs[0];
if (!dbi_output)
return;
if (pipe == 0)
damage_mask = dev_priv->dsr_fb_update & (MDFLD_DSR_DAMAGE_MASK_0);
else if (pipe == 2)
damage_mask = dev_priv->dsr_fb_update & (MDFLD_DSR_DAMAGE_MASK_2);
else
return;
/*if FB is damaged and panel is on update on-panel FB*/
if (damage_mask && dbi_output->dbi_panel_on) {
dbi_output->dsr_fb_update_done = false;
if (dbi_output->p_funcs->update_fb)
dbi_output->p_funcs->update_fb(dbi_output, pipe);
if (dev_priv->dsr_enable && dbi_output->dsr_fb_update_done)
dev_priv->dsr_fb_update &= ~damage_mask;
/*clean IN_DSR flag*/
dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR;
dbi_output->dsr_idle_count = 0;
} else {
dbi_output->dsr_idle_count++;
}
/*try to enter DSR*/
if (dbi_outputs[0]->dsr_idle_count > 1
&& dbi_outputs[1]->dsr_idle_count > 1) {
for (i = 0; i < dsr_info->dbi_output_num; i++) {
if (!mdfld_dbi_is_in_dsr(dev) &&
!(dbi_outputs[i]->mode_flags & MODE_SETTING_ON_GOING)) {
mdfld_dsi_dbi_enter_dsr(dbi_outputs[i],
dbi_outputs[i]->channel_num ? 2 : 0);
#if 0
enter_dsr = 1;
pr_err("%s: enter_dsr = 1\n", __func__);
#endif
}
}
/*schedule rpm suspend after gfxrtdelay*/
#ifdef CONFIG_GFX_RTPM
if (!dev_priv->rpm_enabled
|| !enter_dsr
/* || (REG_READ(HDMIB_CONTROL) & HDMIB_PORT_EN) */
|| pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay))
dev_warn(dev->dev,
"Runtime PM schedule suspend failed, rpm %d\n",
dev_priv->rpm_enabled);
#endif
}
}
/*timers for DSR*/
static void mdfld_dsi_dbi_dsr_timer_func(unsigned long data)
{
struct drm_device *dev = (struct drm_device *)data;
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info;
struct timer_list *dsr_timer = &dsr_info->dsr_timer;
unsigned long flags;
mdfld_dbi_update_panel(dev, 0);
if (dsr_info->dsr_idle_count > 1)
return;
spin_lock_irqsave(&dsr_info->dsr_timer_lock, flags);
if (!timer_pending(dsr_timer)) {
dsr_timer->expires = jiffies + MDFLD_DSR_DELAY;
add_timer(dsr_timer);
}
spin_unlock_irqrestore(&dsr_info->dsr_timer_lock, flags);
}
static int mdfld_dsi_dbi_dsr_timer_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info;
struct timer_list *dsr_timer = &dsr_info->dsr_timer;
unsigned long flags;
spin_lock_init(&dsr_info->dsr_timer_lock);
spin_lock_irqsave(&dsr_info->dsr_timer_lock, flags);
init_timer(dsr_timer);
dsr_timer->data = (unsigned long)dev;
dsr_timer->function = mdfld_dsi_dbi_dsr_timer_func;
dsr_timer->expires = jiffies + MDFLD_DSR_DELAY;
spin_unlock_irqrestore(&dsr_info->dsr_timer_lock, flags);
return 0;
}
void mdfld_dbi_dsr_timer_start(struct mdfld_dbi_dsr_info *dsr_info)
{
struct timer_list *dsr_timer = &dsr_info->dsr_timer;
unsigned long flags;
spin_lock_irqsave(&dsr_info->dsr_timer_lock, flags);
if (!timer_pending(dsr_timer)) {
dsr_timer->expires = jiffies + MDFLD_DSR_DELAY;
add_timer(dsr_timer);
}
spin_unlock_irqrestore(&dsr_info->dsr_timer_lock, flags);
}
int mdfld_dbi_dsr_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info;
if (!dsr_info || IS_ERR(dsr_info)) {
dsr_info = kzalloc(sizeof(struct mdfld_dbi_dsr_info),
GFP_KERNEL);
if (!dsr_info) {
dev_err(dev->dev, "No memory\n");
return -ENOMEM;
}
dev_priv->dbi_dsr_info = dsr_info;
}
return 0;
}
void mdfld_dbi_dsr_exit(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info;
if (!dsr_info) {
del_timer_sync(&dsr_info->dsr_timer);
kfree(dsr_info);
dev_priv->dbi_dsr_info = NULL;
}
}
#endif
void mdfld_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config,
int pipe)
{
struct drm_device *dev = dsi_config->dev;
u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
int lane_count = dsi_config->lane_count;
u32 val = 0;
dev_dbg(dev->dev, "Init DBI interface on pipe %d...\n", pipe);
/*un-ready device*/
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000);
/*init dsi adapter before kicking off*/
REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018);
/*TODO: figure out how to setup these registers*/
REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408);
REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset),
0x000a0014);
REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400);
REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000001);
REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000);
/*enable all interrupts*/
REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff);
/*max value: 20 clock cycles of txclkesc*/
REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f);
/*min 21 txclkesc, max: ffffh*/
REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff);
/*min: 7d0 max: 4e20*/
REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0);
/*set up func_prg*/
val |= lane_count;
val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET);
val |= DSI_DBI_COLOR_FORMAT_OPTION2;
REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val);
REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff);
REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff);
/*de-assert dbi_stall when half of DBI FIFO is empty*/
/* REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000000); */
REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46);
REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000);
REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004);
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001);
}
#if 0
/*DBI encoder helper funcs*/
static const struct drm_encoder_helper_funcs mdfld_dsi_dbi_helper_funcs = {
.dpms = mdfld_dsi_dbi_dpms,
.mode_fixup = mdfld_dsi_dbi_mode_fixup,
.prepare = mdfld_dsi_dbi_prepare,
.mode_set = mdfld_dsi_dbi_mode_set,
.commit = mdfld_dsi_dbi_commit,
};
/*DBI encoder funcs*/
static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
#endif
static int mdfld_dbi_panel_reset(struct mdfld_dsi_dbi_output *output)
{
unsigned gpio;
int ret;
switch (output->channel_num) {
case 0:
gpio = 128;
break;
case 1:
gpio = 34;
break;
default:
pr_err("Invalid output\n");
return -EINVAL;
}
ret = gpio_request(gpio, "gfx");
if (ret) {
pr_err("gpio_rqueset failed\n");
return ret;
}
ret = gpio_direction_output(gpio, 1);
if (ret) {
pr_err("gpio_direction_output failed\n");
goto gpio_error;
}
gpio_get_value(128);
gpio_error:
if (gpio_is_valid(gpio))
gpio_free(gpio);
return ret;
}
/*
* Init DSI DBI encoder.
* Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector
* return pointer of newly allocated DBI encoder, NULL on error
*/
struct mdfld_dsi_encoder *mdfld_dsi_dbi_init(struct drm_device *dev,
struct mdfld_dsi_connector *dsi_connector,
struct panel_funcs *p_funcs)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dsi_dbi_output *dbi_output = NULL;
struct mdfld_dsi_config *dsi_config;
struct drm_connector *connector = NULL;
struct drm_encoder *encoder = NULL;
struct drm_display_mode *fixed_mode = NULL;
struct psb_gtt *pg = dev_priv ? (dev_priv->pg) : NULL;
#ifdef CONFIG_MDFLD_DSI_DPU
struct mdfld_dbi_dpu_info *dpu_info = dev_priv ? (dev_priv->dbi_dpu_info) : NULL;
#else
struct mdfld_dbi_dsr_info *dsr_info = dev_priv ? (dev_priv->dbi_dsr_info) : NULL;
#endif
int ret;
if (!pg || !dsi_connector) {
WARN_ON(1);
return NULL;
}
dbi_output = kzalloc(sizeof(struct mdfld_dsi_dbi_output), GFP_KERNEL);
if (!dbi_output) {
dev_err(dev->dev, "No memory\n");
return NULL;
}
if (dsi_connector->pipe == 0) {
dbi_output->channel_num = 0;
dev_priv->dbi_output = dbi_output;
} else if (dsi_connector->pipe == 2) {
dbi_output->channel_num = 1;
dev_priv->dbi_output2 = dbi_output;
} else {
dev_err(dev->dev, "only support 2 DSI outputs\n");
goto out_err1;
}
dbi_output->dev = dev;
dbi_output->p_funcs = p_funcs;
/*panel reset*/
ret = mdfld_dbi_panel_reset(dbi_output);
if (ret) {
dev_err(dev->dev, "reset panel error\n");
goto out_err1;
}
/*TODO: get panel info from DDB*/
/*get fixed mode*/
dsi_config = mdfld_dsi_get_config(dsi_connector);
fixed_mode = dsi_config->fixed_mode;
dbi_output->panel_fixed_mode = fixed_mode;
/*create drm encoder object*/
connector = &dsi_connector->base.base;
encoder = &dbi_output->base.base;
drm_encoder_init(dev,
encoder,
p_funcs->encoder_funcs,
DRM_MODE_ENCODER_MIPI);
drm_encoder_helper_add(encoder, p_funcs->encoder_helper_funcs);
/*attach to given connector*/
drm_mode_connector_attach_encoder(connector, encoder);
/*set possible crtcs and clones*/
if (dsi_connector->pipe) {
encoder->possible_crtcs = (1 << 2);
encoder->possible_clones = (1 << 1);
} else {
encoder->possible_crtcs = (1 << 0);
encoder->possible_clones = (1 << 0);
}
dev_priv->dsr_fb_update = 0;
dev_priv->dsr_enable = false;
dev_priv->exit_idle = mdfld_dsi_dbi_exit_dsr;
#if defined(CONFIG_MDFLD_DSI_DPU) || defined(CONFIG_MDFLD_DSI_DSR)
dev_priv->dsr_enable_config = false;
#endif /*CONFIG_MDFLD_DSI_DSR*/
dbi_output->first_boot = true;
dbi_output->mode_flags = MODE_SETTING_IN_ENCODER;
#ifdef CONFIG_MDFLD_DSI_DPU
/*add this output to dpu_info*/
if (dsi_connector->pipe == 0)
dpu_info->dbi_outputs[0] = dbi_output;
} else {
dpu_info->dbi_outputs[1] = dbi_output;
}
dpu_info->dbi_output_num++;
#else /*CONFIG_MDFLD_DSI_DPU*/
/*add this output to dsr_info*/
if (dsi_connector->pipe == 0)
dsr_info->dbi_outputs[0] = dbi_output;
else
dsr_info->dbi_outputs[1] = dbi_output;
dsr_info->dbi_output_num++;
#endif
return &dbi_output->base;
out_err1:
kfree(dbi_output);
return NULL;
}