Messages in this thread Patch in this message | | | From | cailiwei <> | Subject | [PATCH 3/8] fb: hisilicon: Add framebuffer driver for hi3660 SoC | Date | Tue, 7 Feb 2017 10:35:54 +0800 |
| |
From: Levy-Cai <cailiwei@hisilicon.com>
Add framebuffer driver for hi3660 SoC, this driver include lcd driver & Hdmi adv7533/adv7535 driver, support lcd display at 1080p@60 and hdmi display at 1080p@60.
Signed-off-by: cailiwei <cailiwei@hisilicon.com> --- drivers/video/fbdev/hisi/dss/hisi_dpe.c | 763 +++++++++ drivers/video/fbdev/hisi/dss/hisi_dpe_utils.h | 64 + drivers/video/fbdev/hisi/dss/hisi_fb.c | 2232 +++++++++++++++++++++++++ drivers/video/fbdev/hisi/dss/hisi_fb.h | 559 +++++++ drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.c | 1686 +++++++++++++++++++ drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.h | 152 ++ 6 files changed, 5456 insertions(+) create mode 100755 drivers/video/fbdev/hisi/dss/hisi_dpe.c create mode 100755 drivers/video/fbdev/hisi/dss/hisi_dpe_utils.h create mode 100755 drivers/video/fbdev/hisi/dss/hisi_fb.c create mode 100755 drivers/video/fbdev/hisi/dss/hisi_fb.h create mode 100755 drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.c create mode 100755 drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.h
diff --git a/drivers/video/fbdev/hisi/dss/hisi_dpe.c b/drivers/video/fbdev/hisi/dss/hisi_dpe.c new file mode 100755 index 000000000000..fef13287c933 --- /dev/null +++ b/drivers/video/fbdev/hisi/dss/hisi_dpe.c @@ -0,0 +1,763 @@ +/* Copyright (c) 2013-2014, Hisilicon Tech. Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hisi_fb.h" +#include "hisi_dpe_utils.h" +#include "hisi_overlay_utils.h" +#ifdef CONFIG_HISI_OCBC +#include <linux/hisi/ocbc.h> +#endif + +static int dpe_init(struct hisi_fb_data_type *hisifd, bool fastboot_enable) +{ + BUG_ON(hisifd == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + init_post_scf(hisifd); + init_dbuf(hisifd); + init_dpp(hisifd); + /* init_sbl(hisifd); */ + init_acm(hisifd); + init_dpp_csc(hisifd); + init_igm_gmp_xcc_gm(hisifd); + + init_ifbc(hisifd); + init_ldi(hisifd, fastboot_enable); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + if (hisifd->dss_pxl1_clk) + clk_disable(hisifd->dss_pxl1_clk); + + set_reg(hisifd->dss_base + DSS_LDI0_OFFSET + LDI_DSI1_CLK_SEL, + 0x1, 1, 0); + + if (hisifd->dss_pxl1_clk) + clk_enable(hisifd->dss_pxl1_clk); + + set_reg(hisifd->dss_base + DSS_LDI0_OFFSET + LDI_DSI1_RST_SEL, + 0x1, 1, 0); + /* dual lcd: dsi_mux_sel=1, dual mipi: dsi_mux_sel=0 */ + set_reg(hisifd->dss_base + DSS_MCTRL_SYS_OFFSET + + MCTL_DSI_MUX_SEL, 0x1, 1, 0); + + init_dbuf(hisifd); + init_ldi(hisifd, fastboot_enable); + } else if (hisifd->index == AUXILIARY_PANEL_IDX) { + ; + } else { + HISI_FB_ERR("fb%d, not support this device!\n", hisifd->index); + } + + return 0; +} + +static int dpe_deinit(struct hisi_fb_data_type *hisifd) +{ + BUG_ON(hisifd == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + deinit_ldi(hisifd); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + deinit_ldi(hisifd); + } else if (hisifd->index == AUXILIARY_PANEL_IDX) { + ; + } else { + HISI_FB_ERR("fb%d, not support this device!\n", hisifd->index); + } + + return 0; +} + +static void dpe_check_itf_status(struct hisi_fb_data_type *hisifd) +{ + int tmp = 0; + int delay_count = 0; + bool is_timeout = true; + int itf_idx = 0; + char __iomem *mctl_sys_base = NULL; + + BUG_ON(hisifd == NULL); + + if ((hisifd->index == PRIMARY_PANEL_IDX) || + (hisifd->index == EXTERNAL_PANEL_IDX)) { + itf_idx = hisifd->index; + mctl_sys_base = hisifd->dss_base + DSS_MCTRL_SYS_OFFSET; + + while (1) { + tmp = + inp32(mctl_sys_base + MCTL_MOD17_STATUS + + itf_idx * 0x4); + if (((tmp & 0x10) == 0x10) || delay_count > 100) { + is_timeout = (delay_count > 100) ? true : false; + delay_count = 0; + break; + } else { + mdelay(1); + ++delay_count; + } + } + + if (is_timeout) { + HISI_FB_DEBUG + ("mctl_itf%d not in idle status,ints=0x%x !\n", + hisifd->index, tmp); + } + } +} + +static int dpe_irq_enable(struct hisi_fb_data_type *hisifd) +{ + BUG_ON(hisifd == NULL); + + if (hisifd->dpe_irq) + enable_irq(hisifd->dpe_irq); + + return 0; +} + +static int dpe_irq_disable(struct hisi_fb_data_type *hisifd) +{ + BUG_ON(hisifd == NULL); + + if (hisifd->dpe_irq) + disable_irq(hisifd->dpe_irq); + + return 0; +} + +static int dpe_irq_disable_nosync(struct hisi_fb_data_type *hisifd) +{ + BUG_ON(hisifd == NULL); + + if (hisifd->dpe_irq) + disable_irq_nosync(hisifd->dpe_irq); + + return 0; +} + +int dpe_common_clk_enable(struct hisi_fb_data_type *hisifd) +{ + int ret = 0; + struct clk *clk_tmp = NULL; + + BUG_ON(hisifd == NULL); + +#ifdef CONFIG_DSS_MMBUF_CLK_USED + clk_tmp = hisifd->dss_mmbuf_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_mmbuf_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_mmbuf_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } +#endif + + clk_tmp = hisifd->dss_axi_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_axi_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_axi_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + clk_tmp = hisifd->dss_pclk_dss_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_dss_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_dss_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + return 0; +} + +int dpe_inner_clk_enable(struct hisi_fb_data_type *hisifd) +{ + int ret = 0; + struct clk *clk_tmp = NULL; + + BUG_ON(hisifd == NULL); + + clk_tmp = hisifd->dss_pri_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pri_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pri_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + if (hisifd->index == PRIMARY_PANEL_IDX) { + clk_tmp = hisifd->dss_pxl0_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pxl0_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pxl0_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + clk_tmp = hisifd->dss_pxl1_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pxl1_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pxl1_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + } else { + ; + } + + return 0; +} + +int dpe_common_clk_disable(struct hisi_fb_data_type *hisifd) +{ + struct clk *clk_tmp = NULL; + + BUG_ON(hisifd == NULL); + + clk_tmp = hisifd->dss_pclk_dss_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + + clk_tmp = hisifd->dss_axi_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } +#ifdef CONFIG_DSS_MMBUF_CLK_USED + clk_tmp = hisifd->dss_mmbuf_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } +#endif + + return 0; +} + +int dpe_inner_clk_disable(struct hisi_fb_data_type *hisifd) +{ + struct clk *clk_tmp = NULL; + + BUG_ON(hisifd == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + clk_tmp = hisifd->dss_pxl0_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + clk_tmp = hisifd->dss_pxl1_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + } else { + ; + } + + clk_tmp = hisifd->dss_pri_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + + return 0; +} + +/******************************************************************************* + ** + */ +static int dpe_on(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + dpe_common_clk_enable(hisifd); + dpe_inner_clk_enable(hisifd); + /*DSS regulator are already enabled in fastboot, kernel don't care */ + /*dpe_regulator_enable(hisifd); */ + + dss_inner_clk_common_enable(hisifd, false); + if (hisifd->index == PRIMARY_PANEL_IDX) { + dss_inner_clk_pdp_enable(hisifd, false); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + dss_inner_clk_sdp_enable(hisifd); + } else { + ; + } + + dpe_init(hisifd, false); + if (dpe_recover_pxl_clock(hisifd)) { + HISI_FB_ERR + ("fb%d failed to recover pixel clock which is larger than 288M!\n", + hisifd->index); + return -EINVAL; + } + + if (is_ldi_panel(hisifd)) { + hisifd->panel_info.lcd_init_step = LCD_INIT_POWER_ON; + ret = panel_next_on(pdev); + if (ret) { + HISI_FB_ERR("fb%d failed ret %d\n", hisifd->index, ret); + return -EINVAL; + } + } + + ret = panel_next_on(pdev); + if (hisifd->panel_info.vsync_ctrl_type == VSYNC_CTRL_NONE) { + dpe_interrupt_mask(hisifd); + dpe_interrupt_clear(hisifd); + dpe_irq_enable(hisifd); + dpe_interrupt_unmask(hisifd); + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int dpe_off(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + if (hisifd->panel_info.vsync_ctrl_type == VSYNC_CTRL_NONE) { + dpe_interrupt_mask(hisifd); + dpe_irq_disable(hisifd); + } else { + if (hisifd->vsync_ctrl.vsync_ctrl_enabled == 1) { + if (hisifd->panel_info. + vsync_ctrl_type & VSYNC_CTRL_ISR_OFF) { + dpe_interrupt_mask(hisifd); + dpe_irq_disable(hisifd); + HISI_FB_INFO + ("fb%d, need to disable dpe irq! vsync_ctrl_enabled=%d.\n", + hisifd->index, + hisifd->vsync_ctrl.vsync_ctrl_enabled); + } + } + } + + ret = panel_next_off(pdev); + + dpe_deinit(hisifd); + dpe_check_itf_status(hisifd); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + dss_inner_clk_pdp_disable(hisifd); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + dss_inner_clk_sdp_disable(hisifd); + } else { + ; + } + dss_inner_clk_common_disable(hisifd); + + /*dpe_regulator_disable(hisifd); */ + dpe_inner_clk_disable(hisifd); + dpe_common_clk_disable(hisifd); + + if (hisifd->vsync_ctrl_type != VSYNC_CTRL_NONE) { + if (!is_dss_idle_enable()) + hisifd->panel_info.vsync_ctrl_type = VSYNC_CTRL_NONE; + else + hisifd->panel_info.vsync_ctrl_type = + hisifd->vsync_ctrl_type; + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int dpe_remove(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = panel_next_remove(pdev); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int dpe_set_backlight(struct platform_device *pdev, uint32_t bl_level) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + struct hisi_panel_info *pinfo = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + pinfo = &(hisifd->panel_info); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + HISI_FB_DEBUG("fb%d, bl_level=%d.\n", hisifd->index, bl_level); + + if (pinfo->bl_max < 1) { + HISI_FB_ERR("bl_max(%d) is out of range!!", pinfo->bl_max); + return -EINVAL; + } + + if (bl_level > pinfo->bl_max) { + bl_level = pinfo->bl_max; + } + + if (bl_level < pinfo->bl_min && bl_level) { + bl_level = pinfo->bl_min; + } + + ret = panel_next_set_backlight(pdev, bl_level); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int dpe_vsync_ctrl(struct platform_device *pdev, int enable) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + if (enable) { + ret = panel_next_vsync_ctrl(pdev, enable); + if (hisifd->panel_info.vsync_ctrl_type & VSYNC_CTRL_ISR_OFF) { + dpe_interrupt_mask(hisifd); + dpe_interrupt_clear(hisifd); + dpe_irq_enable(hisifd); + dpe_interrupt_unmask(hisifd); + } + } else { + ret = panel_next_vsync_ctrl(pdev, enable); + if (hisifd->panel_info.vsync_ctrl_type & VSYNC_CTRL_ISR_OFF) { + dpe_interrupt_mask(hisifd); + dpe_interrupt_clear(hisifd); + dpe_irq_disable_nosync(hisifd); + } + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int dpe_regulator_clk_irq_setup(struct platform_device *pdev) +{ + struct hisi_fb_data_type *hisifd = NULL; + struct hisi_panel_info *pinfo = NULL; + struct dss_clk_rate *pdss_clk_rate = NULL; + const char *irq_name = NULL; + irqreturn_t(*isr_fnc)(int irq, void *ptr); + int ret = 0; + uint64_t pxl_clk_rate = 0; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + pinfo = &(hisifd->panel_info); + pdss_clk_rate = get_dss_clk_rate(hisifd); + BUG_ON(pdss_clk_rate == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + irq_name = IRQ_PDP_NAME; + isr_fnc = dss_pdp_isr; + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + irq_name = IRQ_SDP_NAME; + isr_fnc = dss_sdp_isr; + } else if (hisifd->index == AUXILIARY_PANEL_IDX) { + irq_name = IRQ_ADP_NAME; + isr_fnc = dss_adp_isr; + } else { + HISI_FB_ERR("fb%d, not support this device!\n", hisifd->index); + return -EINVAL; + } + + HISI_FB_INFO("dss_pclk_dss_clk:[%llu]->[%llu].\n", + pdss_clk_rate->dss_pclk_dss_rate, + (uint64_t) clk_get_rate(hisifd->dss_pclk_dss_clk)); + + ret = + clk_set_rate(hisifd->dss_pri_clk, pdss_clk_rate->dss_pri_clk_rate); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_pri_clk clk_set_rate(%llu) failed, error=%d!\n", + hisifd->index, pdss_clk_rate->dss_pri_clk_rate, ret); + return -EINVAL; + } + HISI_FB_INFO("dss_pri_clk:[%llu]->[%llu].\n", + pdss_clk_rate->dss_pri_clk_rate, + (uint64_t) clk_get_rate(hisifd->dss_pri_clk)); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + pxl_clk_rate = + (pinfo->pxl_clk_rate > + DSS_MAX_PXL0_CLK_288M) ? DSS_MAX_PXL0_CLK_288M : pinfo-> + pxl_clk_rate; + if (pinfo->pxl_clk_rate_adjust > 0) { + ret = + clk_set_rate(hisifd->dss_pxl0_clk, + pinfo->pxl_clk_rate_adjust); + } else { + ret = clk_set_rate(hisifd->dss_pxl0_clk, pxl_clk_rate); + } + + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_pxl0_clk clk_set_rate(%llu) failed, error=%d!\n", + hisifd->index, pinfo->pxl_clk_rate, ret); + } + HISI_FB_INFO("dss_pxl0_clk:[%llu]->[%llu].\n", + pinfo->pxl_clk_rate, + (uint64_t) clk_get_rate(hisifd->dss_pxl0_clk)); + } else if ((hisifd->index == EXTERNAL_PANEL_IDX) + && !hisifd->panel_info.fake_hdmi) { + ret = clk_set_rate(hisifd->dss_pxl1_clk, pinfo->pxl_clk_rate); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_pxl1_clk clk_set_rate(%llu) failed, error=%d!\n", + hisifd->index, pinfo->pxl_clk_rate, ret); + } + HISI_FB_INFO("dss_pxl1_clk:[%llu]->[%llu].\n", + pinfo->pxl_clk_rate, + (uint64_t) clk_get_rate(hisifd->dss_pxl1_clk)); + } else { + ; + } + + if (hisifd->dpe_irq) { + ret = + request_irq(hisifd->dpe_irq, isr_fnc, 0, irq_name, + (void *)hisifd); + if (ret != 0) { + HISI_FB_ERR + ("fb%d request_irq failed, irq_no=%d error=%d!\n", + hisifd->index, hisifd->dpe_irq, ret); + return ret; + } else { + disable_irq(hisifd->dpe_irq); + } + } + return 0; +} + +static int dpe_probe(struct platform_device *pdev) +{ + struct hisi_fb_data_type *hisifd = NULL; + struct platform_device *hisi_fb_dev = NULL; + struct hisi_fb_panel_data *pdata = NULL; + struct fb_info *fbi = NULL; + int ret = 0; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = dpe_regulator_clk_irq_setup(pdev); + if (ret) { + HISI_FB_ERR("fb%d dpe_irq_clk_setup failed, error=%d!\n", + hisifd->index, ret); + goto err; + } + + /* alloc device */ + hisi_fb_dev = platform_device_alloc(DEV_NAME_FB, pdev->id); + if (!hisi_fb_dev) { + HISI_FB_ERR("fb%d platform_device_alloc failed, error=%d!\n", + hisifd->index, ret); + ret = -ENOMEM; + goto err_device_alloc; + } + + /* link to the latest pdev */ + hisifd->pdev = hisi_fb_dev; + + /* alloc panel device data */ + ret = + platform_device_add_data(hisi_fb_dev, dev_get_platdata(&pdev->dev), + sizeof(struct hisi_fb_panel_data)); + if (ret) { + HISI_FB_ERR("fb%d platform_device_add_data failed, error=%d!\n", + hisifd->index, ret); + goto err_device_put; + } + + /* data chain */ + pdata = dev_get_platdata(&hisi_fb_dev->dev); + pdata->on = dpe_on; + pdata->off = dpe_off; + pdata->remove = dpe_remove; + pdata->set_backlight = dpe_set_backlight; + pdata->vsync_ctrl = dpe_vsync_ctrl; + pdata->next = pdev; + + /* get/set panel info */ + memcpy(&hisifd->panel_info, pdata->panel_info, + sizeof(struct hisi_panel_info)); + + fbi = hisifd->fbi; + fbi->var.pixclock = hisifd->panel_info.pxl_clk_rate; + /*fbi->var.pixclock = clk_round_rate(hisifd->dpe_clk, + hisifd->panel_info.pxl_clk_rate); */ + fbi->var.left_margin = hisifd->panel_info.ldi.h_back_porch; + fbi->var.right_margin = hisifd->panel_info.ldi.h_front_porch; + fbi->var.upper_margin = hisifd->panel_info.ldi.v_back_porch; + fbi->var.lower_margin = hisifd->panel_info.ldi.v_front_porch; + fbi->var.hsync_len = hisifd->panel_info.ldi.h_pulse_width; + fbi->var.vsync_len = hisifd->panel_info.ldi.v_pulse_width; + + hisifd->vsync_ctrl_type = hisifd->panel_info.vsync_ctrl_type; + + /* set driver data */ + platform_set_drvdata(hisi_fb_dev, hisifd); + ret = platform_device_add(hisi_fb_dev); + if (ret) { + HISI_FB_ERR("fb%d platform_device_add failed, error=%d!\n", + hisifd->index, ret); + goto err_device_put; + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return 0; + + err_device_put: + platform_device_put(hisi_fb_dev); + err_device_alloc: + err: + return ret; +} + +static struct platform_driver this_driver = { + .probe = dpe_probe, + .remove = NULL, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = DEV_NAME_DSS_DPE, + }, +}; + +static int __init dpe_driver_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&this_driver); + if (ret) { + HISI_FB_ERR("platform_driver_register failed, error=%d!\n", + ret); + return ret; + } + + return ret; +} + +module_init(dpe_driver_init); diff --git a/drivers/video/fbdev/hisi/dss/hisi_dpe_utils.h b/drivers/video/fbdev/hisi/dss/hisi_dpe_utils.h new file mode 100755 index 000000000000..5cacb874c39b --- /dev/null +++ b/drivers/video/fbdev/hisi/dss/hisi_dpe_utils.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2013-2014, Hisilicon Tech. Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef HISI_DPE_UTILS_H +#define HISI_DPE_UTILS_H + +#include "hisi_fb.h" + +#define COMFORM_MAX 80 +#define CHANGE_MAX 100 +#define DISCOUNT_COEFFICIENT(value) (CHANGE_MAX - value) / CHANGE_MAX + +struct dss_clk_rate *get_dss_clk_rate(struct hisi_fb_data_type *hisifd); +int set_dss_clk_rate(struct hisi_fb_data_type *hisifd, + dss_clk_rate_t dss_clk_rate); + +void init_post_scf(struct hisi_fb_data_type *hisifd); +void init_dbuf(struct hisi_fb_data_type *hisifd); +void init_dpp(struct hisi_fb_data_type *hisifd); +void init_acm(struct hisi_fb_data_type *hisifd); +void init_igm_gmp_xcc_gm(struct hisi_fb_data_type *hisifd); +void init_ifbc(struct hisi_fb_data_type *hisifd); +void init_ldi(struct hisi_fb_data_type *hisifd, bool fastboot_enable); +void deinit_ldi(struct hisi_fb_data_type *hisifd); +void enable_ldi(struct hisi_fb_data_type *hisifd); +void disable_ldi(struct hisi_fb_data_type *hisifd); +void ldi_frame_update(struct hisi_fb_data_type *hisifd, bool update); +void single_frame_update(struct hisi_fb_data_type *hisifd); +void ldi_data_gate(struct hisi_fb_data_type *hisifd, bool enble); +int dpe_recover_pxl_clock(struct hisi_fb_data_type *hisifd); +void init_dpp_csc(struct hisi_fb_data_type *hisifd); + +/* isr */ +irqreturn_t dss_pdp_isr(int irq, void *ptr); +irqreturn_t dss_sdp_isr(int irq, void *ptr); +irqreturn_t dss_adp_isr(int irq, void *ptr); + +void dpe_interrupt_clear(struct hisi_fb_data_type *hisifd); +void dpe_interrupt_unmask(struct hisi_fb_data_type *hisifd); +void dpe_interrupt_mask(struct hisi_fb_data_type *hisifd); +int dpe_common_clk_enable(struct hisi_fb_data_type *hisifd); +int dpe_inner_clk_enable(struct hisi_fb_data_type *hisifd); +int dpe_common_clk_disable(struct hisi_fb_data_type *hisifd); +int dpe_inner_clk_disable(struct hisi_fb_data_type *hisifd); +void dss_inner_clk_common_enable(struct hisi_fb_data_type *hisifd, + bool fastboot_enable); +void dss_inner_clk_common_disable(struct hisi_fb_data_type *hisifd); +void dss_inner_clk_pdp_enable(struct hisi_fb_data_type *hisifd, + bool fastboot_enable); +void dss_inner_clk_pdp_disable(struct hisi_fb_data_type *hisifd); +void dss_inner_clk_sdp_enable(struct hisi_fb_data_type *hisifd); +void dss_inner_clk_sdp_disable(struct hisi_fb_data_type *hisifd); + +#endif diff --git a/drivers/video/fbdev/hisi/dss/hisi_fb.c b/drivers/video/fbdev/hisi/dss/hisi_fb.c new file mode 100755 index 000000000000..e3ff413e5d42 --- /dev/null +++ b/drivers/video/fbdev/hisi/dss/hisi_fb.c @@ -0,0 +1,2232 @@ +/* Copyright (c) 2013-2014, Hisilicon Tech. Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hisi_fb.h" +#include "hisi_overlay_utils.h" +#include <linux/init.h> + +static int hisi_fb_resource_initialized; +static struct platform_device *pdev_list[HISI_FB_MAX_DEV_LIST] = { 0 }; + +static int pdev_list_cnt; +struct fb_info *fbi_list[HISI_FB_MAX_FBI_LIST] = { 0 }; + +static int fbi_list_index; + +struct hisi_fb_data_type *hisifd_list[HISI_FB_MAX_FBI_LIST] = { 0 }; + +static int hisifd_list_index; + +#define HISI_FB_ION_CLIENT_NAME "hisi_fb_ion" + +uint32_t g_dts_resouce_ready = 0; +uint32_t g_dss_base_phy = 0; +uint32_t g_dss_module_resource_initialized = 0; + +struct iommu_domain *g_hisi_domain; + +static char __iomem *hisifd_dss_base; +static char __iomem *hisifd_peri_crg_base; +static char __iomem *hisifd_sctrl_base; +static char __iomem *hisifd_pctrl_base; +static char __iomem *hisifd_noc_dss_base; +static char __iomem *hisifd_mmbuf_crg_base; +static char __iomem *hisifd_mmbuf_asc0_base; +static char __iomem *hisifd_pmctrl_base; + +static uint32_t hisifd_irq_pdp; +static uint32_t hisifd_irq_sdp; +static uint32_t hisifd_irq_adp; +static uint32_t hisifd_irq_dsi0; +static uint32_t hisifd_irq_dsi1; + +/*DSS regulators are already enabled in fastboot, so kernel don't care*/ +/* +#define MAX_DPE_NUM (2) +static struct regulator_bulk_data g_dpe_regulator[MAX_DPE_NUM] = {{0}, {0}}; +*/ + +static struct clk *dss_aclk_dss; +static struct clk *dss_pclk_dss; +static struct clk *dss_clk_edc0; +static struct clk *dss_clk_ldi0; +static struct clk *dss_clk_ldi1; +static struct clk *dss_clk_dss_axi_mm; +static struct clk *dss_pclk_mmbuf; +static struct clk *dss_clk_txdphy0_ref; +static struct clk *dss_clk_txdphy1_ref; +static struct clk *dss_clk_txdphy0_cfg; +static struct clk *dss_clk_txdphy1_cfg; +static struct clk *dss_pclk_dsi0; +static struct clk *dss_pclk_dsi1; + +int g_debug_enable_lcd_sleep_in = 0; +int g_err_status = 0; + +/* + ** for debug, S_IRUGO + ** /sys/module/hisifb/parameters + */ +unsigned hisi_fb_msg_level = 7; +module_param_named(debug_msg_level, hisi_fb_msg_level, int, 0644); +MODULE_PARM_DESC(debug_msg_level, "hisi fb msg level"); + +int g_debug_mmu_error = 0; +module_param_named(debug_mmu_error, g_debug_mmu_error, int, 0644); +MODULE_PARM_DESC(debug_mmu_error, "hisi mmu error debug"); + +int g_debug_ldi_underflow = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ldi_underflow, g_debug_ldi_underflow, int, 0644); +MODULE_PARM_DESC(debug_ldi_underflow, "hisi ldi_underflow debug"); +#endif + +int g_debug_ldi_underflow_clear = 1; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ldi_underflow_clear, + g_debug_ldi_underflow_clear, int, 0644); +MODULE_PARM_DESC(debug_ldi_underflow_clear, "hisi ldi_underflow_clear debug"); +#endif + +int g_debug_set_reg_val = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_set_reg_val, g_debug_set_reg_val, int, 0644); +MODULE_PARM_DESC(debug_set_reg_val, "hisi set reg val debug"); +#endif + +int g_debug_online_vsync = 0; +module_param_named(debug_online_vsync, g_debug_online_vsync, int, 0644); +MODULE_PARM_DESC(debug_online_vsync, "hisi online vsync debug"); + +int g_debug_ovl_online_composer = 0; +module_param_named(debug_ovl_online_composer, + g_debug_ovl_online_composer, int, 0644); +MODULE_PARM_DESC(debug_ovl_online_composer, + "hisi overlay online composer debug"); + +int g_debug_ovl_online_composer_hold = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ovl_online_composer_hold, + g_debug_ovl_online_composer_hold, int, 0644); +MODULE_PARM_DESC(debug_ovl_online_composer_hold, + "hisi overlay online composer hold debug"); +#endif + +int g_debug_ovl_online_composer_return = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ovl_online_composer_return, + g_debug_ovl_online_composer_return, int, 0644); +MODULE_PARM_DESC(debug_ovl_online_composer_return, + "hisi overlay online composer return debug"); +#endif + +int g_debug_ovl_online_composer_timediff = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ovl_online_composer_timediff, + g_debug_ovl_online_composer_timediff, int, 0644); +MODULE_PARM_DESC(debug_ovl_online_composer_timediff, + "hisi overlay online composer timediff debug"); +#endif + +int g_debug_ovl_online_composer_time_threshold = 6000; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ovl_online_composer_time_threshold, + g_debug_ovl_online_composer_time_threshold, int, 0644); +MODULE_PARM_DESC(debug_ovl_online_composer_time_threshold, + "hisi overlay online composer time threshold debug"); +#endif + +int g_debug_ovl_block_composer = 0; +module_param_named(debug_ovl_block_composer, + g_debug_ovl_block_composer, int, 0644); +MODULE_PARM_DESC(debug_ovl_block_composer, + "hisi overlay block composer debug"); + +int g_debug_ovl_cmdlist = 0; +module_param_named(debug_ovl_cmdlist, g_debug_ovl_cmdlist, int, 0644); +MODULE_PARM_DESC(debug_ovl_cmdlist, "hisi overlay cmdlist debug"); + +int g_dump_cmdlist_content = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(dump_cmdlist_content, g_dump_cmdlist_content, int, 0644); +MODULE_PARM_DESC(dump_cmdlist_content, "hisi overlay dump cmdlist content"); +#endif + +int g_enable_ovl_cmdlist_online = 1; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(enable_ovl_cmdlist_online, + g_enable_ovl_cmdlist_online, int, 0644); +MODULE_PARM_DESC(enable_ovl_cmdlist_online, + "hisi overlay cmdlist online enable"); +#endif + +int g_enable_ovl_cmdlist_offline = 1; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(enable_ovl_cmdlist_offline, + g_enable_ovl_cmdlist_offline, int, 0644); +MODULE_PARM_DESC(enable_ovl_cmdlist_offline, + "hisi overlay cmdlist offline enable"); +#endif + +int g_rdma_stretch_threshold = RDMA_STRETCH_THRESHOLD; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(rdma_stretch_threshold, + g_rdma_stretch_threshold, int, 0644); +MODULE_PARM_DESC(rdma_stretch_threshold, "hisi rdma stretch threshold"); +#endif + +int g_enable_dirty_region_updt = 1; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(enable_dirty_region_updt, + g_enable_dirty_region_updt, int, 0644); +MODULE_PARM_DESC(enable_dirty_region_updt, + "hisi dss dirty_region_updt enable"); +#endif + +int g_debug_dirty_region_updt = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_dirty_region_updt, + g_debug_dirty_region_updt, int, 0644); +MODULE_PARM_DESC(debug_dirty_region_updt, + "hisi dss dirty_region_updt debug"); +#endif + +int g_enable_crc_debug = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(enable_crc_debug, g_enable_crc_debug, int, 0644); +MODULE_PARM_DESC(enable_crc_debug, "hisi dss crc debug enable"); +#endif + +int g_ldi_data_gate_en = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(enable_ldi_data_gate, g_ldi_data_gate_en, int, 0644); +MODULE_PARM_DESC(enable_ldi_data_gate, "hisi dss ldi data gate enable"); +#endif + +int g_debug_need_save_file = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_need_save_file, g_debug_need_save_file, int, 0644); +MODULE_PARM_DESC(debug_need_save_file, "hisi dss debug need to save file"); +#endif + +int g_debug_ovl_credit_step = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_ovl_credit_step, g_debug_ovl_credit_step, int, 0644); +MODULE_PARM_DESC(debug_ovl_credit_step, "hisi overlay debug_ovl_credit_step"); +#endif + +int g_debug_layerbuf_sync = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(debug_layerbuf_sync, g_debug_layerbuf_sync, int, 0644); +MODULE_PARM_DESC(debug_layerbuf_sync, "hisi dss debug_layerbuf_sync"); +#endif + +int g_enable_dss_idle; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(enable_dss_idle, g_enable_dss_idle, int, 0644); +MODULE_PARM_DESC(enable_dss_idle, "hisi dss enable_dss_idle"); +#endif + +unsigned int g_dss_smmu_outstanding = DSS_SMMU_OUTSTANDING_VAL + 1; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(dss_smmu_outstanding, g_dss_smmu_outstanding, int, 0644); +MODULE_PARM_DESC(dss_smmu_outstanding, "hisi dss smmu outstanding"); +#endif + +int g_debug_dump_mmbuf = 0; +module_param_named(debug_dump_mmbuf, g_debug_dump_mmbuf, int, 0644); +MODULE_PARM_DESC(debug_dump_mmbuf, "hisi dump mmbuf debug"); + +uint32_t g_underflow_stop_perf_stat = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(underflow_stop_perf, g_underflow_stop_perf_stat, int, 0600); +MODULE_PARM_DESC(underflow_stop_perf, "hisi underflow stop perf stat"); +#endif + +uint32_t g_dss_min_bandwidth_inbusbusy = 200; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(dss_min_bandwidth_inbusbusy, + g_dss_min_bandwidth_inbusbusy, int, 0644); +MODULE_PARM_DESC(dss_min_bandwidth_inbusbusy, + "hisi overlay dss_min_bandwidth_inbusbusy"); +#endif + +uint32_t g_mmbuf_addr_test = 0; +#ifdef CONFIG_FB_DEBUG_USED +module_param_named(mmbuf_addr_test, g_mmbuf_addr_test, int, 0600); +MODULE_PARM_DESC(mmbuf_addr_test, "hisi mmbuf addr test"); +#endif + +/****************************************************************************** + ** FUNCTIONS PROTOTYPES + */ +static int hisi_fb_register(struct hisi_fb_data_type *hisifd); + +static int hisi_fb_open(struct fb_info *info, int user); +static int hisi_fb_release(struct fb_info *info, int user); +static int hisi_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); +static int hisi_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int hisi_fb_set_par(struct fb_info *info); +static int hisi_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg); +static int hisi_fb_mmap(struct fb_info *info, struct vm_area_struct *vma); + +static int hisi_fb_suspend_sub(struct hisi_fb_data_type *hisifd); +static int hisi_fb_resume_sub(struct hisi_fb_data_type *hisifd); + +/******************************************************************************* + ** + */ +struct platform_device *hisi_fb_add_device(struct platform_device *pdev) +{ + struct hisi_fb_panel_data *pdata = NULL; + struct platform_device *this_dev = NULL; + struct fb_info *fbi = NULL; + struct hisi_fb_data_type *hisifd = NULL; + uint32_t type = 0; + uint32_t id = 0; + + BUG_ON(pdev == NULL); + pdata = dev_get_platdata(&pdev->dev); + BUG_ON(pdata == NULL); + + if (fbi_list_index >= HISI_FB_MAX_FBI_LIST) { + HISI_FB_ERR("no more framebuffer info list!\n"); + return NULL; + } + + id = pdev->id; + type = pdata->panel_info->type; + + /* alloc panel device data */ + this_dev = hisi_fb_device_alloc(pdata, type, id); + if (!this_dev) { + HISI_FB_ERR("failed to hisi_fb_device_alloc!\n"); + return NULL; + } + + /* alloc framebuffer info + par data */ + fbi = framebuffer_alloc(sizeof(struct hisi_fb_data_type), NULL); + if (fbi == NULL) { + HISI_FB_ERR("can't alloc framebuffer info data!\n"); + platform_device_put(this_dev); + return NULL; + } + + /* data chain */ + pdata = dev_get_platdata(&this_dev->dev); + pdata->next = pdev; + + hisifd = (struct hisi_fb_data_type *)fbi->par; + memset(hisifd, 0, sizeof(struct hisi_fb_data_type)); + hisifd->fbi = fbi; + + hisifd->fb_imgType = HISI_FB_PIXEL_FORMAT_BGRA_8888; + hisifd->index = fbi_list_index; + hisifd->dss_base = hisifd_dss_base; + hisifd->peri_crg_base = hisifd_peri_crg_base; + hisifd->sctrl_base = hisifd_sctrl_base; + hisifd->pctrl_base = hisifd_pctrl_base; + hisifd->noc_dss_base = hisifd_noc_dss_base; + hisifd->mmbuf_crg_base = hisifd_mmbuf_crg_base; + hisifd->mmbuf_asc0_base = hisifd_mmbuf_asc0_base; + hisifd->pmctrl_base = hisifd_pmctrl_base; + + hisifd->mipi_dsi0_base = hisifd->dss_base + DSS_MIPI_DSI0_OFFSET; + hisifd->mipi_dsi1_base = hisifd->dss_base + DSS_MIPI_DSI1_OFFSET; + hisifd->dss_base_phy = g_dss_base_phy; + + hisifd->dss_axi_clk = dss_aclk_dss; + hisifd->dss_pclk_dss_clk = dss_pclk_dss; + hisifd->dss_pri_clk = dss_clk_edc0; + hisifd->dss_pxl0_clk = dss_clk_ldi0; + hisifd->dss_pxl1_clk = dss_clk_ldi1; + hisifd->dss_mmbuf_clk = dss_clk_dss_axi_mm; + hisifd->dss_pclk_mmbuf_clk = dss_pclk_mmbuf; + hisifd->dss_dphy0_ref_clk = dss_clk_txdphy0_ref; + hisifd->dss_dphy1_ref_clk = dss_clk_txdphy1_ref; + hisifd->dss_dphy0_cfg_clk = dss_clk_txdphy0_cfg; + hisifd->dss_dphy1_cfg_clk = dss_clk_txdphy1_cfg; + hisifd->dss_pclk_dsi0_clk = dss_pclk_dsi0; + hisifd->dss_pclk_dsi1_clk = dss_pclk_dsi1; + + hisifd->dsi0_irq = hisifd_irq_dsi0; + hisifd->dsi1_irq = hisifd_irq_dsi1; + if (hisifd->index == PRIMARY_PANEL_IDX) { + hisifd->fb_num = HISI_FB0_NUM; + hisifd->dpe_irq = hisifd_irq_pdp; + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + hisifd->fb_num = HISI_FB1_NUM; + hisifd->dpe_irq = hisifd_irq_sdp; + } else if (hisifd->index == AUXILIARY_PANEL_IDX) { + hisifd->fb_num = HISI_FB2_NUM; + hisifd->dpe_irq = hisifd_irq_adp; + } else { + HISI_FB_ERR("fb%d not support now!\n", hisifd->index); + platform_device_put(this_dev); + framebuffer_release(fbi); + return NULL; + } + + /* link to the latest pdev */ + hisifd->pdev = this_dev; + + hisifd_list[hisifd_list_index++] = hisifd; + fbi_list[fbi_list_index++] = fbi; + + /* get/set panel info */ + memcpy(&hisifd->panel_info, pdata->panel_info, + sizeof(struct hisi_panel_info)); + + /* set driver data */ + platform_set_drvdata(this_dev, hisifd); + + if (platform_device_add(this_dev)) { + HISI_FB_ERR("failed to platform_device_add!\n"); + framebuffer_release(fbi); + platform_device_put(this_dev); + hisifd_list_index--; + fbi_list_index--; + return NULL; + } + + return this_dev; +} + +int hisi_fb_blank_sub(int blank_mode, struct fb_info *info) +{ + struct hisi_fb_data_type *hisifd = NULL; + int ret = 0; + int curr_pwr_state = 0; + + hisifd = (struct hisi_fb_data_type *)info->par; + BUG_ON(hisifd == NULL); + + down(&hisifd->blank_sem); + down(&hisifd->blank_sem0); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + if (!hisifd->panel_power_on) { + ret = hisifd->on_fnc(hisifd); + if (ret == 0) { + hisifd->panel_power_on = true; + } + } + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + case FB_BLANK_POWERDOWN: + default: + if (hisifd->panel_power_on) { + curr_pwr_state = hisifd->panel_power_on; + hisifd->panel_power_on = false; + + if (hisifd->bl_cancel) { + hisifd->bl_cancel(hisifd); + } + + ret = hisifd->off_fnc(hisifd); + if (ret) + hisifd->panel_power_on = curr_pwr_state; + + if (hisifd->buf_sync_suspend) + hisifd->buf_sync_suspend(hisifd); + } + break; + } + up(&hisifd->blank_sem); + up(&hisifd->blank_sem0); + + return ret; +} + +static int hisi_fb_open_sub(struct fb_info *info) +{ + struct hisi_fb_data_type *hisifd = NULL; + int ret = 0; + bool needed = false; + + BUG_ON(info == NULL); + hisifd = (struct hisi_fb_data_type *)info->par; + BUG_ON(hisifd == NULL); + + if (hisifd->set_fastboot_fnc) { + needed = hisifd->set_fastboot_fnc(info); + } + + if (!needed) { + ret = hisi_fb_blank_sub(FB_BLANK_UNBLANK, info); + if (ret != 0) { + HISI_FB_ERR("can't turn on display!\n"); + return ret; + } + } + + return 0; +} + +static int hisi_fb_release_sub(struct fb_info *info) +{ + int ret = 0; + + BUG_ON(info == NULL); + + ret = hisi_fb_blank_sub(FB_BLANK_POWERDOWN, info); + if (ret != 0) { + HISI_FB_ERR("can't turn off display!\n"); + return ret; + } + + return 0; +} + +/******************************************************************************* + ** + */ +static int hisi_fb_blank(int blank_mode, struct fb_info *info) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + if (hisifd->panel_info.fake_hdmi + && (hisifd->index == EXTERNAL_PANEL_IDX)) { + HISI_FB_INFO("it is fake, blank it fail \n"); + return -EINVAL; + } +#if 0 + if (blank_mode == FB_BLANK_POWERDOWN) { + struct fb_event event; + event.info = info; + event.data = &blank_mode; + fb_notifier_call_chain(FB_EVENT_BLANK, &event); + } +#endif + + if (hisifd->index == AUXILIARY_PANEL_IDX) { + HISI_FB_DEBUG("fb%d, blank_mode(%d) +.\n", hisifd->index, + blank_mode); + } else { + HISI_FB_INFO("fb%d, blank_mode(%d) +.\n", hisifd->index, + blank_mode); + } + + ret = hisi_fb_blank_sub(blank_mode, info); + if (ret != 0) { + HISI_FB_ERR("fb%d, blank_mode(%d) failed!\n", hisifd->index, + blank_mode); + return ret; + } + + if (hisifd->index == AUXILIARY_PANEL_IDX) { + HISI_FB_DEBUG("fb%d, blank_mode(%d) -.\n", hisifd->index, + blank_mode); + } else { + HISI_FB_INFO("fb%d, blank_mode(%d) -.\n", hisifd->index, + blank_mode); + } + + return 0; +} + +static int hisi_fb_open(struct fb_info *info, int user) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + if (hisifd->panel_info.fake_hdmi + && (hisifd->index == EXTERNAL_PANEL_IDX)) { + HISI_FB_INFO("fb%d, is fake, open it fail \n", hisifd->index); + return -EINVAL; + } + + if (!hisifd->ref_cnt) { + HISI_FB_DEBUG("fb%d, +!\n", hisifd->index); + if (hisifd->open_sub_fnc) { + ret = hisifd->open_sub_fnc(info); + } + HISI_FB_DEBUG("fb%d, -!\n", hisifd->index); + } + + hisifd->ref_cnt++; + + return ret; +} + +static int hisi_fb_release(struct fb_info *info, int user) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + if (hisifd->panel_info.fake_hdmi + && (hisifd->index == EXTERNAL_PANEL_IDX)) { + HISI_FB_INFO("fb%d, is fake, release it fail \n", + hisifd->index); + return -EINVAL; + } + + if (!hisifd->ref_cnt) { + HISI_FB_INFO("try to close unopened fb%d!\n", hisifd->index); + return -EINVAL; + } + + hisifd->ref_cnt--; + + if (!hisifd->ref_cnt) { + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + if (hisifd->release_sub_fnc) { + ret = hisifd->release_sub_fnc(info); + } + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + if (hisifd->fb_mem_free_flag) + hisifb_free_fb_buffer(hisifd); + } + } + return ret; +} + +static int hisi_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + if (var->rotate != FB_ROTATE_UR) { + HISI_FB_ERR("error rotate %d!\n", var->rotate); + return -EINVAL; + } + + if (var->grayscale != info->var.grayscale) { + HISI_FB_DEBUG("error grayscale %d!\n", var->grayscale); + return -EINVAL; + } + + if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0)) { + HISI_FB_ERR("xres_virtual=%d yres_virtual=%d out of range!", + var->xres_virtual, var->yres_virtual); + return -EINVAL; + } +#if 0 + if (info->fix.smem_len < + (hisifb_line_length + (hisifd->index, var->xres_virtual, + (var->bits_per_pixel >> 3)) * var->yres_virtual)) { + HISI_FB_ERR("fb%d smem_len=%d is out of range!\n", + hisifd->index, info->fix.smem_len); + return -EINVAL; + } +#endif + + if ((var->xres == 0) || (var->yres == 0)) { + HISI_FB_ERR("xres=%d, yres=%d is invalid!\n", var->xres, + var->yres); + return -EINVAL; + } + + if (var->xoffset > (var->xres_virtual - var->xres)) { + HISI_FB_ERR + ("xoffset=%d(xres_virtual=%d, xres=%d) out of range!\n", + var->xoffset, var->xres_virtual, var->xres); + return -EINVAL; + } + + if (var->yoffset > (var->yres_virtual - var->yres)) { + HISI_FB_ERR + ("yoffset=%d(yres_virtual=%d, yres=%d) out of range!\n", + var->yoffset, var->yres_virtual, var->yres); + return -EINVAL; + } + + return 0; +} + +static int hisi_fb_set_par(struct fb_info *info) +{ + struct hisi_fb_data_type *hisifd = NULL; + struct fb_var_screeninfo *var = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer\n"); + return -EINVAL; + } + + var = &info->var; + + hisifd->fbi->fix.line_length = + hisifb_line_length(hisifd->index, var->xres_virtual, + var->bits_per_pixel >> 3); + + return 0; +} + +static int hisi_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == var || NULL == info) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + down(&hisifd->blank_sem); + + if (!hisifd->panel_power_on) { + HISI_FB_INFO("fb%d, panel power off!\n", hisifd->index); + ret = -EPERM; + goto err_out; + } + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) { + ret = -EINVAL; + goto err_out; + } + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) { + ret = -EINVAL; + goto err_out; + } + + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; + + if (hisifd->pan_display_fnc) { + hisifd->pan_display_fnc(hisifd); + } else { + HISI_FB_ERR("fb%d pan_display_fnc not set!\n", hisifd->index); + } + + up(&hisifd->blank_sem); + + if (hisifd->bl_update) { + hisifd->bl_update(hisifd); + } + + return ret; + + err_out: + up(&hisifd->blank_sem); + return 0; +} + +static int hisifb_lcd_dirty_region_info_get(struct fb_info *info, + void __user *argp) +{ + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + if (NULL == argp) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + if (copy_to_user(argp, &(hisifd->panel_info.dirty_region_info), + sizeof(struct lcd_dirty_region_info))) { + HISI_FB_ERR("copy to user fail"); + return -EFAULT; + } + + return 0; +} + +static int hisifb_dirty_region_updt_set(struct fb_info *info, + void __user *argp) +{ + int enable = 0; + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + if (hisifd->index != PRIMARY_PANEL_IDX) { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + return -EINVAL; + } + + if (NULL == argp) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + if (g_enable_dirty_region_updt + && hisifd->panel_info.dirty_region_updt_support + && !hisifd->sbl_enable + && !hisifd->color_temperature_flag && !hisifd->esd_happened) { + enable = 1; + } + + if (copy_to_user(argp, &enable, sizeof(enable))) { + HISI_FB_ERR("copy to user fail"); + return -EFAULT; + } + + return 0; +} + +static int hisifb_dss_mmbuf_alloc(struct fb_info *info, void __user *argp) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + dss_mmbuf_t mmbuf_info; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + if (NULL == argp) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + ret = copy_from_user(&mmbuf_info, argp, sizeof(dss_mmbuf_t)); + if (ret) { + HISI_FB_ERR("fb%d, copy for user failed!ret=%d.\n", + hisifd->index, ret); + ret = -EINVAL; + goto err_out; + } + + if ((mmbuf_info.size <= 0) || (mmbuf_info.size > MMBUF_SIZE_MAX) + || (mmbuf_info.size & (MMBUF_ADDR_ALIGN - 1))) { + HISI_FB_ERR("fb%d, mmbuf size is invalid, size=%d!\n", + hisifd->index, mmbuf_info.size); + ret = -EINVAL; + goto err_out; + } + + if (g_mmbuf_addr_test > 0) { + if (g_mmbuf_addr_test >= (MMBUF_SIZE_MAX + 0x40)) { + HISI_FB_ERR + ("g_mmbuf_addr_test(0x%x) is overflow max mmbuf size + 0x40(0x%x)\n", + g_mmbuf_addr_test, MMBUF_SIZE_MAX + 0x40); + + HISI_FB_ERR("remain buff size if %d \n", + (MMBUF_SIZE_MAX + 0x40) - + (g_mmbuf_addr_test - mmbuf_info.size)); + + g_mmbuf_addr_test = 0; + } else { + mmbuf_info.addr = g_mmbuf_addr_test; + g_mmbuf_addr_test += mmbuf_info.size; + } + + HISI_FB_INFO + ("addr = 0x%x, size =%d, g_mmbuf_addr_test = 0x%x, MAX_SIZE= 0x%x\n", + mmbuf_info.addr, mmbuf_info.size, g_mmbuf_addr_test, + MMBUF_SIZE_MAX + 0x40); + } + + if (0 == g_mmbuf_addr_test) { + mmbuf_info.addr = + hisi_dss_mmbuf_alloc(hisifd->mmbuf_gen_pool, + mmbuf_info.size); + if (mmbuf_info.addr < MMBUF_BASE) { + ret = -EINVAL; + goto err_out; + } + } + + ret = copy_to_user(argp, &mmbuf_info, sizeof(dss_mmbuf_t)); + if (ret) { + HISI_FB_ERR("fb%d, copy to user failed!ret=%d.", + hisifd->index, ret); + hisi_dss_mmbuf_free(hisifd->mmbuf_gen_pool, + mmbuf_info.addr, mmbuf_info.size); + ret = -EFAULT; + goto err_out; + } + + return 0; + + err_out: + return ret; +} + +static int hisifb_dss_mmbuf_free(struct fb_info *info, void __user *argp) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + struct hisi_fb_panel_data *pdata = NULL; + dss_mmbuf_t mmbuf_info; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + pdata = dev_get_platdata(&hisifd->pdev->dev); + if (NULL == pdata) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + if (NULL == argp) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + + ret = copy_from_user(&mmbuf_info, argp, sizeof(dss_mmbuf_t)); + if (ret) { + HISI_FB_ERR("fb%d, copy for user failed!ret=%d.", hisifd->index, + ret); + ret = -EINVAL; + goto err_out; + } + + if ((mmbuf_info.addr <= 0) || (mmbuf_info.size <= 0)) { + HISI_FB_ERR("fb%d, addr=0x%x, size=%d is invalid!\n", + hisifd->index, mmbuf_info.addr, mmbuf_info.size); + ret = -EINVAL; + goto err_out; + } + + hisi_dss_mmbuf_free(hisifd->mmbuf_gen_pool, mmbuf_info.addr, + mmbuf_info.size); + + return 0; + + err_out: + return ret; +} + +static int hisifb_dss_get_platform_type(struct fb_info *info, + void __user *argp) +{ + int type; + int ret = 0; + + type = HISIFB_DSS_PLATFORM_TYPE; + + if (NULL == argp) { + HISI_FB_ERR("NULL Pointer!\n"); + return -EINVAL; + } + ret = copy_to_user(argp, &type, sizeof(type)); + if (ret) { + HISI_FB_ERR("copy to user failed! ret=%d.", ret); + ret = -EFAULT; + } + + return ret; +} + +static int hisi_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOSYS; + struct hisi_fb_data_type *hisifd = NULL; + void __user *argp = (void __user *)arg; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer"); + return -EINVAL; + } + + switch (cmd) { + case HISIFB_VSYNC_CTRL: + if (hisifd->vsync_ctrl_fnc) { + ret = hisifd->vsync_ctrl_fnc(info, argp); + } + break; + + case HISIFB_DSS_CLK_RATE_SET: + ret = hisifb_ctrl_dss_clk_rate_set(info, argp); + break; + + case HISIFB_LCD_DIRTY_REGION_INFO_GET: + ret = hisifb_lcd_dirty_region_info_get(info, argp); + break; + + case HISIFB_DIRTY_REGION_UPDT_SET: + ret = hisifb_dirty_region_updt_set(info, argp); + break; + + case HISIFB_DSS_MMBUF_ALLOC: + ret = hisifb_dss_mmbuf_alloc(info, argp); + break; + + case HISIFB_DSS_MMBUF_FREE: + ret = hisifb_dss_mmbuf_free(info, argp); + break; + + case HISIFB_PLATFORM_TYPE_GET: + ret = hisifb_dss_get_platform_type(info, argp); + break; + + default: + if (hisifd->ov_ioctl_handler) + ret = hisifd->ov_ioctl_handler(hisifd, cmd, argp); + break; + } + + if (ret == -ENOSYS) + HISI_FB_ERR("unsupported ioctl (%x)\n", cmd); + + return ret; +} + +static int hisi_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct hisi_fb_data_type *hisifd = NULL; + struct sg_table *table = NULL; + struct scatterlist *sg = NULL; + struct page *page = NULL; + unsigned long remainder = 0; + unsigned long len = 0; + unsigned long addr = 0; + unsigned long offset = 0; + int i = 0; + int ret = 0; + + if (NULL == info) { + HISI_FB_ERR("NULL Pointer"); + return -EINVAL; + } + + hisifd = (struct hisi_fb_data_type *)info->par; + if (NULL == hisifd) { + HISI_FB_ERR("NULL Pointer"); + return -EINVAL; + } + + if (hisifd->index == PRIMARY_PANEL_IDX) { + if (hisifd->fb_mem_free_flag) { + if (!hisifb_alloc_fb_buffer(hisifd)) { + HISI_FB_ERR("fb%d, hisifb_alloc_buffer failed!\n", + hisifd->index); + return -ENOMEM; + } + } + } else { + HISI_FB_ERR("fb%d, no fb buffer!\n", hisifd->index); + return -EFAULT;; + } + + table = ion_sg_table(hisifd->ion_client, hisifd->ion_handle); + BUG_ON(table == NULL); + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + addr = vma->vm_start; + offset = vma->vm_pgoff * PAGE_SIZE; + + for_each_sg(table->sgl, sg, table->nents, i) { + page = sg_page(sg); + remainder = vma->vm_end - addr; + len = sg->length; + + if (offset >= sg->length) { + offset -= sg->length; + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len = sg->length - offset; + offset = 0; + } + len = min(len, remainder); + ret = remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + if (ret != 0) { + HISI_FB_ERR("fb%d, failed to remap_pfn_range! ret=%d\n", + hisifd->index, ret); + } + + addr += len; + if (addr >= vma->vm_end) + return 0; + } + + return 0; +} + +unsigned long hisifb_alloc_fb_buffer(struct hisi_fb_data_type *hisifd) +{ + struct fb_info *fbi = NULL; + struct ion_client *client = NULL; + struct ion_handle *handle = NULL; + size_t buf_len = 0; + unsigned long buf_addr = 0; + + BUG_ON(hisifd == NULL); + fbi = hisifd->fbi; + BUG_ON(fbi == NULL); + + if (hisifd->ion_handle != NULL) + return fbi->fix.smem_start; + + client = hisifd->ion_client; + if (IS_ERR_OR_NULL(client)) { + HISI_FB_ERR("failed to create ion client!\n"); + goto err_return; + } + + buf_len = fbi->fix.smem_len; + handle = + ion_alloc(client, buf_len, PAGE_SIZE, ION_HEAP(ION_SYSTEM_HEAP_ID), 0); + + if (IS_ERR_OR_NULL(handle)) { + HISI_FB_ERR("failed to ion_alloc!\n"); + goto err_return; + } + + fbi->screen_base = ion_map_kernel(client, handle); + if (!fbi->screen_base) { + HISI_FB_ERR("failed to ion_map_kernel!\n"); + goto err_ion_map; + } + + if (ion_map_iommu(client, handle, &(hisifd->iommu_format))) { + HISI_FB_ERR("failed to ion_map_iommu!\n"); + goto err_ion_get_addr; + } + + buf_addr = hisifd->iommu_format.iova_start; + + fbi->fix.smem_start = buf_addr; + fbi->screen_size = fbi->fix.smem_len; + + + hisifd->ion_handle = handle; + + return buf_addr; + + err_ion_get_addr: + ion_unmap_kernel(hisifd->ion_client, handle); + err_ion_map: + ion_free(hisifd->ion_client, handle); + err_return: + return 0; +} + +void hisifb_free_fb_buffer(struct hisi_fb_data_type *hisifd) +{ + struct fb_info *fbi = NULL; + + BUG_ON(hisifd == NULL); + fbi = hisifd->fbi; + BUG_ON(fbi == NULL); + + if (hisifd->ion_client != NULL && hisifd->ion_handle != NULL) { + ion_unmap_iommu(hisifd->ion_client, hisifd->ion_handle); + ion_unmap_kernel(hisifd->ion_client, hisifd->ion_handle); + ion_free(hisifd->ion_client, hisifd->ion_handle); + hisifd->ion_handle = NULL; + fbi->screen_base = 0; + fbi->fix.smem_start = 0; + } +} + +/******************************************************************************* + ** fb sys fs + */ +static void hisifb_sysfs_init(struct hisi_fb_data_type *hisifd) +{ + int i = 0; + + BUG_ON(hisifd == NULL); + + hisifd->sysfs_index = 0; + for (i = 0; i < HISI_FB_SYSFS_ATTRS_NUM; i++) { + hisifd->sysfs_attrs[i] = NULL; + } + hisifd->sysfs_attr_group.attrs = hisifd->sysfs_attrs; +} + +static void hisifb_sysfs_attrs_append(struct hisi_fb_data_type *hisifd, + struct attribute *attr) +{ + BUG_ON(hisifd == NULL); + BUG_ON(attr == NULL); + + if (hisifd->sysfs_index >= HISI_FB_SYSFS_ATTRS_NUM) { + HISI_FB_ERR("fb%d, sysfs_atts_num(%d) is out of range(%d)!\n", + hisifd->index, hisifd->sysfs_index, + HISI_FB_SYSFS_ATTRS_NUM); + BUG_ON(1); + return; + } + + hisifd->sysfs_attrs[hisifd->sysfs_index] = attr; + hisifd->sysfs_index++; +} + +static int hisifb_sysfs_create(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + ret = + sysfs_create_group(&hisifd->fbi->dev->kobj, + &(hisifd->sysfs_attr_group)); + if (ret) { + HISI_FB_ERR("fb%d sysfs group creation failed, error=%d!\n", + hisifd->index, ret); + } + + return ret; +} + +static void hisifb_sysfs_remove(struct platform_device *pdev) +{ + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + sysfs_remove_group(&hisifd->fbi->dev->kobj, + &(hisifd->sysfs_attr_group)); + + hisifb_sysfs_init(hisifd); +} + +/******************************************************************************* + ** + */ +static struct fb_ops hisi_fb_ops = { + .owner = THIS_MODULE, + .fb_open = hisi_fb_open, + .fb_release = hisi_fb_release, + .fb_read = NULL, + .fb_write = NULL, + .fb_cursor = NULL, + .fb_check_var = hisi_fb_check_var, + .fb_set_par = hisi_fb_set_par, + .fb_setcolreg = NULL, + .fb_blank = hisi_fb_blank, + .fb_pan_display = hisi_fb_pan_display, + .fb_fillrect = NULL, + .fb_copyarea = NULL, + .fb_imageblit = NULL, + .fb_rotate = NULL, + .fb_sync = NULL, + .fb_ioctl = hisi_fb_ioctl, + .fb_compat_ioctl = hisi_fb_ioctl, + .fb_mmap = hisi_fb_mmap, +}; + +static int hisi_fb_register(struct hisi_fb_data_type *hisifd) +{ + int bpp = 0; + struct hisi_panel_info *panel_info = NULL; + struct fb_info *fbi = NULL; + struct fb_fix_screeninfo *fix = NULL; + struct fb_var_screeninfo *var = NULL; + + BUG_ON(hisifd == NULL); + panel_info = &hisifd->panel_info; + BUG_ON(panel_info == NULL); + + /* + * fb info initialization + */ + fbi = hisifd->fbi; + fix = &fbi->fix; + var = &fbi->var; + + fix->type_aux = 0; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->ywrapstep = 0; + fix->mmio_start = 0; + fix->mmio_len = 0; + fix->accel = FB_ACCEL_NONE; + + var->xoffset = 0; + var->yoffset = 0; + var->grayscale = 0; + var->nonstd = 0; + var->activate = FB_ACTIVATE_VBL; + var->height = panel_info->height; + var->width = panel_info->width; + var->accel_flags = 0; + var->sync = 0; + var->rotate = 0; + + switch (hisifd->fb_imgType) { + case HISI_FB_PIXEL_FORMAT_BGR_565: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->transp.offset = 0; + + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->transp.length = 0; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + bpp = 2; + break; + + case HISI_FB_PIXEL_FORMAT_BGRX_4444: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + var->blue.offset = 0; + var->green.offset = 4; + var->red.offset = 8; + var->transp.offset = 0; + + var->blue.length = 4; + var->green.length = 4; + var->red.length = 4; + var->transp.length = 0; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + bpp = 2; + break; + + case HISI_FB_PIXEL_FORMAT_BGRA_4444: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + var->blue.offset = 0; + var->green.offset = 4; + var->red.offset = 8; + var->transp.offset = 12; + + var->blue.length = 4; + var->green.length = 4; + var->red.length = 4; + var->transp.length = 4; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + bpp = 2; + break; + + case HISI_FB_PIXEL_FORMAT_BGRX_5551: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 10; + var->transp.offset = 0; + + var->blue.length = 5; + var->green.length = 5; + var->red.length = 5; + var->transp.length = 0; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + bpp = 2; + break; + + case HISI_FB_PIXEL_FORMAT_BGRA_5551: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 10; + var->transp.offset = 15; + + var->blue.length = 5; + var->green.length = 5; + var->red.length = 5; + var->transp.length = 1; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + bpp = 2; + break; + + case HISI_FB_PIXEL_FORMAT_BGRA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->transp.offset = 24; + + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->transp.length = 8; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + + bpp = 4; + break; + + case HISI_FB_PIXEL_FORMAT_YUV_422_I: + fix->type = FB_TYPE_INTERLEAVED_PLANES; + fix->xpanstep = 2; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + /* FIXME: R/G/B offset? */ + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->transp.offset = 0; + + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->transp.length = 0; + + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.msb_right = 0; + + bpp = 2; + break; + + default: + HISI_FB_ERR("fb%d, unkown image type!\n", hisifd->index); + return -EINVAL; + } + + + memset(&(hisifd->resolution_rect), 0, sizeof(dss_rect_t)); + memset(&(hisifd->res_updt_rect), 0, sizeof(dss_rect_t)); + + var->xres = panel_info->xres; + var->yres = panel_info->yres; + var->xres_virtual = var->xres; + var->yres_virtual = var->yres * hisifd->fb_num; + var->bits_per_pixel = bpp * 8; + + snprintf(fix->id, sizeof(fix->id), "hisifb%d", hisifd->index); + fix->line_length = + hisifb_line_length(hisifd->index, var->xres_virtual, bpp); + fix->smem_len = + roundup(fix->line_length * var->yres_virtual, PAGE_SIZE); + fix->smem_start = 0; + + fbi->screen_base = 0; + fbi->fbops = &hisi_fb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = NULL; + + fix->reserved[0] = is_mipi_cmd_panel(hisifd) ? 1 : 0; + + hisifd->ion_client = hisi_ion_client_create(HISI_FB_ION_CLIENT_NAME); + if (IS_ERR_OR_NULL(hisifd->ion_client)) { + HISI_FB_ERR("failed to create ion client!\n"); + return -ENOMEM; + } + hisifd->ion_handle = NULL; + memset(&hisifd->iommu_format, 0, sizeof(struct iommu_map_format)); + + if (fix->smem_len > 0) { + if (!hisifb_alloc_fb_buffer(hisifd)) { + HISI_FB_ERR("hisifb_alloc_buffer failed!\n"); + return -ENOMEM; + } + } + + hisifd->ref_cnt = 0; + hisifd->panel_power_on = false; + hisifd->aod_function = 0; + sema_init(&hisifd->blank_sem, 1); + sema_init(&hisifd->blank_sem0, 1); + + hisifb_sysfs_init(hisifd); + + hisifd->on_fnc = hisifb_ctrl_on; + hisifd->off_fnc = hisifb_ctrl_off; + hisifd->hisi_domain = g_hisi_domain; + + if (hisifd->index == PRIMARY_PANEL_IDX) { + hisifd->fb_mem_free_flag = false; + hisifd->open_sub_fnc = hisi_fb_open_sub; + hisifd->release_sub_fnc = hisi_fb_release_sub; + hisifd->sysfs_attrs_add_fnc = hisifb_sysfs_attrs_add; + hisifd->sysfs_attrs_append_fnc = hisifb_sysfs_attrs_append; + hisifd->sysfs_create_fnc = hisifb_sysfs_create; + hisifd->sysfs_remove_fnc = hisifb_sysfs_remove; + hisifd->bl_register = hisifb_backlight_register; + hisifd->bl_unregister = hisifb_backlight_unregister; + hisifd->bl_update = hisifb_backlight_update; + hisifd->bl_cancel = hisifb_backlight_cancel; + hisifd->vsync_register = hisifb_vsync_register; + hisifd->vsync_unregister = hisifb_vsync_unregister; + hisifd->vsync_ctrl_fnc = hisifb_vsync_ctrl; + hisifd->vsync_isr_handler = hisifb_vsync_isr_handler; + hisifd->buf_sync_register = hisifb_buf_sync_register; + hisifd->buf_sync_unregister = hisifb_buf_sync_unregister; + hisifd->buf_sync_signal = hisifb_buf_sync_signal; + hisifd->buf_sync_suspend = hisifb_buf_sync_suspend; + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + hisifd->fb_mem_free_flag = true; + hisifd->release_sub_fnc = hisi_fb_release_sub; + hisifd->bl_register = hisifb_backlight_register; + hisifd->bl_unregister = hisifb_backlight_unregister; + hisifd->bl_update = hisifb_backlight_update; + hisifd->bl_cancel = hisifb_backlight_cancel; + hisifd->vsync_register = hisifb_vsync_register; + hisifd->vsync_unregister = hisifb_vsync_unregister; + hisifd->vsync_ctrl_fnc = hisifb_vsync_ctrl; + hisifd->vsync_isr_handler = hisifb_vsync_isr_handler; + hisifd->buf_sync_register = hisifb_buf_sync_register; + hisifd->buf_sync_unregister = hisifb_buf_sync_unregister; + hisifd->buf_sync_signal = hisifb_buf_sync_signal; + hisifd->buf_sync_suspend = hisifb_buf_sync_suspend; + + } else { + sema_init(&hisifd->offline_composer_sr_sem, 1); + hisifd->offline_composer_sr_refcount = 0; + hisifd->fb_mem_free_flag = true; + } + + if (hisi_overlay_init(hisifd)) { + HISI_FB_ERR("unable to init overlay!\n"); + return -EPERM; + } + + if (register_framebuffer(fbi) < 0) { + HISI_FB_ERR("fb%d failed to register_framebuffer!", + hisifd->index); + return -EPERM; + } + + if (hisifd->sysfs_attrs_add_fnc) { + hisifd->sysfs_attrs_add_fnc(hisifd); + } + + /* backlight register */ + if (hisifd->bl_register) + hisifd->bl_register(hisifd->pdev); + /* vsync register */ + if (hisifd->vsync_register) + hisifd->vsync_register(hisifd->pdev); + /* buf_sync register */ + if (hisifd->buf_sync_register) + hisifd->buf_sync_register(hisifd->pdev); + /* fb sysfs create */ + if (hisifd->sysfs_create_fnc) + hisifd->sysfs_create_fnc(hisifd->pdev); + + HISI_FB_INFO + ("FrameBuffer[%d] %dx%d size=%d bytes phy_addr=%lu virt_addr=%p " + "is registered successfully!\n", hisifd->index, var->xres, + var->yres, fbi->fix.smem_len, fix->smem_start, fbi->screen_base); + + return 0; +} + +/******************************************************************************* + ** + */ +static int hisi_fb_enable_iommu(struct platform_device *pdev) +{ + struct iommu_domain *hisi_domain = NULL; + struct device *dev = NULL; + + BUG_ON(pdev == NULL); + + dev = &pdev->dev; + + /* create iommu domain */ + hisi_domain = iommu_domain_alloc(dev->bus); + if (!hisi_domain) { + HISI_FB_ERR("iommu_domain_alloc failed!\n"); + return -EINVAL; + } + + iommu_attach_device(hisi_domain, dev); + + g_hisi_domain = hisi_domain; + + return 0; +} + +static int hisi_fb_probe(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + struct device_node *np = NULL; + + if (!hisi_fb_resource_initialized) { + HISI_FB_DEBUG("initialized=%d, +.\n", + hisi_fb_resource_initialized); + + pdev->id = 0; + np = of_find_compatible_node(NULL, NULL, DTS_COMP_FB_NAME); + if (!np) { + HISI_FB_ERR("NOT FOUND device node %s!\n", + DTS_COMP_FB_NAME); + return -ENXIO; + } + + dss_aclk_dss = devm_clk_get(&pdev->dev, "aclk_dss"); + if (IS_ERR(dss_aclk_dss)) { + ret = PTR_ERR(dss_aclk_dss); + HISI_FB_ERR("dss_aclk_dss error, ret = %d", ret); + return ret; + } + + dss_pclk_dss = devm_clk_get(&pdev->dev, "pclk_dss"); + if (IS_ERR(dss_pclk_dss)) { + ret = PTR_ERR(dss_pclk_dss); + HISI_FB_ERR("dss_pclk_dss error, ret = %d", ret); + return ret; + } + + dss_clk_edc0 = devm_clk_get(&pdev->dev, "clk_edc0"); + if (IS_ERR(dss_clk_edc0)) { + ret = PTR_ERR(dss_clk_edc0); + HISI_FB_ERR("dss_clk_edc0 error, ret = %d", ret); + return ret; + } + + dss_clk_ldi0 = devm_clk_get(&pdev->dev, "clk_ldi0"); + if (IS_ERR(dss_clk_ldi0)) { + ret = PTR_ERR(dss_clk_ldi0); + HISI_FB_ERR("dss_clk_ldi0 error, ret = %d", ret); + return ret; + } + + dss_clk_ldi1 = devm_clk_get(&pdev->dev, "clk_ldi1"); + if (IS_ERR(dss_clk_ldi1)) { + ret = PTR_ERR(dss_clk_ldi1); + HISI_FB_ERR("dss_clk_ldi1 error, ret = %d", ret); + return ret; + } + + dss_clk_dss_axi_mm = devm_clk_get(&pdev->dev, "clk_dss_axi_mm"); + if (IS_ERR(dss_clk_dss_axi_mm)) { + ret = PTR_ERR(dss_clk_dss_axi_mm); + HISI_FB_ERR("dss_clk_dss_axi_mm error, ret = %d", ret); + return ret; + } + + dss_pclk_mmbuf = devm_clk_get(&pdev->dev, "pclk_mmbuf"); + if (IS_ERR(dss_pclk_mmbuf)) { + ret = PTR_ERR(dss_pclk_mmbuf); + HISI_FB_ERR("dss_pclk_mmbuf error, ret = %d", ret); + return ret; + } + + dss_clk_txdphy0_ref = + devm_clk_get(&pdev->dev, "clk_txdphy0_ref"); + if (IS_ERR(dss_clk_txdphy0_ref)) { + ret = PTR_ERR(dss_clk_txdphy0_ref); + HISI_FB_ERR("dss_clk_txdphy0_ref error, ret = %d", ret); + return ret; + } + + dss_clk_txdphy1_ref = + devm_clk_get(&pdev->dev, "clk_txdphy1_ref"); + if (IS_ERR(dss_clk_txdphy1_ref)) { + ret = PTR_ERR(dss_clk_txdphy1_ref); + HISI_FB_ERR("dss_clk_txdphy1_ref error, ret = %d", ret); + return ret; + } + + dss_clk_txdphy0_cfg = + devm_clk_get(&pdev->dev, "clk_txdphy0_cfg"); + if (IS_ERR(dss_clk_txdphy0_cfg)) { + ret = PTR_ERR(dss_clk_txdphy0_cfg); + HISI_FB_ERR("dss_clk_txdphy0_cfg error, ret = %d", ret); + return ret; + } + + dss_clk_txdphy1_cfg = + devm_clk_get(&pdev->dev, "clk_txdphy1_cfg"); + if (IS_ERR(dss_clk_txdphy1_cfg)) { + ret = PTR_ERR(dss_clk_txdphy1_cfg); + HISI_FB_ERR("dss_clk_txdphy1_cfg error, ret = %d", ret); + return ret; + } + + dss_pclk_dsi0 = devm_clk_get(&pdev->dev, "pclk_dsi0"); + if (IS_ERR(dss_pclk_dsi0)) { + ret = PTR_ERR(dss_pclk_dsi0); + HISI_FB_ERR("dss_pclk_dsi0 error, ret = %d", ret); + return ret; + } + + dss_pclk_dsi1 = devm_clk_get(&pdev->dev, "pclk_dsi1"); + if (IS_ERR(dss_pclk_dsi1)) { + ret = PTR_ERR(dss_pclk_dsi1); + HISI_FB_ERR("dss_pclk_dsi1 error, ret = %d", ret); + return ret; + } + + ret = of_property_read_u32(np, "dss_base_phy", &g_dss_base_phy); + if (ret) { + HISI_FB_ERR("failed to get dss_base_phy.\n"); + return -ENXIO; + } + HISI_FB_INFO("g_dss_base_phy=0x%x.\n", g_dss_base_phy); + + /* get irq no */ + hisifd_irq_pdp = irq_of_parse_and_map(np, 0); + if (!hisifd_irq_pdp) { + HISI_FB_ERR("failed to get hisifd_irq_pdp resource.\n"); + return -ENXIO; + } + + hisifd_irq_sdp = irq_of_parse_and_map(np, 1); + if (!hisifd_irq_sdp) { + HISI_FB_ERR("failed to get hisifd_irq_sdp resource.\n"); + return -ENXIO; + } + + hisifd_irq_adp = irq_of_parse_and_map(np, 2); + if (!hisifd_irq_sdp) { + HISI_FB_ERR("failed to get hisifd_irq_sdp resource.\n"); + return -ENXIO; + } + + hisifd_irq_dsi0 = irq_of_parse_and_map(np, 3); + if (!hisifd_irq_dsi0) { + HISI_FB_ERR + ("failed to get hisifd_irq_dsi0 resource.\n"); + return -ENXIO; + } + + hisifd_irq_dsi1 = irq_of_parse_and_map(np, 4); + if (!hisifd_irq_dsi1) { + HISI_FB_ERR + ("failed to get hisifd_irq_dsi1 resource.\n"); + return -ENXIO; + } + + /* get dss reg base */ + hisifd_dss_base = of_iomap(np, 0); + if (!hisifd_dss_base) { + HISI_FB_ERR + ("failed to get hisifd_dss_base resource.\n"); + return -ENXIO; + } + + hisifd_peri_crg_base = of_iomap(np, 1); + if (!hisifd_peri_crg_base) { + HISI_FB_ERR + ("failed to get hisifd_peri_crg_base resource.\n"); + return -ENXIO; + } + + hisifd_sctrl_base = of_iomap(np, 2); + if (!hisifd_sctrl_base) { + HISI_FB_ERR + ("failed to get hisifd_sctrl_base resource.\n"); + return -ENXIO; + } + + hisifd_pctrl_base = of_iomap(np, 3); + if (!hisifd_pctrl_base) { + HISI_FB_ERR + ("failed to get hisifd_pctrl_base resource.\n"); + return -ENXIO; + } + + hisifd_noc_dss_base = of_iomap(np, 4); + if (!hisifd_noc_dss_base) { + HISI_FB_ERR + ("failed to get hisifd_noc_dss_base resource.\n"); + return -ENXIO; + } + + hisifd_mmbuf_crg_base = of_iomap(np, 5); + if (!hisifd_mmbuf_crg_base) { + HISI_FB_ERR + ("failed to get hisifd_mmbuf_crg_base resource.\n"); + return -ENXIO; + } + + hisifd_pmctrl_base = of_iomap(np, 6); + if (!hisifd_pmctrl_base) { + HISI_FB_ERR + ("failed to get hisifd_pmctrl_base resource.\n"); + return -ENXIO; + } + + /* get regulator resource, DSS regulator is already enabled in fastboot, so kernel dont care */ + /* + g_dpe_regulator[0].supply = REGULATOR_PDP_NAME; + g_dpe_regulator[1].supply = REGULATOR_MMBUF; + ret = devm_regulator_bulk_get(&(pdev->dev), + ARRAY_SIZE(g_dpe_regulator), g_dpe_regulator); + if (ret) { + HISI_FB_ERR("failed to get regulator resource! ret=%d.\n", ret); + return -ENXIO; + } + */ + + ret = hisi_fb_enable_iommu(pdev); + if (ret != 0) { + HISI_FB_ERR("failed to hisi_fb_enable_iommu! ret=%d.\n", + ret); + return -ENXIO; + } + + hisi_fb_resource_initialized = 1; + hisi_fb_device_set_status0(DTS_FB_RESOURCE_INIT_READY); + + HISI_FB_DEBUG("initialized = %d, -.\n", + hisi_fb_resource_initialized); + return 0; + } + + if (pdev->id < 0) { + HISI_FB_ERR("WARNING: id=%d, name=%s!\n", pdev->id, pdev->name); + return 0; + } + + if (!hisi_fb_resource_initialized) { + HISI_FB_ERR("fb resource not initialized!\n"); + return -EPERM; + } + + if (pdev_list_cnt >= HISI_FB_MAX_DEV_LIST) { + HISI_FB_ERR("too many fb devices, num=%d!\n", pdev_list_cnt); + return -ENOMEM; + } + + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = hisi_fb_register(hisifd); + if (ret) { + HISI_FB_ERR("fb%d hisi_fb_register failed, error=%d!\n", + hisifd->index, ret); + return ret; + } + + pdev_list[pdev_list_cnt++] = pdev; + + /* set device probe status */ + hisi_fb_device_set_status1(hisifd); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return 0; +} + +static int hisi_fb_remove(struct platform_device *pdev) +{ + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + /* stop the device */ + if (hisi_fb_suspend_sub(hisifd) != 0) + HISI_FB_ERR("fb%d hisi_fb_suspend_sub failed!\n", + hisifd->index); + + /* overlay destroy */ + hisi_overlay_deinit(hisifd); + + /* free framebuffer */ + hisifb_free_fb_buffer(hisifd); + if (hisifd->ion_client) { + ion_client_destroy(hisifd->ion_client); + hisifd->ion_client = NULL; + } + + /* remove /dev/fb* */ + unregister_framebuffer(hisifd->fbi); + + /* unregister buf_sync */ + if (hisifd->buf_sync_unregister) + hisifd->buf_sync_unregister(pdev); + /* unregister vsync */ + if (hisifd->vsync_unregister) + hisifd->vsync_unregister(pdev); + /* unregister backlight */ + if (hisifd->bl_unregister) + hisifd->bl_unregister(pdev); + /* fb sysfs remove */ + if (hisifd->sysfs_remove_fnc) + hisifd->sysfs_remove_fnc(hisifd->pdev); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return 0; +} + +static int hisi_fb_suspend_sub(struct hisi_fb_data_type *hisifd) +{ + int ret = 0; + + BUG_ON(hisifd == NULL); + + ret = hisi_fb_blank_sub(FB_BLANK_POWERDOWN, hisifd->fbi); + if (ret) { + HISI_FB_ERR("fb%d can't turn off display, error=%d!\n", + hisifd->index, ret); + return ret; + } + + return 0; +} + +static int hisi_fb_resume_sub(struct hisi_fb_data_type *hisifd) +{ + int ret = 0; + + BUG_ON(hisifd == NULL); + + ret = hisi_fb_blank_sub(FB_BLANK_UNBLANK, hisifd->fbi); + if (ret) { + HISI_FB_ERR("fb%d can't turn on display, error=%d!\n", + hisifd->index, ret); + } + + return ret; +} + +#ifdef CONFIG_PM +static int hisi_fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_INFO("fb%d, +.\n", hisifd->index); + + console_lock(); + fb_set_suspend(hisifd->fbi, FBINFO_STATE_SUSPENDED); + ret = hisi_fb_suspend_sub(hisifd); + if (ret != 0) { + HISI_FB_ERR("fb%d hisi_fb_suspend_sub failed, error=%d!\n", + hisifd->index, ret); + fb_set_suspend(hisifd->fbi, FBINFO_STATE_RUNNING); + } else { + pdev->dev.power.power_state = state; + } + console_unlock(); + + HISI_FB_INFO("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int hisi_fb_resume(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_INFO("fb%d, +.\n", hisifd->index); + + console_lock(); + ret = hisi_fb_resume_sub(hisifd); + pdev->dev.power.power_state = PMSG_ON; + fb_set_suspend(hisifd->fbi, FBINFO_STATE_RUNNING); + console_unlock(); + + HISI_FB_INFO("fb%d, -.\n", hisifd->index); + + return ret; +} +#else +#define hisi_fb_suspend NULL +#define hisi_fb_resume NULL +#endif + +#ifdef CONFIG_PM_SLEEP +static int hisi_fb_pm_suspend(struct device *dev) +{ + struct hisi_fb_data_type *hisifd = NULL; + int ret = 0; + + if (NULL == dev) { + HISI_FB_ERR("NULL Poniter\n"); + return 0; + } + + hisifd = dev_get_drvdata(dev); + if (!hisifd) + return 0; + + if (hisifd->index != PRIMARY_PANEL_IDX) + return 0; + + HISI_FB_INFO("fb%d, +.\n", hisifd->index); + + ret = hisi_fb_suspend_sub(hisifd); + if (ret != 0) { + HISI_FB_ERR("fb%d, failed to hisi_fb_suspend_sub! ret=%d\n", + hisifd->index, ret); + } + + HISI_FB_INFO("fb%d, -.\n", hisifd->index); + + return 0; +} + +#if 0 +static int hisi_fb_pm_resume(struct device *dev) +{ + struct hisi_fb_data_type *hisifd = NULL; + int ret = 0; + + hisifd = dev_get_drvdata(dev); + if (!hisifd) + return 0; + + if (hisifd->index != PRIMARY_PANEL_IDX) + return 0; + + HISI_FB_INFO("fb%d, +.\n", hisifd->index); + + ret = hisi_fb_resume_sub(hisifd); + if (ret != 0) { + HISI_FB_ERR("fb%d, failed to hisi_fb_resume_sub! ret=%d\n", + hisifd->index, ret); + } + + HISI_FB_INFO("fb%d, -.\n", hisifd->index); + + return 0; +} +#endif +#endif + +static void hisi_fb_shutdown(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + if (NULL == pdev) { + HISI_FB_ERR("pdev NULL Pointer\n"); + return; + } + + hisifd = platform_get_drvdata(pdev); + if (!hisifd) { + if (pdev->id) { + HISI_FB_ERR("hisifd NULL Pointer,pdev->id=%d\n", + pdev->id); + } + return; + } + + if (hisifd->index != PRIMARY_PANEL_IDX) { + HISI_FB_DEBUG("fb%d do not shutdown\n", hisifd->index); + return; + } + + HISI_FB_INFO("fb%d shutdown +\n", hisifd->index); + hisifd->fb_shutdown = true; + + ret = hisi_fb_blank_sub(FB_BLANK_POWERDOWN, hisifd->fbi); + if (ret) { + HISI_FB_ERR("fb%d can't turn off display, error=%d!\n", + hisifd->index, ret); + } + + HISI_FB_INFO("fb%d shutdown -\n", hisifd->index); +} + +/******************************************************************************* + ** + */ +static struct dev_pm_ops hisi_fb_dev_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = hisi_fb_pm_suspend, + .resume = NULL, +#endif +}; + +static const struct of_device_id hisi_fb_match_table[] = { + { + .compatible = DTS_COMP_FB_NAME, + .data = NULL, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, hisi_fb_match_table); + +static struct platform_driver hisi_fb_driver = { + .probe = hisi_fb_probe, + .remove = hisi_fb_remove, + .suspend = hisi_fb_suspend, + .resume = hisi_fb_resume, + .shutdown = hisi_fb_shutdown, + .driver = { + .name = DEV_NAME_FB, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hisi_fb_match_table), + .pm = &hisi_fb_dev_pm_ops, + }, +}; + +static int __init hisi_fb_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&hisi_fb_driver); + if (ret) { + HISI_FB_ERR("platform_driver_register failed, error=%d!\n", + ret); + return ret; + } + + return ret; +} + +module_init(hisi_fb_init); + +MODULE_DESCRIPTION("Hisilicon Framebuffer Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fbdev/hisi/dss/hisi_fb.h b/drivers/video/fbdev/hisi/dss/hisi_fb.h new file mode 100755 index 000000000000..d13ca97797d7 --- /dev/null +++ b/drivers/video/fbdev/hisi/dss/hisi_fb.h @@ -0,0 +1,559 @@ +/* Copyright (c) 2013-2014, Hisilicon Tech. Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef HISI_FB_H +#define HISI_FB_H + +#include <linux/console.h> +#include <linux/uaccess.h> +#include <linux/leds.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/fb.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/raid/pq.h> +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif +#include <linux/time.h> +#include <linux/kthread.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/version.h> +#include <linux/backlight.h> +#include <linux/pwm.h> +#include <linux/pm_runtime.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/memblock.h> + +#include <linux/spi/spi.h> + +#include <linux/ion.h> +#include <linux/hisi/hisi_ion.h> +#include <linux/gpio.h> + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> + +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/pinctrl/consumer.h> +#include <linux/file.h> +#include <linux/dma-buf.h> +#include <linux/genalloc.h> +#include <linux/hisi/hisi-iommu.h> + + + +#include "hisi_fb_def.h" +#include "hisi_fb_panel.h" +#include "hisi_dss.h" +#include "hisi_mipi_dsi.h" +#include "hisi_overlay_cmdlist_utils.h" + +#include "hisi_dss_regs_hi3660.h" +#include "hisi_overlay_utils_hi3660.h" +#include "hisi_dpe_utils.h" +#include "hisi_overlay_utils.h" + +#define CONFIG_HISI_FB_BACKLIGHT_DELAY +#define CONFIG_BUF_SYNC_USED +#define CONFIG_FB_DEBUG_USED +#define CONFIG_SMMU_RWERRADDR_USED +#define CONFIG_DSS_MMBUF_CLK_USED +#define CONFIG_BACKLIGHT_2048 + +#define HISI_DSS_COMPOSER_HOLD_TIME (1000 * 3600 * 24 * 7) + +#define HISI_FB0_NUM (3) +#define HISI_FB1_NUM (0) +#define HISI_FB2_NUM (0) + +#define HISI_FB_SYSFS_ATTRS_NUM (64) + +#define HISI_FB_MAX_DEV_LIST (32) +#define HISI_FB_MAX_FBI_LIST (32) + +#define HISI_DSS_OFFLINE_MAX_BLOCK (64) +#define HISI_DSS_OFFLINE_MAX_LIST (128) + +#define ESD_CHECK_TIME_PERIOD (5000) + +struct hisifb_vsync { + wait_queue_head_t vsync_wait; + ktime_t vsync_timestamp; + int vsync_created; + int vsync_enabled; + int vsync_infinite; + int vsync_infinite_count; + + int vsync_ctrl_expire_count; + int vsync_ctrl_enabled; + int vsync_ctrl_disabled_set; + int vsync_ctrl_isr_enabled; + int vsync_ctrl_offline_enabled; + struct work_struct vsync_ctrl_work; + spinlock_t spin_lock; + + struct mutex vsync_lock; +#ifdef CONFIG_HISI_FB_VSYNC_THREAD + struct task_struct *vsync_thread; +#endif + + atomic_t buffer_updated; + void (*vsync_report_fnc) (int buffer_updated); + + struct hisi_fb_data_type *hisifd; +}; + +enum bl_control_mode { + REG_ONLY_MODE = 1, + PWM_ONLY_MODE, + MUTI_THEN_RAMP_MODE, + RAMP_THEN_MUTI_MODE, +}; + +enum ESD_RECOVER_STATE { + ESD_RECOVER_STATE_NONE = 0, + ESD_RECOVER_STATE_START = 1, + ESD_RECOVER_STATE_COMPLETE = 2, +}; + +/* esd func define */ +struct hisifb_esd { + int esd_inited; + struct hrtimer esd_hrtimer; + struct workqueue_struct *esd_check_wq; + struct work_struct esd_check_work; + struct task_struct *esd_handle_thread; + wait_queue_head_t esd_handle_wait; + + struct hisi_fb_data_type *hisifd; +}; + +#ifdef CONFIG_BUF_SYNC_USED +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) +#include "sync.h" +#include "sw_sync.h" +#else +#include <linux/sync.h> +#include <linux/sw_sync.h> +#endif +#endif + +struct hisifb_buf_sync { +#ifdef CONFIG_BUF_SYNC_USED + struct sw_sync_timeline *timeline; + int timeline_max; + int refresh; + spinlock_t refresh_lock; +#endif + struct workqueue_struct *free_layerbuf_queue; + struct work_struct free_layerbuf_work; + struct list_head layerbuf_list; + bool layerbuf_flushed; + spinlock_t layerbuf_spinlock; +}; + +struct hisifb_layerbuf { + struct ion_handle *ion_handle; + struct list_head list_node; + int timeline; + bool has_map_iommu; + + int32_t shared_fd; + uint32_t frame_no; + dss_mmbuf_t mmbuf; + uint64_t vir_addr; + int32_t chn_idx; +}; + +struct hisifb_backlight { +#ifdef CONFIG_HISI_FB_BACKLIGHT_DELAY + struct delayed_work bl_worker; +#endif + struct semaphore bl_sem; + int bl_updated; + int bl_level_old; + int frame_updated; + + struct workqueue_struct *sbl_queue; + struct work_struct sbl_work; +}; + +struct hisi_fb_data_type { + uint32_t index; + uint32_t ref_cnt; + uint32_t fb_num; + uint32_t fb_imgType; + uint32_t bl_level; + + char __iomem *dss_base; + char __iomem *peri_crg_base; + char __iomem *sctrl_base; + char __iomem *pctrl_base; + char __iomem *noc_dss_base; + char __iomem *mmbuf_crg_base; + char __iomem *pmctrl_base; + char __iomem *mmbuf_asc0_base; + char __iomem *mipi_dsi0_base; + char __iomem *mipi_dsi1_base; + + uint32_t dss_base_phy; + + uint32_t dpe_irq; + uint32_t dsi0_irq; + uint32_t dsi1_irq; + uint32_t mmbuf_asc0_irq; + + struct regulator_bulk_data *dpe_regulator; + struct regulator_bulk_data *mmbuf_regulator; + + const char *dss_axi_clk_name; + const char *dss_pclk_dss_name; + const char *dss_pri_clk_name; + const char *dss_pxl0_clk_name; + const char *dss_pxl1_clk_name; + const char *dss_mmbuf_clk_name; + const char *dss_pclk_mmbuf_name; + const char *dss_dphy0_ref_clk_name; + const char *dss_dphy1_ref_clk_name; + const char *dss_dphy0_cfg_clk_name; + const char *dss_dphy1_cfg_clk_name; + const char *dss_pclk_dsi0_name; + const char *dss_pclk_dsi1_name; + const char *dss_pclk_pctrl_name; + + struct clk *dss_axi_clk; + struct clk *dss_pclk_dss_clk; + struct clk *dss_pri_clk; + struct clk *dss_pxl0_clk; + struct clk *dss_pxl1_clk; + struct clk *dss_mmbuf_clk; + struct clk *dss_pclk_mmbuf_clk; + struct clk *dss_dphy0_ref_clk; + struct clk *dss_dphy1_ref_clk; + struct clk *dss_dphy0_cfg_clk; + struct clk *dss_dphy1_cfg_clk; + struct clk *dss_pclk_dsi0_clk; + struct clk *dss_pclk_dsi1_clk; + struct clk *dss_pclk_pctrl_clk; + + struct hisi_panel_info panel_info; + bool panel_power_on; + bool fb_shutdown; + bool lcd_self_testing; + bool video_ldi_dis_at_vac_start; + unsigned int aod_function; + + struct semaphore blank_sem; + struct semaphore blank_sem0; + struct semaphore offline_composer_sr_sem; + uint32_t offline_composer_sr_refcount; + + void (*sysfs_attrs_append_fnc) (struct hisi_fb_data_type *hisifd, + struct attribute *attr); + int (*sysfs_create_fnc) (struct platform_device *pdev); + void (*sysfs_remove_fnc) (struct platform_device *pdev); + void (*pm_runtime_register) (struct platform_device *pdev); + void (*pm_runtime_unregister) (struct platform_device *pdev); + void (*pm_runtime_get) (struct hisi_fb_data_type *hisifd); + void (*pm_runtime_put) (struct hisi_fb_data_type *hisifd); + void (*bl_register) (struct platform_device *pdev); + void (*bl_unregister) (struct platform_device *pdev); + void (*bl_update) (struct hisi_fb_data_type *hisifd); + void (*bl_cancel) (struct hisi_fb_data_type *hisifd); + void (*vsync_register) (struct platform_device *pdev); + void (*vsync_unregister) (struct platform_device *pdev); + int (*vsync_ctrl_fnc) (struct fb_info *info, void __user *argp); + void (*vsync_isr_handler) (struct hisi_fb_data_type *hisifd); + void (*secure_register) (struct platform_device *pdev); + void (*secure_unregister) (struct platform_device *pdev); + void (*buf_sync_register) (struct platform_device *pdev); + void (*buf_sync_unregister) (struct platform_device *pdev); + void (*buf_sync_signal) (struct hisi_fb_data_type *hisifd); + void (*buf_sync_suspend) (struct hisi_fb_data_type *hisifd); + void (*esd_register) (struct platform_device *pdev); + void (*esd_unregister) (struct platform_device *pdev); + void (*debug_register) (struct platform_device *pdev); + void (*debug_unregister) (struct platform_device *pdev); + int (*cabc_update) (struct hisi_fb_data_type *hisifd); + + bool(*set_fastboot_fnc) (struct fb_info *info); + int (*open_sub_fnc) (struct fb_info *info); + int (*release_sub_fnc) (struct fb_info *info); + int (*on_fnc) (struct hisi_fb_data_type *hisifd); + int (*off_fnc) (struct hisi_fb_data_type *hisifd); + int (*lp_fnc) (struct hisi_fb_data_type *hisifd, bool lp_enter); + int (*esd_fnc) (struct hisi_fb_data_type *hisifd); + int (*sbl_ctrl_fnc) (struct fb_info *info, int value); + void (*sbl_isr_handler) (struct hisi_fb_data_type *hisifd); + int (*mipi_dsi_bit_clk_upt_isr_handler) (struct hisi_fb_data_type *hisifd); + void (*crc_isr_handler) (struct hisi_fb_data_type *hisifd); + void (*ov_ldi_underflow_isr_handle) (struct hisi_fb_data_type *hisifd); + + int (*pan_display_fnc) (struct hisi_fb_data_type *hisifd); + int (*ov_ioctl_handler) (struct hisi_fb_data_type *hisifd, + uint32_t cmd, void __user *argp); + int (*ov_online_play) (struct hisi_fb_data_type *hisifd, + void __user *argp); + void (*ov_wb_isr_handler) (struct hisi_fb_data_type *hisifd); + void (*ov_vactive0_start_isr_handler) (struct hisi_fb_data_type *hisifd); + void (*set_reg) (struct hisi_fb_data_type *hisifd, char __iomem *addr, + uint32_t val, uint8_t bw, uint8_t bs); + + void (*sysfs_attrs_add_fnc) (struct hisi_fb_data_type *hisifd); + + struct hisifb_backlight backlight; + int sbl_enable; + int sbl_lsensor_value; + int sbl_level; + dss_sbl_t sbl; + int color_temperature_flag; + + int sysfs_index; + struct attribute *sysfs_attrs[HISI_FB_SYSFS_ATTRS_NUM]; + struct attribute_group sysfs_attr_group; + + struct hisifb_vsync vsync_ctrl; + struct hisifb_buf_sync buf_sync_ctrl; + struct dss_clk_rate dss_clk_rate; + struct hisifb_esd esd_ctrl; + +#ifdef CONFIG_FAKE_VSYNC_USED + bool fake_vsync_used; + struct hrtimer fake_vsync_hrtimer; +#endif + dss_module_reg_t dss_module; + dss_overlay_t ov_req; + dss_overlay_block_t ov_block_infos[HISI_DSS_OV_BLOCK_NUMS]; + dss_overlay_t ov_req_prev; + dss_overlay_block_t ov_block_infos_prev[HISI_DSS_OV_BLOCK_NUMS]; + dss_overlay_t ov_req_prev_prev; + dss_overlay_block_t ov_block_infos_prev_prev[HISI_DSS_OV_BLOCK_NUMS]; + + dss_rect_t *ov_block_rects[HISI_DSS_OFFLINE_MAX_BLOCK]; + dss_wb_info_t wb_info; + + dss_cmdlist_data_t *cmdlist_data_tmp[HISI_DSS_CMDLIST_DATA_MAX]; + dss_cmdlist_data_t *cmdlist_data; + dss_cmdlist_info_t *cmdlist_info; + int32_t cmdlist_idx; + + dss_copybit_info_t *copybit_info; + + struct gen_pool *mmbuf_gen_pool; + dss_mmbuf_info_t mmbuf_infos[HISI_DSS_CMDLIST_DATA_MAX]; + dss_mmbuf_info_t *mmbuf_info; + struct list_head *mmbuf_list; + + bool dss_module_resource_initialized; + dss_module_reg_t dss_module_default; + + struct dss_rect dirty_region_updt; + uint32_t esd_happened; + uint32_t esd_recover_state; + + struct ion_client *ion_client; + struct ion_handle *ion_handle; + struct iommu_map_format iommu_format; + struct iommu_domain *hisi_domain; + + struct fb_info *fbi; + struct platform_device *pdev; + + wait_queue_head_t vactive0_start_wq; + uint32_t vactive0_start_flag; + uint32_t vactive0_end_flag; + uint32_t ldi_data_gate_en; + + wait_queue_head_t crc_wq; + uint32_t crc_flag; + struct workqueue_struct *dss_debug_wq; + struct work_struct dss_debug_work; + + struct workqueue_struct *ldi_underflow_wq; + struct work_struct ldi_underflow_work; + struct workqueue_struct *rch2_ce_end_wq; + struct work_struct rch2_ce_end_work; + struct workqueue_struct *rch4_ce_end_wq; + struct work_struct rch4_ce_end_work; + struct workqueue_struct *dpp_ce_end_wq; + struct work_struct dpp_ce_end_work; + struct workqueue_struct *hiace_end_wq; + struct work_struct hiace_end_work; + + dss_rect_t res_updt_rect; + dss_rect_t resolution_rect; + + uint32_t frame_count; + uint32_t frame_update_flag; + bool fb_mem_free_flag; + + uint8_t core_clk_upt_support; + + uint32_t vactive_start_event; + + uint32_t vsync_ctrl_type; + struct notifier_block nb; + struct notifier_block lcd_int_nb; +}; + +/****************************************************************************** + ** FUNCTIONS PROTOTYPES + */ +extern int g_primary_lcd_xres; +extern int g_primary_lcd_yres; +extern uint64_t g_pxl_clk_rate; + +extern uint32_t g_online_cmdlist_idxs; +extern uint32_t g_offline_cmdlist_idxs; + +extern uint32_t g_dss_version_tag; +extern uint32_t g_dss_module_resource_initialized; +extern uint32_t g_logo_buffer_base; +extern uint32_t g_logo_buffer_size; +extern uint32_t g_underflow_stop_perf_stat; + +/* for debug */ +extern int g_debug_ldi_underflow; +extern int g_debug_ldi_underflow_clear; + +extern int g_debug_mmu_error; +extern int g_debug_set_reg_val; +extern int g_debug_online_vsync; +extern int g_debug_ovl_online_composer; +extern int g_debug_ovl_online_composer_hold; +extern int g_debug_ovl_online_composer_return; +extern int g_debug_ovl_online_composer_timediff; +extern int g_debug_ovl_online_composer_time_threshold; + +extern int g_debug_ovl_offline_composer; +extern int g_debug_ovl_block_composer; +extern int g_debug_ovl_offline_composer_hold; +extern int g_debug_ovl_offline_composer_timediff; +extern int g_debug_ovl_offline_composer_time_threshold; +extern int g_debug_ovl_offline_block_num; +extern int g_debug_ovl_copybit_composer; +extern int g_debug_ovl_copybit_composer_hold; +extern int g_debug_ovl_copybit_composer_timediff; +extern int g_debug_ovl_copybit_composer_time_threshold; + +extern int g_debug_ovl_cmdlist; +extern int g_dump_cmdlist_content; +extern int g_enable_ovl_cmdlist_online; +extern int g_enable_ovl_cmdlist_offline; +extern int g_rdma_stretch_threshold; +extern int g_enable_dirty_region_updt; +extern int g_debug_dirty_region_updt; +extern int g_enable_crc_debug; +extern int g_ldi_data_gate_en; +extern int g_debug_need_save_file; +extern int g_debug_ovl_credit_step; +extern int g_debug_layerbuf_sync; +extern int g_enable_dss_idle; +extern int g_debug_dump_mmbuf; +extern uint32_t g_mmbuf_addr_test; +extern uint32_t g_dss_min_bandwidth_inbusbusy; + +extern int g_err_status; +extern int g_debug_enable_lcd_sleep_in; + +extern struct fb_info *fbi_list[HISI_FB_MAX_FBI_LIST]; +extern struct hisi_fb_data_type *hisifd_list[HISI_FB_MAX_FBI_LIST]; + +uint32_t get_panel_xres(struct hisi_fb_data_type *hisifd); +uint32_t get_panel_yres(struct hisi_fb_data_type *hisifd); + +bool is_dss_idle_enable(void); + +/* fb buffer */ +unsigned long hisifb_alloc_fb_buffer(struct hisi_fb_data_type *hisifd); +void hisifb_free_fb_buffer(struct hisi_fb_data_type *hisifd); +void hisifb_free_logo_buffer(struct hisi_fb_data_type *hisifd); + +int hisi_fb_blank_sub(int blank_mode, struct fb_info *info); + +/* backlight */ +void hisifb_backlight_update(struct hisi_fb_data_type *hisifd); +void hisifb_backlight_cancel(struct hisi_fb_data_type *hisifd); +void hisifb_backlight_register(struct platform_device *pdev); +void hisifb_backlight_unregister(struct platform_device *pdev); +void hisifb_set_backlight(struct hisi_fb_data_type *hisifd, uint32_t bkl_lvl); + +/* vsync */ +void hisifb_frame_updated(struct hisi_fb_data_type *hisifd); +#ifdef CONFIG_FAKE_VSYNC_USED +enum hrtimer_restart hisifb_fake_vsync(struct hrtimer *timer); +#endif +void hisifb_set_vsync_activate_state(struct hisi_fb_data_type *hisifd, + bool infinite); +void hisifb_activate_vsync(struct hisi_fb_data_type *hisifd); +void hisifb_deactivate_vsync(struct hisi_fb_data_type *hisifd); +int hisifb_vsync_ctrl(struct fb_info *info, void __user *argp); +int hisifb_vsync_resume(struct hisi_fb_data_type *hisifd); +int hisifb_vsync_suspend(struct hisi_fb_data_type *hisifd); +void hisifb_vsync_isr_handler(struct hisi_fb_data_type *hisifd); +void hisifb_vsync_register(struct platform_device *pdev); +void hisifb_vsync_unregister(struct platform_device *pdev); +/* buffer sync */ +int hisifb_layerbuf_lock(struct hisi_fb_data_type *hisifd, + dss_overlay_t *pov_req, struct list_head *lock_list); +void hisifb_layerbuf_flush(struct hisi_fb_data_type *hisifd, + struct list_head *lock_list); +void hisifb_layerbuf_unlock(struct hisi_fb_data_type *hisifd, + struct list_head *pfree_list); +void hisifb_layerbuf_lock_exception(struct hisi_fb_data_type *hisifd, + struct list_head *lock_list); + +int hisifb_buf_sync_wait(int fence_fd); +int hisifb_buf_sync_handle(struct hisi_fb_data_type *hisifd, + dss_overlay_t *pov_req); +void hisifb_buf_sync_signal(struct hisi_fb_data_type *hisifd); +void hisifb_buf_sync_suspend(struct hisi_fb_data_type *hisifd); +int hisifb_buf_sync_create_fence(struct hisi_fb_data_type *hisifd, + unsigned value); +void hisifb_buf_sync_register(struct platform_device *pdev); +void hisifb_buf_sync_unregister(struct platform_device *pdev); + +/* control */ +int hisifb_ctrl_on(struct hisi_fb_data_type *hisifd); +int hisifb_ctrl_off(struct hisi_fb_data_type *hisifd); +int hisifb_ctrl_dss_clk_rate_set(struct fb_info *info, void __user *argp); +void hisifb_sysfs_attrs_add(struct hisi_fb_data_type *hisifd); + +void set_reg(char __iomem *addr, uint32_t val, uint8_t bw, uint8_t bs); +uint32_t set_bits32(uint32_t old_val, uint32_t val, uint8_t bw, uint8_t bs); +void hisifb_set_reg(struct hisi_fb_data_type *hisifd, + char __iomem *addr, uint32_t val, uint8_t bw, uint8_t bs); +uint32_t hisifb_line_length(int index, uint32_t xres, int bpp); +void hisifb_get_timestamp(struct timeval *tv); +uint32_t hisifb_timestamp_diff(struct timeval *lasttime, + struct timeval *curtime); +void hisifb_save_file(char *filename, char *buf, uint32_t buf_len); +struct platform_device *hisi_fb_device_alloc(struct hisi_fb_panel_data *pdata, + uint32_t type, uint32_t id); +struct platform_device *hisi_fb_add_device(struct platform_device *pdev); +#endif /* HISI_FB_H */ diff --git a/drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.c b/drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.c new file mode 100755 index 000000000000..54c43a3e85a5 --- /dev/null +++ b/drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.c @@ -0,0 +1,1686 @@ +/* Copyright (c) 2013-2014, Hisilicon Tech. Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hisi_mipi_dsi.h" + +#define DEFAULT_MAX_TX_ESC_CLK (10 * 1000000UL) +#define DEFAULT_MIPI_CLK_RATE (192 * 100000L) +#define DEFAULT_PCLK_DSI_RATE (120 * 1000000L) + +#define ROUND1(x,y) ((x) / (y) + ((x) % (y) > 0 ? 1 : 0)) +#define DSS_REDUCE(x) ((x) > 0 ? ((x) - 1) : (x)) + +struct dsi_phy_seq_info { + uint32_t min_range; + uint32_t max_range; + uint32_t rg_pll_vco_750M; + uint32_t rg_hstx_ckg_sel; +}; + +struct dsi_phy_seq_info dphy_seq_info[] = { + {47, 94, 0, 7}, + {94, 188, 0, 6}, + {188, 375, 0, 5}, + {375, 750, 0, 4}, + {750, 1500, 0, 0} +}; + +static void get_dsi_phy_ctrl(struct hisi_fb_data_type *hisifd, + struct mipi_dsi_phy_ctrl *phy_ctrl) +{ + struct hisi_panel_info *pinfo = NULL; + uint32_t dsi_bit_clk = 0; + + uint32_t ui = 0; + uint32_t m_pll = 0; + uint32_t n_pll = 0; + uint32_t m_n_fract = 0; + uint32_t m_n_int = 0; + uint64_t lane_clock = 0; + uint64_t vco_div = 1; + + uint32_t accuracy = 0; + uint32_t unit_tx_byte_clk_hs = 0; + uint32_t clk_post = 0; + uint32_t clk_pre = 0; + uint32_t clk_t_hs_exit = 0; + uint32_t clk_pre_delay = 0; + uint32_t clk_t_hs_prepare = 0; + uint32_t clk_t_lpx = 0; + uint32_t clk_t_hs_zero = 0; + uint32_t clk_t_hs_trial = 0; + uint32_t data_post_delay = 0; + uint32_t data_t_hs_prepare = 0; + uint32_t data_t_hs_zero = 0; + uint32_t data_t_hs_trial = 0; + uint32_t data_t_lpx = 0; + uint32_t clk_pre_delay_reality = 0; + uint32_t clk_t_hs_zero_reality = 0; + uint32_t clk_post_delay_reality = 0; + uint32_t data_t_hs_zero_reality = 0; + uint32_t data_post_delay_reality = 0; + uint32_t data_pre_delay_reality = 0; + + BUG_ON(phy_ctrl == NULL); + BUG_ON(hisifd == NULL); + pinfo = &(hisifd->panel_info); + + dsi_bit_clk = pinfo->mipi.dsi_bit_clk_upt; + lane_clock = 2 * dsi_bit_clk; + HISI_FB_DEBUG("Expected : lane_clock = %llu M\n", lane_clock); + + /************************ PLL parameters config *********************/ + if ((320 <= lane_clock) && (lane_clock <= 2500)) { + phy_ctrl->rg_band_sel = 0; + vco_div = 1; + } else if ((80 <= lane_clock) && (lane_clock < 320)) { + phy_ctrl->rg_band_sel = 1; + vco_div = 4; + } else { + HISI_FB_ERR + ("80M <= lane_clock< = 2500M, not support lane_clock = %llu M\n", + lane_clock); + } + + m_n_int = lane_clock * vco_div * 1000000UL / DEFAULT_MIPI_CLK_RATE; + m_n_fract = ((lane_clock * vco_div * 1000000UL * 1000UL / + DEFAULT_MIPI_CLK_RATE) % 1000) * 10 / 1000; + + if (m_n_int % 2 == 0) { + if (m_n_fract * 6 >= 50) { + n_pll = 2; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n_fract * 6 >= 30) { + n_pll = 3; + m_pll = m_n_int * n_pll + 2; + } else { + n_pll = 1; + m_pll = m_n_int * n_pll; + } + } else { + if (m_n_fract * 6 >= 50) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n_fract * 6 >= 30) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n_fract * 6 >= 10) { + n_pll = 3; + m_pll = m_n_int * n_pll + 1; + } else { + n_pll = 2; + m_pll = m_n_int * n_pll; + } + } + + if (m_pll <= 8) { + phy_ctrl->rg_pll_fbd_s = 1; + phy_ctrl->rg_pll_enswc = 0; + + if (m_pll % 2 == 0) { + phy_ctrl->rg_pll_fbd_p = m_pll / 2; + } else { + if (n_pll == 1) { + n_pll *= 2; + phy_ctrl->rg_pll_fbd_p = (m_pll * 2) / 2; + } else { + HISI_FB_ERR + ("phy m_pll not support!m_pll = %d\n", m_pll); + return; + } + } + } else if (m_pll <= 300) { + if (m_pll % 2 == 0) { + phy_ctrl->rg_pll_enswc = 0; + } else { + phy_ctrl->rg_pll_enswc = 1; + } + phy_ctrl->rg_pll_fbd_s = 1; + phy_ctrl->rg_pll_fbd_p = m_pll / 2; + } else if (m_pll <= 315) { + phy_ctrl->rg_pll_fbd_p = 150; + phy_ctrl->rg_pll_fbd_s = m_pll - 2 * phy_ctrl->rg_pll_fbd_p; + phy_ctrl->rg_pll_enswc = 1; + } else { + HISI_FB_ERR("phy m_pll not support!m_pll = %d\n", m_pll); + return; + } + + phy_ctrl->rg_pll_pre_p = n_pll; + + lane_clock = m_pll * (DEFAULT_MIPI_CLK_RATE / n_pll) / vco_div; + HISI_FB_DEBUG("Config : lane_clock = %llu\n", lane_clock); + + phy_ctrl->rg_pll_cp = 1; + phy_ctrl->rg_pll_cp_p = 3; + + phy_ctrl->rg_pll_enbwt = 0; + phy_ctrl->rg_pll_chp = 0; + + phy_ctrl->rg_pll_lpf_cs = 0; + phy_ctrl->rg_pll_refsel = 1; + + phy_ctrl->reload_sel = 1; + phy_ctrl->rg_phase_gen_en = 1; + phy_ctrl->pll_power_down = 0; + phy_ctrl->pll_register_override = 1; + + phy_ctrl->rg_vrefsel_vcm = 0x55; + if (pinfo->mipi.rg_vrefsel_vcm_clk_adjust != 0) + phy_ctrl->rg_vrefsel_vcm = (phy_ctrl->rg_vrefsel_vcm & 0x0F) | + ((pinfo->mipi.rg_vrefsel_vcm_clk_adjust & 0x0F) << 4); + + if (pinfo->mipi.rg_vrefsel_vcm_data_adjust != 0) + phy_ctrl->rg_vrefsel_vcm = (phy_ctrl->rg_vrefsel_vcm & 0xF0) | + (pinfo->mipi.rg_vrefsel_vcm_data_adjust & 0x0F); + + phy_ctrl->load_command = 0x5A; + + /******************** clock/data lane parameters config ******************/ + accuracy = 10; + ui = 10 * 1000000000UL * accuracy / lane_clock; + unit_tx_byte_clk_hs = 8 * ui; + + clk_post = 600 * accuracy + 52 * ui + pinfo->mipi.clk_post_adjust * ui; + + clk_pre = 8 * ui + pinfo->mipi.clk_pre_adjust * ui; + + clk_t_hs_exit = 1000 * accuracy + pinfo->mipi.clk_t_hs_exit_adjust * ui; + + clk_pre_delay = 0 + pinfo->mipi.clk_pre_delay_adjust * ui; + + clk_t_hs_trial = + 600 * accuracy + 3 * unit_tx_byte_clk_hs + + pinfo->mipi.clk_t_hs_trial_adjust * ui; + + if (pinfo->mipi.clk_t_hs_prepare_adjust == 0) + pinfo->mipi.clk_t_hs_prepare_adjust = 43; + + clk_t_hs_prepare = + ((380 * accuracy + pinfo->mipi.clk_t_hs_prepare_adjust * ui) <= + (950 * accuracy - 8 * ui)) ? (380 * accuracy + + pinfo->mipi.clk_t_hs_prepare_adjust * + ui) : (950 * accuracy - 8 * ui); + + data_post_delay = 0 + pinfo->mipi.data_post_delay_adjust * ui; + + data_t_hs_trial = + ((600 * accuracy + 4 * ui) >= + (8 * ui) ? (600 * accuracy + 4 * ui) : (8 * ui)) + 8 * ui + + 3 * unit_tx_byte_clk_hs + pinfo->mipi.data_t_hs_trial_adjust * ui; + + if (pinfo->mipi.data_t_hs_prepare_adjust == 0) + pinfo->mipi.data_t_hs_prepare_adjust = 35; + + data_t_hs_prepare = + ((400 * accuracy + 4 * ui + + pinfo->mipi.data_t_hs_prepare_adjust * ui) <= + (850 * accuracy + 6 * ui - 8 * ui)) ? (400 * accuracy + 4 * ui + + pinfo->mipi.data_t_hs_prepare_adjust * + ui) : (850 * accuracy + 6 * ui - 8 * ui); + + clk_t_lpx = (((2000 * accuracy - clk_t_hs_prepare) >= 500 * accuracy) ? + ((2000 * accuracy - clk_t_hs_prepare)) : (500 * accuracy)) + + pinfo->mipi.clk_t_lpx_adjust * ui; + + clk_t_hs_zero = + 3000 * accuracy - clk_t_hs_prepare + 3 * unit_tx_byte_clk_hs + + pinfo->mipi.clk_t_hs_zero_adjust * ui; + + data_t_lpx = clk_t_lpx + pinfo->mipi.data_t_lpx_adjust * ui; + + data_t_hs_zero = 1450 * accuracy + 10 * ui - data_t_hs_prepare + + 3 * unit_tx_byte_clk_hs + pinfo->mipi.data_t_hs_zero_adjust * ui; + + phy_ctrl->clk_pre_delay = ROUND1(clk_pre_delay, unit_tx_byte_clk_hs); + phy_ctrl->clk_t_hs_prepare = + ROUND1(clk_t_hs_prepare, unit_tx_byte_clk_hs); + phy_ctrl->clk_t_lpx = ROUND1(clk_t_lpx, unit_tx_byte_clk_hs); + phy_ctrl->clk_t_hs_zero = ROUND1(clk_t_hs_zero, unit_tx_byte_clk_hs); + phy_ctrl->clk_t_hs_trial = ROUND1(clk_t_hs_trial, unit_tx_byte_clk_hs); + + phy_ctrl->data_post_delay = + ROUND1(data_post_delay, unit_tx_byte_clk_hs); + phy_ctrl->data_t_hs_prepare = + ROUND1(data_t_hs_prepare, unit_tx_byte_clk_hs); + phy_ctrl->data_t_lpx = ROUND1(data_t_lpx, unit_tx_byte_clk_hs); + phy_ctrl->data_t_hs_zero = ROUND1(data_t_hs_zero, unit_tx_byte_clk_hs); + phy_ctrl->data_t_hs_trial = + ROUND1(data_t_hs_trial, unit_tx_byte_clk_hs); + phy_ctrl->data_t_ta_go = 4; + phy_ctrl->data_t_ta_get = 5; + + clk_pre_delay_reality = phy_ctrl->clk_pre_delay + 2; + clk_t_hs_zero_reality = phy_ctrl->clk_t_hs_zero + 8; + data_t_hs_zero_reality = phy_ctrl->data_t_hs_zero + 4; + data_post_delay_reality = phy_ctrl->data_post_delay + 4; + + phy_ctrl->clk_post_delay = + phy_ctrl->data_t_hs_trial + ROUND1(clk_post, unit_tx_byte_clk_hs); + phy_ctrl->data_pre_delay = + clk_pre_delay_reality + phy_ctrl->clk_t_lpx + + phy_ctrl->clk_t_hs_prepare + clk_t_hs_zero_reality + + ROUND1(clk_pre, unit_tx_byte_clk_hs); + + clk_post_delay_reality = phy_ctrl->clk_post_delay + 4; + data_pre_delay_reality = phy_ctrl->data_pre_delay + 2; + + phy_ctrl->clk_lane_lp2hs_time = + clk_pre_delay_reality + phy_ctrl->clk_t_lpx + + phy_ctrl->clk_t_hs_prepare + clk_t_hs_zero_reality + 3; + phy_ctrl->clk_lane_hs2lp_time = + clk_post_delay_reality + phy_ctrl->clk_t_hs_trial + 3; + phy_ctrl->data_lane_lp2hs_time = + data_pre_delay_reality + phy_ctrl->data_t_lpx + + phy_ctrl->data_t_hs_prepare + data_t_hs_zero_reality + 3; + phy_ctrl->data_lane_hs2lp_time = + data_post_delay_reality + phy_ctrl->data_t_hs_trial + 3; + phy_ctrl->phy_stop_wait_time = + clk_post_delay_reality + phy_ctrl->clk_t_hs_trial + + ROUND1(clk_t_hs_exit, unit_tx_byte_clk_hs) - + (data_post_delay_reality + phy_ctrl->data_t_hs_trial) + 3; + + phy_ctrl->lane_byte_clk = lane_clock / 8; + phy_ctrl->clk_division = + (((phy_ctrl->lane_byte_clk / 2) % pinfo->mipi.max_tx_esc_clk) > + 0) ? (phy_ctrl->lane_byte_clk / 2 / pinfo->mipi.max_tx_esc_clk + + 1) : (phy_ctrl->lane_byte_clk / 2 / + pinfo->mipi.max_tx_esc_clk); + + HISI_FB_DEBUG("PHY clock_lane and data_lane config : \n" + "rg_vrefsel_vcm=%u\n" + "clk_pre_delay=%u\n" + "clk_post_delay=%u\n" + "clk_t_hs_prepare=%u\n" + "clk_t_lpx=%u\n" + "clk_t_hs_zero=%u\n" + "clk_t_hs_trial=%u\n" + "data_pre_delay=%u\n" + "data_post_delay=%u\n" + "data_t_hs_prepare=%u\n" + "data_t_lpx=%u\n" + "data_t_hs_zero=%u\n" + "data_t_hs_trial=%u\n" + "data_t_ta_go=%u\n" + "data_t_ta_get=%u\n", + phy_ctrl->rg_vrefsel_vcm, + phy_ctrl->clk_pre_delay, + phy_ctrl->clk_post_delay, + phy_ctrl->clk_t_hs_prepare, + phy_ctrl->clk_t_lpx, + phy_ctrl->clk_t_hs_zero, + phy_ctrl->clk_t_hs_trial, + phy_ctrl->data_pre_delay, + phy_ctrl->data_post_delay, + phy_ctrl->data_t_hs_prepare, + phy_ctrl->data_t_lpx, + phy_ctrl->data_t_hs_zero, + phy_ctrl->data_t_hs_trial, + phy_ctrl->data_t_ta_go, phy_ctrl->data_t_ta_get); + HISI_FB_DEBUG("clk_lane_lp2hs_time=%u\n" + "clk_lane_hs2lp_time=%u\n" + "data_lane_lp2hs_time=%u\n" + "data_lane_hs2lp_time=%u\n" + "phy_stop_wait_time=%u\n", + phy_ctrl->clk_lane_lp2hs_time, + phy_ctrl->clk_lane_hs2lp_time, + phy_ctrl->data_lane_lp2hs_time, + phy_ctrl->data_lane_hs2lp_time, + phy_ctrl->phy_stop_wait_time); +} + +static uint32_t mipi_pixel_clk(struct hisi_fb_data_type *hisifd) +{ + struct hisi_panel_info *pinfo = NULL; + + BUG_ON(hisifd == NULL); + pinfo = &(hisifd->panel_info); + + if (pinfo->pxl_clk_rate_div == 0) { + return pinfo->pxl_clk_rate; + } + + if ((pinfo->ifbc_type == IFBC_TYPE_NONE) && !is_dual_mipi_panel(hisifd)) { + pinfo->pxl_clk_rate_div = 1; + } + + return pinfo->pxl_clk_rate / pinfo->pxl_clk_rate_div; +} + +static void mipi_init(struct hisi_fb_data_type *hisifd, char __iomem *mipi_dsi_base) +{ + uint32_t hline_time = 0; + uint32_t hsa_time = 0; + uint32_t hbp_time = 0; + uint64_t pixel_clk = 0; + uint32_t i = 0; + unsigned long dw_jiffies = 0; + uint32_t tmp = 0; + bool is_ready = false; + struct hisi_panel_info *pinfo = NULL; + dss_rect_t rect; + uint32_t cmp_stopstate_val = 0; + + BUG_ON(hisifd == NULL); + BUG_ON(mipi_dsi_base == NULL); + + pinfo = &(hisifd->panel_info); + + if (pinfo->mipi.max_tx_esc_clk == 0) { + HISI_FB_ERR("fb%d, max_tx_esc_clk is invalid!", hisifd->index); + pinfo->mipi.max_tx_esc_clk = DEFAULT_MAX_TX_ESC_CLK; + } + + memset(&(pinfo->dsi_phy_ctrl), 0, sizeof(struct mipi_dsi_phy_ctrl)); + get_dsi_phy_ctrl(hisifd, &(pinfo->dsi_phy_ctrl)); + + rect.x = 0; + rect.y = 0; + rect.w = pinfo->xres; + rect.h = pinfo->yres; + + mipi_ifbc_get_rect(hisifd, &rect); + + /*************************Configure the DPHY start*************************/ + set_reg(mipi_dsi_base + MIPIDSI_PHY_IF_CFG_OFFSET, + pinfo->mipi.lane_nums, 2, 0); + set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, + pinfo->dsi_phy_ctrl.clk_division, 8, 0); + set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, + pinfo->dsi_phy_ctrl.clk_division, 8, 8); + + outp32(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000001); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010014); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + (pinfo->dsi_phy_ctrl.rg_pll_fbd_s << 4) + + (pinfo->dsi_phy_ctrl.rg_pll_enswc << 3) + + (pinfo->dsi_phy_ctrl.rg_pll_enbwt << 2) + + pinfo->dsi_phy_ctrl.rg_pll_chp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010015); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + pinfo->dsi_phy_ctrl.rg_pll_fbd_p); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010016); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + (pinfo->dsi_phy_ctrl.rg_pll_cp << 5) + + (pinfo->dsi_phy_ctrl.rg_pll_lpf_cs << 4) + + pinfo->dsi_phy_ctrl.rg_pll_refsel); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010017); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + pinfo->dsi_phy_ctrl.rg_pll_pre_p); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x0001001D); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + pinfo->dsi_phy_ctrl.rg_vrefsel_vcm); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x0001001E); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + (pinfo->dsi_phy_ctrl.rg_pll_cp_p << 5) + + (pinfo->dsi_phy_ctrl.reload_sel << 4) + + (pinfo->dsi_phy_ctrl.rg_phase_gen_en << 3) + + (pinfo->dsi_phy_ctrl.rg_band_sel << 2) + + (pinfo->dsi_phy_ctrl.pll_power_down << 1) + + pinfo->dsi_phy_ctrl.pll_register_override); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x0001001F); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + pinfo->dsi_phy_ctrl.load_command); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010020); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.clk_pre_delay)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010021); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.clk_post_delay)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010022); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.clk_t_lpx)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010023); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.clk_t_hs_prepare)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010024); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.clk_t_hs_zero)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, 0x00010025); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + pinfo->dsi_phy_ctrl.clk_t_hs_trial); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); + + for (i = 0; i <= pinfo->mipi.lane_nums; i++) { + tmp = 0x10030 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_pre_delay)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10031 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_post_delay)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10032 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_t_lpx)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10033 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_t_hs_prepare)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10034 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_t_hs_zero)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10035 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + pinfo->dsi_phy_ctrl.data_t_hs_trial); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10036 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_t_ta_go)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + + tmp = 0x10037 + (i << 4); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, tmp); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL1_OFFSET, + DSS_REDUCE(pinfo->dsi_phy_ctrl.data_t_ta_get)); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000002); + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, + 0x00000000); + } + + outp32(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x00000007); + + is_ready = false; + dw_jiffies = jiffies + HZ / 2; + do { + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + if ((tmp & 0x00000001) == 0x00000001) { + is_ready = true; + break; + } + } while (time_after(dw_jiffies, jiffies)); + + if (!is_ready) { + HISI_FB_INFO + ("fb%d, phylock is not ready!MIPIDSI_PHY_STATUS_OFFSET=0x%x.\n", + hisifd->index, tmp); + } + + if (pinfo->mipi.lane_nums >= DSI_4_LANES) { + cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9) | BIT(11)); + } else if (pinfo->mipi.lane_nums >= DSI_3_LANES) { + cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9)); + } else if (pinfo->mipi.lane_nums >= DSI_2_LANES) { + cmp_stopstate_val = (BIT(4) | BIT(7)); + } else { + cmp_stopstate_val = (BIT(4)); + } + + is_ready = false; + dw_jiffies = jiffies + HZ / 2; + do { + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + if ((tmp & cmp_stopstate_val) == cmp_stopstate_val) { + is_ready = true; + break; + } + } while (time_after(dw_jiffies, jiffies)); + + if (!is_ready) { + HISI_FB_INFO + ("fb%d, phystopstateclklane is not ready! " + "MIPIDSI_PHY_STATUS_OFFSET=0x%x.\n", + hisifd->index, tmp); + } + + /*************************Configure the DPHY end*************************/ + + if (is_mipi_cmd_panel(hisifd)) { + + set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x1, 1, 0); + set_reg(mipi_dsi_base + MIPIDSI_EDPI_CMD_SIZE_OFFSET, rect.w, 16, 0); + + if (pinfo->mipi.hs_wr_to_time == 0) { + set_reg(mipi_dsi_base + MIPIDSI_HS_WR_TO_CNT_OFFSET, + 0x1000002, 25, 0); + } else { + set_reg(mipi_dsi_base + MIPIDSI_HS_WR_TO_CNT_OFFSET, + (0x1 << 24) | (pinfo->mipi.hs_wr_to_time * + pinfo->dsi_phy_ctrl.lane_byte_clk / 1000000000UL), 25, 0); + } + } + + set_reg(mipi_dsi_base + MIPIDSI_PHY_IF_CFG_OFFSET, + pinfo->dsi_phy_ctrl.phy_stop_wait_time, 8, 8); + + /* + ** 2. Configure the DPI Interface: + ** This defines how the DPI interface interacts with the controller. + */ + set_reg(mipi_dsi_base + MIPIDSI_DPI_VCID_OFFSET, pinfo->mipi.vc, 2, 0); + set_reg(mipi_dsi_base + MIPIDSI_DPI_COLOR_CODING_OFFSET, + pinfo->mipi.color_mode, 4, 0); + + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, + pinfo->ldi.data_en_plr, 1, 0); + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, + pinfo->ldi.vsync_plr, 1, 1); + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, + pinfo->ldi.hsync_plr, 1, 2); + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, 0x0, 1, 3); + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, 0x0, 1, 4); + + /* + ** 3. Select the Video Transmission Mode: + ** This defines how the processor requires the video line to be + ** transported through the DSI link. + */ + + set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x3f, 6, 8); + /* set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x0, 1, 14); */ + if (is_mipi_video_panel(hisifd)) { + set_reg(mipi_dsi_base + MIPIDSI_DPI_LP_CMD_TIM_OFFSET, 0x4, 8, 16); + set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x1, 1, 15); + } + + if ((pinfo->mipi.dsi_version == DSI_1_2_VERSION) + && (is_mipi_video_panel(hisifd)) + && ((pinfo->ifbc_type == IFBC_TYPE_VESA3X_SINGLE) + || (pinfo->ifbc_type == IFBC_TYPE_VESA3X_DUAL))) { + + set_reg(mipi_dsi_base + MIPIDSI_VID_PKT_SIZE_OFFSET, + rect.w * pinfo->pxl_clk_rate_div, 14, 0); + + if (pinfo->mipi.burst_mode < DSI_BURST_SYNC_PULSES_1) { + HISI_FB_INFO + ("pinfo->mipi.burst_mode = %d. video need config BURST mode\n", + pinfo->mipi.burst_mode); + pinfo->mipi.burst_mode = DSI_BURST_SYNC_PULSES_1; + } + } else { + set_reg(mipi_dsi_base + MIPIDSI_VID_PKT_SIZE_OFFSET, rect.w, 14, 0); + } + + set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, + pinfo->mipi.burst_mode, 2, 0); + set_reg(mipi_dsi_base + MIPIDSI_PCKHDL_CFG_OFFSET, 0x1, 1, 2); + + /* + ** 4. Define the DPI Horizontal timing configuration: + ** + ** Hsa_time = HSA*(PCLK period/Clk Lane Byte Period); + ** Hbp_time = HBP*(PCLK period/Clk Lane Byte Period); + ** Hline_time = (HSA+HBP+HACT+HFP)*(PCLK period/Clk Lane Byte Period); + */ + pixel_clk = mipi_pixel_clk(hisifd); + hsa_time = + pinfo->ldi.h_pulse_width * pinfo->dsi_phy_ctrl.lane_byte_clk / + pixel_clk; + hbp_time = + pinfo->ldi.h_back_porch * pinfo->dsi_phy_ctrl.lane_byte_clk / + pixel_clk; + hline_time = + (pinfo->ldi.h_pulse_width + pinfo->ldi.h_back_porch + rect.w + + pinfo->ldi.h_front_porch) * pinfo->dsi_phy_ctrl.lane_byte_clk / + pixel_clk; + set_reg(mipi_dsi_base + MIPIDSI_VID_HSA_TIME_OFFSET, hsa_time, 12, 0); + set_reg(mipi_dsi_base + MIPIDSI_VID_HBP_TIME_OFFSET, hbp_time, 12, 0); + set_reg(mipi_dsi_base + MIPIDSI_VID_HLINE_TIME_OFFSET, hline_time, 15, 0); + + set_reg(mipi_dsi_base + MIPIDSI_VID_VSA_LINES_OFFSET, + pinfo->ldi.v_pulse_width, 10, 0); + set_reg(mipi_dsi_base + MIPIDSI_VID_VBP_LINES_OFFSET, + pinfo->ldi.v_back_porch, 10, 0); + set_reg(mipi_dsi_base + MIPIDSI_VID_VFP_LINES_OFFSET, + pinfo->ldi.v_front_porch, 10, 0); + set_reg(mipi_dsi_base + MIPIDSI_VID_VACTIVE_LINES_OFFSET, rect.h, 14, 0); + set_reg(mipi_dsi_base + MIPIDSI_TO_CNT_CFG_OFFSET, 0x7FF, 16, 0); + + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET, + pinfo->dsi_phy_ctrl.clk_lane_lp2hs_time, 10, 0); + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET, + pinfo->dsi_phy_ctrl.clk_lane_hs2lp_time, 10, 16); + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_RD_CFG_OFFSET, 0x7FFF, 15, 0); + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_CFG_OFFSET, + pinfo->dsi_phy_ctrl.data_lane_lp2hs_time, 10, 0); + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_CFG_OFFSET, + pinfo->dsi_phy_ctrl.data_lane_hs2lp_time, 10, 16); + + set_reg(mipi_dsi_base + MIPIDSI_PWR_UP_OFFSET, 0x1, 1, 0); +} + +int mipi_dsi_clk_enable(struct hisi_fb_data_type *hisifd) +{ + int ret = 0; + struct clk *clk_tmp = NULL; + + BUG_ON(hisifd == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + clk_tmp = hisifd->dss_dphy0_ref_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy0_ref_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy0_ref_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + clk_tmp = hisifd->dss_dphy0_cfg_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy0_cfg_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy0_cfg_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + clk_tmp = hisifd->dss_pclk_dsi0_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_dsi0_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_dsi0_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + } +#ifdef CONFIG_PCLK_PCTRL_USED + clk_tmp = hisifd->dss_pclk_pctrl_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_pctrl_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_pctrl_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } +#endif + + if (is_dual_mipi_panel(hisifd) || (hisifd->index == EXTERNAL_PANEL_IDX)) { + clk_tmp = hisifd->dss_dphy1_ref_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy1_ref_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy1_ref_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + clk_tmp = hisifd->dss_dphy1_cfg_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy1_cfg_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_dphy1_cfg_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + + clk_tmp = hisifd->dss_pclk_dsi1_clk; + if (clk_tmp) { + ret = clk_prepare(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_dsi1_clk clk_prepare failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + + ret = clk_enable(clk_tmp); + if (ret) { + HISI_FB_ERR + ("fb%d dss_pclk_dsi1_clk clk_enable failed, error=%d!\n", + hisifd->index, ret); + return -EINVAL; + } + } + } + + return 0; +} + +int mipi_dsi_clk_disable(struct hisi_fb_data_type *hisifd) +{ + struct clk *clk_tmp = NULL; + + BUG_ON(hisifd == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + clk_tmp = hisifd->dss_dphy0_ref_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + + clk_tmp = hisifd->dss_dphy0_cfg_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + + clk_tmp = hisifd->dss_pclk_dsi0_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + } +#ifdef CONFIG_PCLK_PCTRL_USED + clk_tmp = hisifd->dss_pclk_pctrl_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } +#endif + + if (is_dual_mipi_panel(hisifd) || (hisifd->index == EXTERNAL_PANEL_IDX)) { + clk_tmp = hisifd->dss_dphy1_ref_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + + clk_tmp = hisifd->dss_dphy1_cfg_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + + clk_tmp = hisifd->dss_pclk_dsi1_clk; + if (clk_tmp) { + clk_disable(clk_tmp); + clk_unprepare(clk_tmp); + } + } + + return 0; +} + +/******************************************************************************* + ** + */ +static int mipi_dsi_on_sub1(struct hisi_fb_data_type *hisifd, + char __iomem *mipi_dsi_base) +{ + BUG_ON(mipi_dsi_base == NULL); + + /* mipi init */ + mipi_init(hisifd, mipi_dsi_base); + + /* switch to cmd mode */ + set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x1, 1, 0); + /* cmd mode: low power mode */ + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x7f, 7, 8); + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0xf, 4, 16); + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x1, 1, 24); + /* disable generate High Speed clock */ + /* delete? */ + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x0, 1, 0); + + return 0; +} + +static int mipi_dsi_on_sub2(struct hisi_fb_data_type *hisifd, + char __iomem *mipi_dsi_base) +{ + struct hisi_panel_info *pinfo = NULL; + uint32_t pctrl_dphytx_stopcnt = 0; + + BUG_ON(hisifd == NULL); + BUG_ON(mipi_dsi_base == NULL); + + pinfo = &(hisifd->panel_info); + + if (is_mipi_video_panel(hisifd)) { + /* switch to video mode */ + set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x0, 1, 0); + } + + if (is_mipi_cmd_panel(hisifd)) { + /* cmd mode: high speed mode */ + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x0, 7, 8); + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x0, 4, 16); + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x0, 1, 24); + } + + /* enable EOTP TX */ + set_reg(mipi_dsi_base + MIPIDSI_PCKHDL_CFG_OFFSET, 0x1, 1, 0); + + /* enable generate High Speed clock, non continue */ + if (pinfo->mipi.non_continue_en) { + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x3, 2, 0); + } else { + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x1, 2, 0); + } + + if ((pinfo->mipi.dsi_version == DSI_1_2_VERSION) + && (pinfo->ifbc_type == IFBC_TYPE_VESA3X_SINGLE)) { + set_reg(mipi_dsi_base + MIPIDSI_DSC_PARAMETER_OFFSET, 0x01, 32, + 0); + } + + pctrl_dphytx_stopcnt = (uint64_t) (pinfo->ldi.h_back_porch + + pinfo->ldi.h_front_porch + + pinfo->ldi.h_pulse_width + + 5) * + hisifd->dss_clk_rate.dss_pclk_pctrl_rate / pinfo->pxl_clk_rate; + + outp32(hisifd->pctrl_base + PERI_CTRL29, pctrl_dphytx_stopcnt); + if (is_dual_mipi_panel(hisifd)) { + outp32(hisifd->pctrl_base + PERI_CTRL32, pctrl_dphytx_stopcnt); + } + + return 0; +} + +int mipi_dsi_off_sub(struct hisi_fb_data_type *hisifd, + char __iomem *mipi_dsi_base) +{ + BUG_ON(hisifd == NULL); + BUG_ON(mipi_dsi_base == NULL); + + /* switch to cmd mode */ + set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x1, 1, 0); + /* cmd mode: low power mode */ + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x7f, 7, 8); + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0xf, 4, 16); + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x1, 1, 24); + + /* disable generate High Speed clock */ + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x0, 1, 0); + + /* shutdown d_phy */ + set_reg(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x0, 3, 0); + + return 0; +} + +static int mipi_dsi_ulps_enter(struct hisi_fb_data_type *hisifd, + char __iomem *mipi_dsi_base) +{ + uint32_t tmp = 0; + uint32_t cmp_ulpsactivenot_val = 0; + uint32_t cmp_stopstate_val = 0; + uint32_t try_times = 0; + + BUG_ON(hisifd == NULL); + BUG_ON(mipi_dsi_base == NULL); + + HISI_FB_DEBUG("fb%d, +!\n", hisifd->index); + + if (hisifd->panel_info.mipi.lane_nums >= DSI_4_LANES) { + cmp_ulpsactivenot_val = (BIT(5) | BIT(8) | BIT(10) | BIT(12)); + cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9) | BIT(11)); + } else if (hisifd->panel_info.mipi.lane_nums >= DSI_3_LANES) { + cmp_ulpsactivenot_val = (BIT(5) | BIT(8) | BIT(10)); + cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9)); + } else if (hisifd->panel_info.mipi.lane_nums >= DSI_2_LANES) { + cmp_ulpsactivenot_val = (BIT(5) | BIT(8)); + cmp_stopstate_val = (BIT(4) | BIT(7)); + } else { + cmp_ulpsactivenot_val = (BIT(5)); + cmp_stopstate_val = (BIT(4)); + } + + if (inp32(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET) & (BIT(1))) + cmp_stopstate_val |= (BIT(2)); + + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & cmp_stopstate_val) != cmp_stopstate_val) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, check DPHY data and clock lane stopstate failed! MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + return 0; + } + + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + + if (mipi_dsi_base == hisifd->mipi_dsi0_base) { + set_reg(hisifd->pctrl_base + PERI_CTRL23, 0x0, 1, 3); + } else { + set_reg(hisifd->pctrl_base + PERI_CTRL23, 0x0, 1, 4); + } + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x0, 1, 0); + set_reg(mipi_dsi_base + MIPIDSI_PHY_ULPS_CTRL_OFFSET, 0x4, 4, 0); + + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & cmp_ulpsactivenot_val) != 0) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, check DPHY data lane ulpsactivenot_status failed! MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + break; + } + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + set_reg(mipi_dsi_base + MIPIDSI_PHY_ULPS_CTRL_OFFSET, 0x5, 4, 0); + + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & BIT(3)) != 0) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, check DPHY clock lane ulpsactivenot_status failed! MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + break; + } + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + outp32(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x7); + + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & BIT(0)) != 0) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, check DPHY clock lane phy_lock failed! MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + break; + } + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + + set_reg(hisifd->peri_crg_base + PERDIS3, 0x1, 4, 28); + HISI_FB_DEBUG("fb%d, -!\n", hisifd->index); + + return 0; +} + +static int mipi_dsi_ulps_exit(struct hisi_fb_data_type *hisifd, + char __iomem *mipi_dsi_base) +{ + uint32_t tmp = 0; + uint32_t cmp_ulpsactivenot_val = 0; + uint32_t try_times = 0; + + BUG_ON(hisifd == NULL); + BUG_ON(mipi_dsi_base == NULL); + + HISI_FB_DEBUG("fb%d, +!\n", hisifd->index); + + set_reg(hisifd->peri_crg_base + PEREN3, 0x1, 4, 28); + set_reg(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x1, 1, 3); + + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & BIT(0)) != 1) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, check DPHY clock lane phy_lock failed! MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + break; + } + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + + if (hisifd->panel_info.mipi.lane_nums >= DSI_4_LANES) { + cmp_ulpsactivenot_val = + (BIT(3) | BIT(5) | BIT(8) | BIT(10) | BIT(12)); + } else if (hisifd->panel_info.mipi.lane_nums >= DSI_3_LANES) { + cmp_ulpsactivenot_val = (BIT(3) | BIT(5) | BIT(8) | BIT(10)); + } else if (hisifd->panel_info.mipi.lane_nums >= DSI_2_LANES) { + cmp_ulpsactivenot_val = (BIT(3) | BIT(5) | BIT(8)); + } else { + cmp_ulpsactivenot_val = (BIT(3) | BIT(5)); + } + + if (mipi_dsi_base == hisifd->mipi_dsi0_base) { + set_reg(hisifd->pctrl_base + PERI_CTRL23, 0x1, 1, 3); + } else { + set_reg(hisifd->pctrl_base + PERI_CTRL23, 0x1, 1, 4); + } + + outp32(mipi_dsi_base + MIPIDSI_PHY_ULPS_CTRL_OFFSET, 0xF); + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & cmp_ulpsactivenot_val) != cmp_ulpsactivenot_val) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, failed to request that data lane and clock lane exit ULPS!MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + break; + } + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + mdelay(1); + outp32(mipi_dsi_base + MIPIDSI_PHY_ULPS_CTRL_OFFSET, 0x0); + + try_times = 0; + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + while ((tmp & BIT(0)) != 0x1) { + udelay(10); + if (++try_times > 100) { + HISI_FB_ERR + ("fb%d, failed to wait DPHY PLL Lock!MIPIDSI_PHY_STATUS=0x%x.\n", + hisifd->index, tmp); + break; + } + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); + } + + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x1, 1, 0); + HISI_FB_DEBUG("fb%d, -!\n", hisifd->index); + + return 0; +} + +int mipi_dsi_ulps_cfg(struct hisi_fb_data_type *hisifd, int enable) +{ + int ret = 0; + + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + if (enable) { + mipi_dsi_ulps_exit(hisifd, hisifd->mipi_dsi0_base); + if (is_dual_mipi_panel(hisifd)) + mipi_dsi_ulps_exit(hisifd, hisifd->mipi_dsi1_base); + } else { + mipi_dsi_ulps_enter(hisifd, hisifd->mipi_dsi0_base); + if (is_dual_mipi_panel(hisifd)) + mipi_dsi_ulps_enter(hisifd, hisifd->mipi_dsi1_base); + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +void mipi_dsi_reset(struct hisi_fb_data_type *hisifd) +{ + BUG_ON(hisifd == NULL); + set_reg(hisifd->mipi_dsi0_base + MIPIDSI_PWR_UP_OFFSET, 0x0, 1, 0); + msleep(2); + set_reg(hisifd->mipi_dsi0_base + MIPIDSI_PWR_UP_OFFSET, 0x1, 1, 0); +} + +/******************************************************************************* + ** + */ +static int mipi_dsi_on(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + /* set LCD init step before LCD on */ + hisifd->panel_info.lcd_init_step = LCD_INIT_POWER_ON; + ret = panel_next_on(pdev); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + if (is_dual_mipi_panel(hisifd)) + outp32(hisifd->peri_crg_base + PERRSTDIS3, 0x30000000); + else + outp32(hisifd->peri_crg_base + PERRSTDIS3, 0x10000000); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + outp32(hisifd->peri_crg_base + PERRSTDIS3, 0x20000000); + } else { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + } + + mipi_dsi_clk_enable(hisifd); + if (hisifd->index == PRIMARY_PANEL_IDX) { + mipi_dsi_on_sub1(hisifd, hisifd->mipi_dsi0_base); + if (is_dual_mipi_panel(hisifd)) + mipi_dsi_on_sub1(hisifd, hisifd->mipi_dsi1_base); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + mipi_dsi_on_sub1(hisifd, hisifd->mipi_dsi1_base); + } else { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + } + + ret = panel_next_on(pdev); + if (hisifd->index == PRIMARY_PANEL_IDX) { + mipi_dsi_on_sub2(hisifd, hisifd->mipi_dsi0_base); + if (is_dual_mipi_panel(hisifd)) + mipi_dsi_on_sub2(hisifd, hisifd->mipi_dsi1_base); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + mipi_dsi_on_sub2(hisifd, hisifd->mipi_dsi1_base); + } else { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + } + + /* mipi hs video/command mode */ + ret = panel_next_on(pdev); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int mipi_dsi_off(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + /* set LCD uninit step before LCD off */ + hisifd->panel_info.lcd_uninit_step = LCD_UNINIT_MIPI_HS_SEND_SEQUENCE; + ret = panel_next_off(pdev); + + if (hisifd->panel_info.lcd_uninit_step_support) { + /* TODO: add MIPI LP mode here if necessary */ + /* MIPI LP mode end */ + ret = panel_next_off(pdev); + } + + if (hisifd->index == PRIMARY_PANEL_IDX) { + mipi_dsi_off_sub(hisifd, hisifd->mipi_dsi0_base); + if (is_dual_mipi_panel(hisifd)) + mipi_dsi_off_sub(hisifd, hisifd->mipi_dsi1_base); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + mipi_dsi_off_sub(hisifd, hisifd->mipi_dsi1_base); + } else { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + } + + mipi_dsi_clk_disable(hisifd); + if (hisifd->index == PRIMARY_PANEL_IDX) { + if (is_dual_mipi_panel(hisifd)) + outp32(hisifd->peri_crg_base + PERRSTEN3, 0x30000000); + else + outp32(hisifd->peri_crg_base + PERRSTEN3, 0x10000000); + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + outp32(hisifd->peri_crg_base + PERRSTEN3, 0x20000000); + } else { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + } + + if (hisifd->panel_info.lcd_uninit_step_support) { + ret = panel_next_off(pdev); + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int mipi_dsi_remove(struct platform_device *pdev) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = panel_next_remove(pdev); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + if (hisifd->dss_dphy0_ref_clk) { + clk_put(hisifd->dss_dphy0_ref_clk); + hisifd->dss_dphy0_ref_clk = NULL; + } + + if (hisifd->dss_dphy0_cfg_clk) { + clk_put(hisifd->dss_dphy0_cfg_clk); + hisifd->dss_dphy0_cfg_clk = NULL; + } + + if (is_dual_mipi_panel(hisifd)) { + if (hisifd->dss_dphy1_ref_clk) { + clk_put(hisifd->dss_dphy1_ref_clk); + hisifd->dss_dphy1_ref_clk = NULL; + } + + if (hisifd->dss_dphy1_cfg_clk) { + clk_put(hisifd->dss_dphy1_cfg_clk); + hisifd->dss_dphy1_cfg_clk = NULL; + } + } + } else if (hisifd->index == EXTERNAL_PANEL_IDX) { + if (hisifd->dss_dphy1_ref_clk) { + clk_put(hisifd->dss_dphy1_ref_clk); + hisifd->dss_dphy1_ref_clk = NULL; + } + + if (hisifd->dss_dphy1_cfg_clk) { + clk_put(hisifd->dss_dphy1_cfg_clk); + hisifd->dss_dphy1_cfg_clk = NULL; + } + } else { + HISI_FB_ERR("fb%d, not supported!\n", hisifd->index); + } + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int mipi_dsi_set_backlight(struct platform_device *pdev, + uint32_t bl_level) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = panel_next_set_backlight(pdev, bl_level); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int mipi_dsi_vsync_ctrl(struct platform_device *pdev, int enable) +{ + int ret = 0; + struct hisi_fb_data_type *hisifd = NULL; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = panel_next_vsync_ctrl(pdev, enable); + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return ret; +} + +static int mipi_dsi_clk_irq_setup(struct platform_device *pdev) +{ + struct hisi_fb_data_type *hisifd = NULL; + int ret = 0; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + if (hisifd->index == PRIMARY_PANEL_IDX) { + ret = + clk_set_rate(hisifd->dss_dphy0_ref_clk, + DEFAULT_MIPI_CLK_RATE); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_dphy0_ref_clk clk_set_rate(%lu) failed, error=%d!\n", + hisifd->index, DEFAULT_MIPI_CLK_RATE, ret); + return -EINVAL; + } + HISI_FB_INFO("dss_dphy0_ref_clk:[%lu]->[%lu].\n", + DEFAULT_MIPI_CLK_RATE, + clk_get_rate(hisifd->dss_dphy0_ref_clk)); + + ret = + clk_set_rate(hisifd->dss_dphy0_cfg_clk, + DEFAULT_MIPI_CLK_RATE); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_dphy0_cfg_clk clk_set_rate(%lu) failed, error=%d!\n", + hisifd->index, DEFAULT_MIPI_CLK_RATE, ret); + return -EINVAL; + } + HISI_FB_INFO("dss_dphy0_cfg_clk:[%lu]->[%lu].\n", + DEFAULT_MIPI_CLK_RATE, + clk_get_rate(hisifd->dss_dphy0_cfg_clk)); + HISI_FB_INFO("dss_pclk_dsi0_clk:[%lu]->[%lu].\n", + DEFAULT_PCLK_DSI_RATE, + clk_get_rate(hisifd->dss_pclk_dsi0_clk)); + } +#ifdef CONFIG_PCLK_PCTRL_USED + ret = clk_set_rate(hisifd->dss_pclk_pctrl_clk, DEFAULT_PCLK_PCTRL_RATE); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_pclk_pctrl clk_set_rate(%lu) failed, error=%d!\n", + hisifd->index, DEFAULT_PCLK_PCTRL_RATE, ret); + return -EINVAL; + } + HISI_FB_INFO("dss_pclk_pctrl_clk:[%lu]->[%lu].\n", + DEFAULT_PCLK_PCTRL_RATE, + clk_get_rate(hisifd->dss_pclk_pctrl_clk)); +#endif + + if (is_dual_mipi_panel(hisifd) || (hisifd->index == EXTERNAL_PANEL_IDX)) { + ret = + clk_set_rate(hisifd->dss_dphy1_ref_clk, + DEFAULT_MIPI_CLK_RATE); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_dphy1_ref_clk clk_set_rate(%lu) failed, error=%d!\n", + hisifd->index, DEFAULT_MIPI_CLK_RATE, ret); + return -EINVAL; + } + HISI_FB_INFO("dss_dphy1_ref_clk:[%lu]->[%lu].\n", + DEFAULT_MIPI_CLK_RATE, + clk_get_rate(hisifd->dss_dphy1_ref_clk)); + + ret = + clk_set_rate(hisifd->dss_dphy1_cfg_clk, + DEFAULT_MIPI_CLK_RATE); + if (ret < 0) { + HISI_FB_ERR + ("fb%d dss_dphy1_cfg_clk clk_set_rate(%lu) failed, " + "error=%d!\n", + hisifd->index, DEFAULT_MIPI_CLK_RATE, ret); + return -EINVAL; + } + HISI_FB_INFO("dss_dphy1_cfg_clk:[%lu]->[%lu].\n", + DEFAULT_MIPI_CLK_RATE, + clk_get_rate(hisifd->dss_dphy1_cfg_clk)); + HISI_FB_INFO("dss_pclk_dsi1_clk:[%lu]->[%lu].\n", + DEFAULT_PCLK_DSI_RATE, + clk_get_rate(hisifd->dss_pclk_dsi1_clk)); + } + + return ret; +} + +static int mipi_dsi_probe(struct platform_device *pdev) +{ + struct hisi_fb_data_type *hisifd = NULL; + struct platform_device *dpp_dev = NULL; + struct hisi_fb_panel_data *pdata = NULL; + int ret = 0; + + BUG_ON(pdev == NULL); + hisifd = platform_get_drvdata(pdev); + BUG_ON(hisifd == NULL); + + HISI_FB_DEBUG("fb%d, +.\n", hisifd->index); + + ret = mipi_dsi_clk_irq_setup(pdev); + if (ret) { + HISI_FB_ERR("fb%d mipi_dsi_irq_clk_setup failed, error=%d!\n", + hisifd->index, ret); + goto err; + } + /* alloc device */ + dpp_dev = platform_device_alloc(DEV_NAME_DSS_DPE, pdev->id); + if (!dpp_dev) { + HISI_FB_ERR("fb%d platform_device_alloc failed, error=%d!\n", + hisifd->index, ret); + ret = -ENOMEM; + goto err_device_alloc; + } + /* link to the latest pdev */ + hisifd->pdev = dpp_dev; + + /* alloc panel device data */ + ret = platform_device_add_data(dpp_dev, dev_get_platdata(&pdev->dev), + sizeof(struct hisi_fb_panel_data)); + if (ret) { + HISI_FB_ERR("fb%d platform_device_add_data failed error=%d!\n", + hisifd->index, ret); + goto err_device_put; + } + + /* data chain */ + pdata = dev_get_platdata(&dpp_dev->dev); + pdata->on = mipi_dsi_on; + pdata->off = mipi_dsi_off; + pdata->remove = mipi_dsi_remove; + pdata->set_backlight = mipi_dsi_set_backlight; + pdata->vsync_ctrl = mipi_dsi_vsync_ctrl; + pdata->next = pdev; + + /* get/set panel info */ + memcpy(&hisifd->panel_info, pdata->panel_info, + sizeof(struct hisi_panel_info)); + + /* set driver data */ + platform_set_drvdata(dpp_dev, hisifd); + /* device add */ + ret = platform_device_add(dpp_dev); + if (ret) { + HISI_FB_ERR("fb%d platform_device_add failed, error=%d!\n", + hisifd->index, ret); + goto err_device_put; + } + + HISI_FB_DEBUG("fb%d, -.\n", hisifd->index); + + return 0; + + err_device_put: + platform_device_put(dpp_dev); + err_device_alloc: + err: + return ret; +} + +static struct platform_driver this_driver = { + .probe = mipi_dsi_probe, + .remove = NULL, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = DEV_NAME_MIPIDSI, + }, +}; + +static int __init mipi_dsi_driver_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&this_driver); + if (ret) { + HISI_FB_ERR("platform_driver_register failed, error=%d!\n", + ret); + return ret; + } + return ret; +} + +module_init(mipi_dsi_driver_init); diff --git a/drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.h b/drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.h new file mode 100755 index 000000000000..06fd0d3a22ed --- /dev/null +++ b/drivers/video/fbdev/hisi/dss/hisi_mipi_dsi.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2013-2014, Hisilicon Tech. Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef HISI_MIPI_DSI_H +#define HISI_MIPI_DSI_H + +#include "hisi_fb.h" + +/* mipi dsi panel */ +enum { + DSI_VIDEO_MODE, + DSI_CMD_MODE, +}; + +enum { + DSI_1_1_VERSION = 0, + DSI_1_2_VERSION, +}; + +enum { + DSI_1_LANES = 0, + DSI_2_LANES, + DSI_3_LANES, + DSI_4_LANES, +}; + +enum { + DSI_LANE_NUMS_DEFAULT = 0, + DSI_1_LANES_SUPPORT = BIT(0), + DSI_2_LANES_SUPPORT = BIT(1), + DSI_3_LANES_SUPPORT = BIT(2), + DSI_4_LANES_SUPPORT = BIT(3), +}; + +enum { + DSI_16BITS_1 = 0, + DSI_16BITS_2, + DSI_16BITS_3, + DSI_18BITS_1, + DSI_18BITS_2, + DSI_24BITS_1, + DSI_24BITS_2, + DSI_24BITS_3, + DSI_DSC24_COMPRESSED_DATA = 0xF, +}; + +enum { + DSI_NON_BURST_SYNC_PULSES = 0, + DSI_NON_BURST_SYNC_EVENTS, + DSI_BURST_SYNC_PULSES_1, + DSI_BURST_SYNC_PULSES_2, +}; + +#define DSI_VIDEO_DST_FORMAT_RGB565 0 +#define DSI_VIDEO_DST_FORMAT_RGB666 1 +#define DSI_VIDEO_DST_FORMAT_RGB666_LOOSE 2 +#define DSI_VIDEO_DST_FORMAT_RGB888 3 + +#define DSI_CMD_DST_FORMAT_RGB565 0 +#define DSI_CMD_DST_FORMAT_RGB666 1 +#define DSI_CMD_DST_FORMAT_RGB888 2 + +/* dcs read/write */ +#define DTYPE_DCS_WRITE 0x05 /* short write, 0 parameter */ +#define DTYPE_DCS_WRITE1 0x15 /* short write, 1 parameter */ +#define DTYPE_DCS_READ 0x06 /* read */ +#define DTYPE_DCS_LWRITE 0x39 /* long write */ +#define DTYPE_DSC_LWRITE 0x0A /* dsc dsi1.2 vase3x long write */ + +/* generic read/write */ +#define DTYPE_GEN_WRITE 0x03 /* short write, 0 parameter */ +#define DTYPE_GEN_WRITE1 0x13 /* short write, 1 parameter */ +#define DTYPE_GEN_WRITE2 0x23 /* short write, 2 parameter */ +#define DTYPE_GEN_LWRITE 0x29 /* long write */ +#define DTYPE_GEN_READ 0x04 /* long read, 0 parameter */ +#define DTYPE_GEN_READ1 0x14 /* long read, 1 parameter */ +#define DTYPE_GEN_READ2 0x24 /* long read, 2 parameter */ + +#define DTYPE_TEAR_ON 0x35 /* set tear on */ +#define DTYPE_MAX_PKTSIZE 0x37 /* set max packet size */ +#define DTYPE_NULL_PKT 0x09 /* null packet, no data */ +#define DTYPE_BLANK_PKT 0x19 /* blankiing packet, no data */ + +#define DTYPE_CM_ON 0x02 /* color mode off */ +#define DTYPE_CM_OFF 0x12 /* color mode on */ +#define DTYPE_PERIPHERAL_OFF 0x22 +#define DTYPE_PERIPHERAL_ON 0x32 + +#define DSI_HDR_DTYPE(dtype) ((dtype) & 0x03f) +#define DSI_HDR_VC(vc) (((vc) & 0x03) << 6) +#define DSI_HDR_DATA1(data) (((data) & 0x0ff) << 8) +#define DSI_HDR_DATA2(data) (((data) & 0x0ff) << 16) +#define DSI_HDR_WC(wc) (((wc) & 0x0ffff) << 8) + +#define DSI_PLD_DATA1(data) ((data) & 0x0ff) +#define DSI_PLD_DATA2(data) (((data) & 0x0ff) << 8) +#define DSI_PLD_DATA3(data) (((data) & 0x0ff) << 16) +#define DSI_PLD_DATA4(data) (((data) & 0x0ff) << 24) + +struct dsi_cmd_desc { + int dtype; + int vc; + int wait; + int waittype; + int dlen; + char *payload; +}; + +struct mipi_dsi_read_compare_data { + uint32_t *read_value; + uint32_t *expected_value; + uint32_t *read_mask; + char **reg_name; + int log_on; + struct dsi_cmd_desc *cmds; + int cnt; +}; + +/****************************************************************************** + ** FUNCTIONS PROTOTYPES + */ +void mipi_dsi_max_return_packet_size(struct dsi_cmd_desc *cm, + char __iomem *dsi_base); +void mipi_dsi_sread(uint32_t *out, char __iomem *dsi_base); +void mipi_dsi_lread(uint32_t *out, char __iomem *dsi_base); +uint32_t mipi_dsi_read(uint32_t *out, char __iomem *dsi_base); +int mipi_dsi_swrite(struct dsi_cmd_desc *cm, char __iomem *dsi_base); +int mipi_dsi_lwrite(struct dsi_cmd_desc *cm, char __iomem *dsi_base); +void mipi_dsi_check_0lane_is_ready(char __iomem *dsi_base); +int mipi_dsi_cmds_tx(struct dsi_cmd_desc *cmds, int cnt, + char __iomem *dsi_base); +int mipi_dsi_cmds_rx(uint32_t *out, struct dsi_cmd_desc *cmds, int cnt, + char __iomem *dsi_base); + +int mipi_dsi_read_compare(struct mipi_dsi_read_compare_data *data, + char __iomem *dsi_base); + +struct hisi_fb_data_type; +int mipi_dsi_clk_enable(struct hisi_fb_data_type *hisifd); +int mipi_dsi_clk_disable(struct hisi_fb_data_type *hisifd); +void mipi_dsi_reset(struct hisi_fb_data_type *hisifd); + +#endif /* HISI_MIPI_DSI_H */ -- 2.12.0-rc0
| |