diff options
author | Alan Cox | 2011-07-05 16:36:47 +0200 |
---|---|---|
committer | Greg Kroah-Hartman | 2011-07-05 17:20:39 +0200 |
commit | a897854c30903bc77d919fc303009d17c1548b08 (patch) | |
tree | 22caff957c2cefa7226cd2ff7b6b628303e4b0b9 /drivers/staging/gma500/mdfld_dsi_dbi.c | |
parent | gma500: 2D polish (diff) | |
download | kernel-qcow2-linux-a897854c30903bc77d919fc303009d17c1548b08.tar.gz kernel-qcow2-linux-a897854c30903bc77d919fc303009d17c1548b08.tar.xz kernel-qcow2-linux-a897854c30903bc77d919fc303009d17c1548b08.zip |
gma500: Medfield support
This large patch adds all the basics for Medfield support. Lots of clean up
needed in this area still.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/gma500/mdfld_dsi_dbi.c')
-rw-r--r-- | drivers/staging/gma500/mdfld_dsi_dbi.c | 872 |
1 files changed, 872 insertions, 0 deletions
diff --git a/drivers/staging/gma500/mdfld_dsi_dbi.c b/drivers/staging/gma500/mdfld_dsi_dbi.c new file mode 100644 index 000000000000..15055c85c1d4 --- /dev/null +++ b/drivers/staging/gma500/mdfld_dsi_dbi.c @@ -0,0 +1,872 @@ +/* + * 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, + ¶m, + 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; +} |