diff options
| -rw-r--r-- | Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt | 140 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/Makefile | 6 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss.h | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi.c | 383 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi.h | 294 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_host.c | 1259 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_panel.c | 358 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_hwio.h | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_video.c | 18 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_panel.h | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_mdss_io_8974.c | 199 |
11 files changed, 2652 insertions, 8 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt new file mode 100644 index 000000000000..6db1150e1ba8 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -0,0 +1,140 @@ +Qualcomm mdss-dsi-panel + +mdss-dsi-panel is a dsi panel driver which supports panels that +are compatable with MIPI display serial interface specification. + +Required properties: +- compatible: Must be "qcom,mdss-dsi-panel" +- status: A string that has to be set to "okay/ok" to enable + the panel driver. By default this property will be + set to "disable". Will be set to "ok/okay" status + for specific platforms. +- qcom,mdss-pan-res: A two dimensional array that specifies the panel + resolution. +- qcom,mdss-pan-bpp: Specifies the panel bits per pixel. Default value is 24(rgb888). + 18 = for rgb666 + 16 = for rgb565 +- qcom,mdss-panel-on-cmds: An array of variable length that lists the init commands + of the panel. Each command will have the format specified + as below: + --> data type of the command + --> specifies whether this command packet is last. + --> virtual channel + --> Needs acknowledge from the panel or not. + --> wait time after the command is transmitter. + --> size of payload + --> payload. +- qcom,mdss-panel-off-cmds: An array of variable length that lists the panel off + commands. Each command will have the format specified + as below: + --> data type of the command + --> specifies whether this command packet is last. + --> virtual channel + --> Needs acknowledge from the panel or not. + --> wait time after the command is transmitter. + --> size of payload + --> payload. + +Required structure: +- A qcom,mdss-dsi-panel node must be a child of an mdss-dsi controller node that links to + one of the two DSI controllers. + + +Optional properties: +- label: A string used as a descriptive name of the panel +- qcom,mdss-pan-porch-values: An array of size 6 that specifies the panel blanking values. +- qcom,mdss-pan-underflow-clr: Specifies the controller settings for the panel underflow clear + settings. Default value is 0xff. +- qcom,mdss-pan-bl-levels: Specifies the backlight levels supported by the panel. + Default range is 1 to 255. + +- qcom,mdss-pan-dsi-mode: Specifies the panel operating mode. + 0 = enable video mode(default mode). + 1 = enable command mode. +- qcom,mdss-pan-dsi-h-pulse-mode: Specifies the pulse mode option for the panel. + 0 = Don't send hsa/he following vs/ve packet(default) + 1 = Send hsa/he following vs/ve packet +- qcom,mdss-pan-dsi-h-power-stop: An Array of size 3 that specifies the power mode + during horizontal porch and sync periods of the panel. + 0 = high speed mode(default mode). + 1 = Low power mode for horizontal porches and sync pulse. +- qcom,mdss-pan-dsi-bllp-power-stop: An Array of size 2 that specifies the power mode + during blanking period and after EOF(end of frame). + 0 = high speed mode(default mode). + 1 = Low power mode during blanking and EOF. +- qcom,mdss-pan-dsi-traffic-mode: Specifies the panel traffic mode. + 0 = non burst with sync pulses (default mode). + 1 = non burst with sync start event. + 2 = burst mode. +- qcom,mdss-pan-dsi-dst-format: Specifies the destination format. + 0 = DSI_VIDEO_DST_FORMAT_RGB565. + 1 = DSI_VIDEO_DST_FORMAT_RGB666. + 2 = DSI_VIDEO_DST_FORMAT_RGB666_LOOSE. + 3 = DSI_VIDEO_DST_FORMAT_RGB888 (Default format) + 6 = DSI_CMD_DST_FORMAT_RGB565 + 7 = DSI_CMD_DST_FORMAT_RGB666 + 8 = DSI_CMD_DST_FORMAT_RGB888 +- qcom,mdss-pan-dsi-vc: Specifies the virtual channel identefier. + 0 = default value. +- qcom,mdss-pan-dsi-rgb-swap: Specifies the R, G and B channel ordering. + 0 = DSI_RGB_SWAP_RGB (default value) + 1 = DSI_RGB_SWAP_RBG + 2 = DSI_RGB_SWAP_BGR + 3 = DSI_RGB_SWAP_BRG + 4 = DSI_RGB_SWAP_GRB + 5 = DSI_RGB_SWAP_GBR +- qcom,mdss-pan-dsi-data-lanes: An array that specifies the data lanes enabled. + <1 1 0 0> = data lanes 1 and 2 are enabled.(default). +- qcom,mdss-pan-dsi-t-clk: An array that specifies the byte clock cycles + before and after each mode switch. +- qcom,mdss-pan-dsi-stream: Specifies the packet stream to be used. + 0 = stream 0 (default) + 1 = stream 1 +- qcom,mdss-pan-dsi-mdp-tr: Specifies the trigger mechanism to be used for MDP path. + 0 = no trigger + 2 = Tear check signal line used for trigger + 4 = Triggered by software (default mode) + 6 = Software trigger and TE +- qcom,mdss-pan-dsi-dma-tr: Specifies the trigger mechanism to be used for DMA path. + 0 = no trigger + 2 = Tear check signal line used for trigger + 4 = Triggered by software (default mode) + 5 = Software trigger and start/end of frame trigger. + 6 = Software trigger and TE +- qcom,mdss-pan-dsi-frame-rate: Specifies the frame rate for the panel. + 60 = 60 frames per second (default) + +Note, if a given optional qcom,* binding is not present, then the driver will configure +the default values specified. + +Example: + qcom,mdss_dsi@fd922800 { + + qcom,mdss_dsi_sim_video { + compatible = "qcom,mdss-dsi-panel"; + label = "simulator video mode dsi panel"; + status = "disable"; + qcom,mdss-pan-res = <640 480>; + qcom,mdss-pan-bpp = <24>; + qcom,mdss-pan-porch-values = <6 2 6 6 2 6>; + qcom,mdss-pan-underflow-clr = <0xff>; + qcom,mdss-pan-bl-levels = <1 15>; + qcom,mdss-pan-dsi-mode = <0>; + qcom,mdss-pan-dsi-h-pulse-mode = <1>; + qcom,mdss-pan-dsi-h-power-stop = <1 1 1>; + qcom,mdss-pan-dsi-bllp-power-stop = <1 1>; + qcom,mdss-pan-dsi-traffic-mode = <0>; + qcom,mdss-pan-dsi-dst-format = <3>; + qcom,mdss-pan-dsi-vc = <0>; + qcom,mdss-pan-dsi-rgb-swap = <0>; + qcom,mdss-pan-dsi-data-lanes = <1 1 0 0>; + qcom,mdss-pan-dsi-t-clk = <0x24 0x03>; + qcom,mdss-pan-dsi-stream = <0>; + qcom,mdss-pan-dsi-mdp-tr = <0x04>; + qcom,mdss-pan-dsi-dma-tr = <0x04>; + qcom,mdss-pan-frame-rate = <60>; + qcom,panel-on-cmds = [32 01 00 00 00 02 00 00]; + qcom,panel-off-cmds = [22 01 00 00 00 00 00]; + }; + + }; diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 492437e62e8c..b6294f41ec5d 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -7,4 +7,10 @@ mdss-mdp-objs += mdss_mdp_overlay.o mdss-mdp-objs += mdss_mdp_wb.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o + +mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o +mdss-dsi-objs += mdss_dsi_panel.o +mdss-dsi-objs += msm_mdss_io_8974.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o + obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index f8f4b5e1012a..d0894389bbc1 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -23,6 +23,7 @@ #define MDSS_REG_READ(addr) readl_relaxed(mdss_reg_base + addr) extern unsigned char *mdss_reg_base; +extern spinlock_t dsi_clk_lock; enum mdss_mdp_clk_type { MDSS_CLK_AHB, diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c new file mode 100644 index 000000000000..a79d4f002810 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -0,0 +1,383 @@ +/* Copyright (c) 2012, The Linux Foundation. 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 <linux/module.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of_device.h> + +#include "mdss.h" +#include "mdss_panel.h" +#include "mdss_dsi.h" + +static struct mdss_panel_common_pdata *panel_pdata; + +static unsigned char *mdss_dsi_base; + +static int mdss_dsi_off(struct mdss_panel_data *pdata) +{ + int ret = 0; + struct mdss_panel_info *pinfo; + + pinfo = &pdata->panel_info; + + if (pdata->panel_info.type == MIPI_VIDEO_PANEL) + mdss_dsi_controller_cfg(0, pdata); + + mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata); + + ret = panel_pdata->off(pdata); + if (ret) { + pr_err("%s: Panel OFF failed\n", __func__); + return ret; + } + + spin_lock_bh(&dsi_clk_lock); + mdss_dsi_clk_disable(); + + /* disable dsi engine */ + MIPI_OUTP(mdss_dsi_base + 0x0004, 0); + + spin_unlock_bh(&dsi_clk_lock); + + mdss_dsi_unprepare_clocks(); + + pr_debug("%s-:\n", __func__); + + return ret; +} + +static int mdss_dsi_on(struct mdss_panel_data *pdata) +{ + int ret = 0; + u32 clk_rate; + struct mdss_panel_info *pinfo; + struct mipi_panel_info *mipi; + u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height; + u32 ystride, bpp, data; + u32 dummy_xres, dummy_yres; + + pinfo = &pdata->panel_info; + + cont_splash_clk_ctrl(0); + mdss_dsi_prepare_clocks(); + + spin_lock_bh(&dsi_clk_lock); + + MIPI_OUTP(mdss_dsi_base + 0x118, 1); + MIPI_OUTP(mdss_dsi_base + 0x118, 0); + + mdss_dsi_clk_enable(); + spin_unlock_bh(&dsi_clk_lock); + + clk_rate = pdata->panel_info.clk_rate; + clk_rate = min(clk_rate, pdata->panel_info.clk_max); + + hbp = pdata->panel_info.lcdc.h_back_porch; + hfp = pdata->panel_info.lcdc.h_front_porch; + vbp = pdata->panel_info.lcdc.v_back_porch; + vfp = pdata->panel_info.lcdc.v_front_porch; + hspw = pdata->panel_info.lcdc.h_pulse_width; + vspw = pdata->panel_info.lcdc.v_pulse_width; + width = pdata->panel_info.xres; + height = pdata->panel_info.yres; + + mipi = &pdata->panel_info.mipi; + if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { + dummy_xres = pdata->panel_info.lcdc.xres_pad; + dummy_yres = pdata->panel_info.lcdc.yres_pad; + + MIPI_OUTP(mdss_dsi_base + 0x24, + ((hspw + hbp + width + dummy_xres) << 16 | + (hspw + hbp))); + MIPI_OUTP(mdss_dsi_base + 0x28, + ((vspw + vbp + height + dummy_yres) << 16 | + (vspw + vbp))); + MIPI_OUTP(mdss_dsi_base + 0x2C, + (vspw + vbp + height + dummy_yres + + vfp - 1) << 16 | (hspw + hbp + + width + dummy_xres + hfp - 1)); + + MIPI_OUTP(mdss_dsi_base + 0x30, (hspw << 16)); + MIPI_OUTP(mdss_dsi_base + 0x34, 0); + MIPI_OUTP(mdss_dsi_base + 0x38, (vspw << 16)); + + } else { /* command mode */ + if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888) + bpp = 3; + else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB666) + bpp = 3; + else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565) + bpp = 2; + else + bpp = 3; /* Default format set to RGB888 */ + + ystride = width * bpp + 1; + + /* DSI_COMMAND_MODE_MDP_STREAM_CTRL */ + data = (ystride << 16) | (mipi->vc << 8) | DTYPE_DCS_LWRITE; + MIPI_OUTP(mdss_dsi_base + 0x60, data); + MIPI_OUTP(mdss_dsi_base + 0x58, data); + + /* DSI_COMMAND_MODE_MDP_STREAM_TOTAL */ + data = height << 16 | width; + MIPI_OUTP(mdss_dsi_base + 0x64, data); + MIPI_OUTP(mdss_dsi_base + 0x5C, data); + } + + mdss_dsi_host_init(mipi, pdata); + + if (mipi->force_clk_lane_hs) { + u32 tmp; + + tmp = MIPI_INP(mdss_dsi_base + 0xac); + tmp |= (1<<28); + MIPI_OUTP(mdss_dsi_base + 0xac, tmp); + wmb(); + } + + ret = panel_pdata->on(pdata); + if (ret) { + pr_err("%s: unable to initialize the panel\n", __func__); + return ret; + } + + mdss_dsi_op_mode_config(mipi->mode, pdata); + + pr_debug("%s-:\n", __func__); + return ret; +} + +unsigned char *mdss_dsi_get_base_adr(void) +{ + return mdss_dsi_base; +} + +unsigned char *mdss_dsi_get_clk_base(void) +{ + return mdss_dsi_base; +} + +static int mdss_dsi_resource_initialized; + +static int mdss_dsi_probe(struct platform_device *pdev) +{ + int rc = 0; + pr_debug("%s\n", __func__); + + if (pdev->dev.of_node && !mdss_dsi_resource_initialized) { + struct resource *mdss_dsi_mres; + pdev->id = 1; + mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mdss_dsi_mres) { + pr_err("%s:%d unable to get the MDSS resources", + __func__, __LINE__); + return -ENOMEM; + } + if (mdss_dsi_mres) { + mdss_dsi_base = ioremap(mdss_dsi_mres->start, + resource_size(mdss_dsi_mres)); + if (!mdss_dsi_base) { + pr_err("%s:%d unable to remap dsi resources", + __func__, __LINE__); + return -ENOMEM; + } + } + + if (mdss_dsi_clk_init(pdev)) { + iounmap(mdss_dsi_base); + return -EPERM; + } + + rc = of_platform_populate(pdev->dev.of_node, + NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, + "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + iounmap(mdss_dsi_base); + return rc; + } + + mdss_dsi_resource_initialized = 1; + } + + if (!mdss_dsi_resource_initialized) + return -EPERM; + + return 0; +} + +static int mdss_dsi_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + iounmap(mdss_dsi_base); + return 0; +} + +struct device dsi_dev; + +int dsi_panel_device_register(struct platform_device *pdev, + struct mdss_panel_common_pdata *panel_data) +{ + struct mipi_panel_info *mipi; + int rc; + u8 lanes = 0, bpp; + u32 h_period, v_period, dsi_pclk_rate; + struct mdss_panel_data *pdata = NULL; + + panel_pdata = panel_data; + + h_period = ((panel_pdata->panel_info.lcdc.h_pulse_width) + + (panel_pdata->panel_info.lcdc.h_back_porch) + + (panel_pdata->panel_info.xres) + + (panel_pdata->panel_info.lcdc.h_front_porch)); + + v_period = ((panel_pdata->panel_info.lcdc.v_pulse_width) + + (panel_pdata->panel_info.lcdc.v_back_porch) + + (panel_pdata->panel_info.yres) + + (panel_pdata->panel_info.lcdc.v_front_porch)); + + mipi = &panel_pdata->panel_info.mipi; + + panel_pdata->panel_info.type = + ((mipi->mode == DSI_VIDEO_MODE) + ? MIPI_VIDEO_PANEL : MIPI_CMD_PANEL); + + if (mipi->data_lane3) + lanes += 1; + if (mipi->data_lane2) + lanes += 1; + if (mipi->data_lane1) + lanes += 1; + if (mipi->data_lane0) + lanes += 1; + + + if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB888) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB666_LOOSE)) + bpp = 3; + else if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB565)) + bpp = 2; + else + bpp = 3; /* Default format set to RGB888 */ + + if (panel_pdata->panel_info.type == MIPI_VIDEO_PANEL && + !panel_pdata->panel_info.clk_rate) { + h_period += panel_pdata->panel_info.lcdc.xres_pad; + v_period += panel_pdata->panel_info.lcdc.yres_pad; + + if (lanes > 0) { + panel_pdata->panel_info.clk_rate = + ((h_period * v_period * (mipi->frame_rate) * bpp * 8) + / lanes); + } else { + pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); + panel_pdata->panel_info.clk_rate = + (h_period * v_period + * (mipi->frame_rate) * bpp * 8); + } + } + pll_divider_config.clk_rate = panel_pdata->panel_info.clk_rate; + + rc = mdss_dsi_clk_div_config(bpp, lanes, &dsi_pclk_rate); + if (rc) { + pr_err("%s: unable to initialize the clk dividers\n", __func__); + return rc; + } + + if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 103300000)) + dsi_pclk_rate = 35000000; + mipi->dsi_pclk_rate = dsi_pclk_rate; + + /* + * data chain + */ + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->on = mdss_dsi_on; + pdata->off = mdss_dsi_off; + memcpy(&(pdata->panel_info), &(panel_pdata->panel_info), + sizeof(struct mdss_panel_info)); + + pdata->dsi_base = mdss_dsi_base; + + /* + * register in mdp driver + */ + rc = mdss_register_panel(pdata); + if (rc) { + dev_err(&pdev->dev, "unable to register MIPI DSI panel\n"); + devm_kfree(&pdev->dev, pdata); + return rc; + } + + pr_debug("%s: Panal data initialized\n", __func__); + return 0; +} + +static const struct of_device_id msm_mdss_dsi_dt_match[] = { + {.compatible = "qcom,msm-mdss-dsi"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_mdss_dsi_dt_match); + +static struct platform_driver mdss_dsi_driver = { + .probe = mdss_dsi_probe, + .remove = mdss_dsi_remove, + .shutdown = NULL, + .driver = { + .name = "mdss_dsi", + .of_match_table = msm_mdss_dsi_dt_match, + }, +}; + +static int mdss_dsi_register_driver(void) +{ + return platform_driver_register(&mdss_dsi_driver); +} + +static int __init mdss_dsi_driver_init(void) +{ + int ret; + + mdss_dsi_init(); + + ret = mdss_dsi_register_driver(); + if (ret) { + pr_err("mdss_dsi_register_driver() failed!\n"); + return ret; + } + + return ret; +} +module_init(mdss_dsi_driver_init); + +static void __exit mdss_dsi_driver_cleanup(void) +{ + iounmap(mdss_dsi_base); + platform_driver_unregister(&mdss_dsi_driver); +} +module_exit(mdss_dsi_driver_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DSI controller driver"); +MODULE_AUTHOR("Chandan Uddaraju <chandanu@codeaurora.org>"); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h new file mode 100644 index 000000000000..52baa3e594ed --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -0,0 +1,294 @@ +/* Copyright (c) 2012, The Linux Foundation. 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 MDSS_DSI_H +#define MDSS_DSI_H + +#include <linux/list.h> +#include <mach/scm-io.h> + +#include "mdss_panel.h" + +#define MMSS_MDSS_CC_BASE_PHY 0xFD8C2300 /* mmss clcok control */ +#define MMSS_SERDES_BASE_PHY 0x04f01000 /* mmss (De)Serializer CFG */ + +#define MIPI_OUTP(addr, data) writel_relaxed((data), (addr)) +#define MIPI_INP(addr) readl_relaxed(addr) + +#ifdef CONFIG_MSM_SECURE_IO +#define MIPI_OUTP_SECURE(addr, data) secure_writel((data), (addr)) +#define MIPI_INP_SECURE(addr) secure_readl(addr) +#else +#define MIPI_OUTP_SECURE(addr, data) writel_relaxed((data), (addr)) +#define MIPI_INP_SECURE(addr) readl_relaxed(addr) +#endif + +#define MIPI_DSI_PRIM 1 +#define MIPI_DSI_SECD 2 + +#define MIPI_DSI_PANEL_VGA 0 +#define MIPI_DSI_PANEL_WVGA 1 +#define MIPI_DSI_PANEL_WVGA_PT 2 +#define MIPI_DSI_PANEL_FWVGA_PT 3 +#define MIPI_DSI_PANEL_WSVGA_PT 4 +#define MIPI_DSI_PANEL_QHD_PT 5 +#define MIPI_DSI_PANEL_WXGA 6 +#define MIPI_DSI_PANEL_WUXGA 7 +#define MIPI_DSI_PANEL_720P_PT 8 +#define DSI_PANEL_MAX 8 + +enum { /* mipi dsi panel */ + DSI_VIDEO_MODE, + DSI_CMD_MODE, +}; + +enum { + ST_DSI_CLK_OFF, + ST_DSI_SUSPEND, + ST_DSI_RESUME, + ST_DSI_PLAYING, + ST_DSI_NUM +}; + +enum { + EV_DSI_UPDATE, + EV_DSI_DONE, + EV_DSI_TOUT, + EV_DSI_NUM +}; + +enum { + LANDSCAPE = 1, + PORTRAIT = 2, +}; + +enum dsi_trigger_type { + DSI_CMD_MODE_DMA, + DSI_CMD_MODE_MDP, +}; + +#define DSI_NON_BURST_SYNCH_PULSE 0 +#define DSI_NON_BURST_SYNCH_EVENT 1 +#define DSI_BURST_MODE 2 + +#define DSI_RGB_SWAP_RGB 0 +#define DSI_RGB_SWAP_RBG 1 +#define DSI_RGB_SWAP_BGR 2 +#define DSI_RGB_SWAP_BRG 3 +#define DSI_RGB_SWAP_GRB 4 +#define DSI_RGB_SWAP_GBR 5 + +#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_RGB111 0 +#define DSI_CMD_DST_FORMAT_RGB332 3 +#define DSI_CMD_DST_FORMAT_RGB444 4 +#define DSI_CMD_DST_FORMAT_RGB565 6 +#define DSI_CMD_DST_FORMAT_RGB666 7 +#define DSI_CMD_DST_FORMAT_RGB888 8 + +#define DSI_INTR_ERROR_MASK BIT(25) +#define DSI_INTR_ERROR BIT(24) +#define DSI_INTR_VIDEO_DONE_MASK BIT(17) +#define DSI_INTR_VIDEO_DONE BIT(16) +#define DSI_INTR_CMD_MDP_DONE_MASK BIT(9) +#define DSI_INTR_CMD_MDP_DONE BIT(8) +#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) +#define DSI_INTR_CMD_DMA_DONE BIT(0) + +#define DSI_CMD_TRIGGER_NONE 0x0 /* mdp trigger */ +#define DSI_CMD_TRIGGER_TE 0x02 +#define DSI_CMD_TRIGGER_SW 0x04 +#define DSI_CMD_TRIGGER_SW_SEOF 0x05 /* cmd dma only */ +#define DSI_CMD_TRIGGER_SW_TE 0x06 + +extern struct device dsi_dev; +extern int mdss_dsi_clk_on; +extern u32 dsi_irq; + +struct dsiphy_pll_divider_config { + u32 clk_rate; + u32 fb_divider; + u32 ref_divider_ratio; + u32 bit_clk_divider; /* oCLK1 */ + u32 byte_clk_divider; /* oCLK2 */ + u32 analog_posDiv; + u32 digital_posDiv; +}; + +extern struct dsiphy_pll_divider_config pll_divider_config; + +struct dsi_clk_mnd_table { + u8 lanes; + u8 bpp; + u8 pll_digital_posDiv; + u8 pclk_m; + u8 pclk_n; + u8 pclk_d; +}; + +static const struct dsi_clk_mnd_table mnd_table[] = { + { 1, 2, 8, 1, 1, 0}, + { 1, 3, 12, 1, 1, 0}, + { 2, 2, 4, 1, 1, 0}, + { 2, 3, 6, 1, 1, 0}, + { 3, 2, 1, 3, 8, 4}, + { 3, 3, 4, 1, 1, 0}, + { 4, 2, 2, 1, 1, 0}, + { 4, 3, 3, 1, 1, 0}, +}; + +struct dsi_clk_desc { + u32 src; + u32 m; + u32 n; + u32 d; + u32 mnd_mode; + u32 pre_div_func; +}; + +#define DSI_HOST_HDR_SIZE 4 +#define DSI_HDR_LAST BIT(31) +#define DSI_HDR_LONG_PKT BIT(30) +#define DSI_HDR_BTA BIT(29) +#define DSI_HDR_VC(vc) (((vc) & 0x03) << 22) +#define DSI_HDR_DTYPE(dtype) (((dtype) & 0x03f) << 16) +#define DSI_HDR_DATA2(data) (((data) & 0x0ff) << 8) +#define DSI_HDR_DATA1(data) ((data) & 0x0ff) +#define DSI_HDR_WC(wc) ((wc) & 0x0ffff) + +#define DSI_BUF_SIZE 1024 +#define MDSS_DSI_MRPS 0x04 /* Maximum Return Packet Size */ + +#define MDSS_DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align */ + +struct dsi_buf { + u32 *hdr; /* dsi host header */ + char *start; /* buffer start addr */ + char *end; /* buffer end addr */ + int size; /* size of buffer */ + char *data; /* buffer */ + int len; /* data length */ + dma_addr_t dmap; /* mapped dma addr */ +}; + +/* 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 */ + +/* 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 + +/* + * dcs response + */ +#define DTYPE_ACK_ERR_RESP 0x02 +#define DTYPE_EOT_RESP 0x08 /* end of tx */ +#define DTYPE_GEN_READ1_RESP 0x11 /* 1 parameter, short */ +#define DTYPE_GEN_READ2_RESP 0x12 /* 2 parameter, short */ +#define DTYPE_GEN_LREAD_RESP 0x1a +#define DTYPE_DCS_LREAD_RESP 0x1c +#define DTYPE_DCS_READ1_RESP 0x21 /* 1 parameter, short */ +#define DTYPE_DCS_READ2_RESP 0x22 /* 2 parameter, short */ + +struct dsi_cmd_desc { + int dtype; + int last; + int vc; + int ack; /* ask ACK from peripheral */ + int wait; + int dlen; + char *payload; +}; + +struct dsi_kickoff_action { + struct list_head act_entry; + void (*action) (void *); + void *data; +}; + +struct mdss_panel_common_pdata { + struct mdss_panel_info panel_info; + int (*on) (struct mdss_panel_data *pdata); + int (*off) (struct mdss_panel_data *pdata); +}; + +int dsi_panel_device_register(struct platform_device *pdev, + struct mdss_panel_common_pdata *panel_data); + +char *mdss_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen); +char *mdss_dsi_buf_init(struct dsi_buf *dp); +void mdss_dsi_init(void); +int mdss_dsi_buf_alloc(struct dsi_buf *, int size); +int mdss_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm); +int mdss_dsi_cmds_tx(struct mdss_panel_data *pdata, + struct dsi_buf *dp, struct dsi_cmd_desc *cmds, int cnt); + +int mdss_dsi_cmd_dma_tx(struct dsi_buf *dp, + struct mdss_panel_data *pdata); +int mdss_dsi_cmd_reg_tx(u32 data, + struct mdss_panel_data *pdata); +int mdss_dsi_cmds_rx(struct mdss_panel_data *pdata, + struct dsi_buf *tp, struct dsi_buf *rp, + struct dsi_cmd_desc *cmds, int len); +int mdss_dsi_cmd_dma_rx(struct dsi_buf *tp, int rlen, + struct mdss_panel_data *pdata); +void mdss_dsi_host_init(struct mipi_panel_info *pinfo, + struct mdss_panel_data *pdata); +void mdss_dsi_op_mode_config(int mode, + struct mdss_panel_data *pdata); +void mdss_dsi_cmd_mode_ctrl(int enable); +void mdp4_dsi_cmd_trigger(void); +void mdss_dsi_cmd_mdp_start(void); +void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata); +void mdss_dsi_ack_err_status(unsigned char *dsi_base); +void mdss_dsi_clk_enable(void); +void mdss_dsi_clk_disable(void); +void mdss_dsi_controller_cfg(int enable, + struct mdss_panel_data *pdata); +void mdss_dsi_sw_reset(struct mdss_panel_data *pdata); + +irqreturn_t mdss_dsi_isr(int irq, void *ptr); + +void mipi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); +int mdss_dsi_clk_div_config(u8 bpp, u8 lanes, + u32 *expected_dsi_pclk); +int mdss_dsi_clk_init(struct platform_device *pdev); +void mdss_dsi_clk_deinit(struct device *dev); +void mdss_dsi_prepare_clocks(void); +void mdss_dsi_unprepare_clocks(void); +void cont_splash_clk_ctrl(int enable); +unsigned char *mdss_dsi_get_base_adr(void); + +#endif /* MDSS_DSI_H */ diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c new file mode 100644 index 000000000000..adaf52ba21e0 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -0,0 +1,1259 @@ + +/* Copyright (c) 2012, The Linux Foundation. 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 <linux/module.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/iopoll.h> + +#include "mdss.h" +#include "mdss_dsi.h" + +static struct completion dsi_dma_comp; +static int dsi_irq_enabled; +static spinlock_t dsi_irq_lock; +static spinlock_t dsi_mdp_lock; +static int dsi_mdp_busy; + +spinlock_t dsi_clk_lock; + +struct mdss_hw mdss_dsi_hw = { + .hw_ndx = MDSS_HW_DSI0, + .irq_handler = mdss_dsi_isr, +}; + +void mdss_dsi_init(void) +{ + init_completion(&dsi_dma_comp); + spin_lock_init(&dsi_irq_lock); + spin_lock_init(&dsi_mdp_lock); + spin_lock_init(&dsi_clk_lock); +} + +void mdss_dsi_enable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dsi_irq_lock, flags); + if (dsi_irq_enabled) { + pr_debug("%s: IRQ aleady enabled\n", __func__); + spin_unlock_irqrestore(&dsi_irq_lock, flags); + return; + } + mdss_enable_irq(&mdss_dsi_hw); + dsi_irq_enabled = 1; + /* TO DO: Check whether MDSS IRQ is enabled */ + spin_unlock_irqrestore(&dsi_irq_lock, flags); +} + +void mdss_dsi_disable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dsi_irq_lock, flags); + if (dsi_irq_enabled == 0) { + pr_debug("%s: IRQ already disabled\n", __func__); + spin_unlock_irqrestore(&dsi_irq_lock, flags); + return; + } + mdss_disable_irq(&mdss_dsi_hw); + dsi_irq_enabled = 0; + /* TO DO: Check whether MDSS IRQ is Disabled */ + spin_unlock_irqrestore(&dsi_irq_lock, flags); +} + +/* + * mdss_dsi_disale_irq_nosync() should be called + * from interrupt context + */ +void mdss_dsi_disable_irq_nosync(void) +{ + spin_lock(&dsi_irq_lock); + if (dsi_irq_enabled == 0) { + pr_debug("%s: IRQ cannot be disabled\n", __func__); + spin_unlock(&dsi_irq_lock); + return; + } + + dsi_irq_enabled = 0; + spin_unlock(&dsi_irq_lock); +} + +/* + * mipi dsi buf mechanism + */ +char *mdss_dsi_buf_reserve(struct dsi_buf *dp, int len) +{ + dp->data += len; + return dp->data; +} + +char *mdss_dsi_buf_unreserve(struct dsi_buf *dp, int len) +{ + dp->data -= len; + return dp->data; +} + +char *mdss_dsi_buf_push(struct dsi_buf *dp, int len) +{ + dp->data -= len; + dp->len += len; + return dp->data; +} + +char *mdss_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen) +{ + dp->hdr = (u32 *)dp->data; + return mdss_dsi_buf_reserve(dp, hlen); +} + +char *mdss_dsi_buf_init(struct dsi_buf *dp) +{ + int off; + + dp->data = dp->start; + off = (int)dp->data; + /* 8 byte align */ + off &= 0x07; + if (off) + off = 8 - off; + dp->data += off; + dp->len = 0; + return dp->data; +} + +int mdss_dsi_buf_alloc(struct dsi_buf *dp, int size) +{ + + dp->start = kmalloc(size, GFP_KERNEL); + if (dp->start == NULL) { + pr_err("%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + + dp->end = dp->start + size; + dp->size = size; + + if ((int)dp->start & 0x07) + pr_err("%s: buf NOT 8 bytes aligned\n", __func__); + + dp->data = dp->start; + dp->len = 0; + return size; +} + +/* + * mipi dsi generic long write + */ +static int mdss_dsi_generic_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + char *bp; + u32 *hp; + int i, len; + + bp = mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + + /* fill up payload */ + if (cm->payload) { + len = cm->dlen; + len += 3; + len &= ~0x03; /* multipled by 4 */ + for (i = 0; i < cm->dlen; i++) + *bp++ = cm->payload[i]; + + /* append 0xff to the end */ + for (; i < len; i++) + *bp++ = 0xff; + + dp->len += len; + } + + /* fill up header */ + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_LWRITE); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; +} + +/* + * mipi dsi generic short write with 0, 1 2 parameters + */ +static int mdss_dsi_generic_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + int len; + + if (cm->dlen && cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return 0; + } + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + if (cm->last) + *hp |= DSI_HDR_LAST; + + + len = (cm->dlen > 2) ? 2 : cm->dlen; + + if (len == 1) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE1); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(0); + } else if (len == 2) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE2); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(cm->payload[1]); + } else { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE); + *hp |= DSI_HDR_DATA1(0); + *hp |= DSI_HDR_DATA2(0); + } + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +/* + * mipi dsi gerneric read with 0, 1 2 parameters + */ +static int mdss_dsi_generic_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + int len; + + if (cm->dlen && cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return 0; + } + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_BTA; + if (cm->last) + *hp |= DSI_HDR_LAST; + + len = (cm->dlen > 2) ? 2 : cm->dlen; + + if (len == 1) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ1); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(0); + } else if (len == 2) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ2); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(cm->payload[1]); + } else { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ); + *hp |= DSI_HDR_DATA1(0); + *hp |= DSI_HDR_DATA2(0); + } + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + return dp->len; /* 4 bytes */ +} + +/* + * mipi dsi dcs long write + */ +static int mdss_dsi_dcs_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + char *bp; + u32 *hp; + int i, len; + + bp = mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + + /* + * fill up payload + * dcs command byte (first byte) followed by payload + */ + if (cm->payload) { + len = cm->dlen; + len += 3; + len &= ~0x03; /* multipled by 4 */ + for (i = 0; i < cm->dlen; i++) + *bp++ = cm->payload[i]; + + /* append 0xff to the end */ + for (; i < len; i++) + *bp++ = 0xff; + + dp->len += len; + } + + /* fill up header */ + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_LWRITE); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; +} + +/* + * mipi dsi dcs short write with 0 parameters + */ +static int mdss_dsi_dcs_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + int len; + + if (cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return -EINVAL; + } + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + if (cm->ack) /* ask ACK trigger msg from peripeheral */ + *hp |= DSI_HDR_BTA; + if (cm->last) + *hp |= DSI_HDR_LAST; + + len = (cm->dlen > 1) ? 1 : cm->dlen; + + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE); + *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */ + *hp |= DSI_HDR_DATA2(0); + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + return dp->len; +} + +/* + * mipi dsi dcs short write with 1 parameters + */ +static int mdss_dsi_dcs_swrite1(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + if (cm->dlen < 2 || cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return -EINVAL; + } + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + if (cm->ack) /* ask ACK trigger msg from peripeheral */ + *hp |= DSI_HDR_BTA; + if (cm->last) + *hp |= DSI_HDR_LAST; + + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE1); + *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs comamnd byte */ + *hp |= DSI_HDR_DATA2(cm->payload[1]); /* parameter */ + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; +} +/* + * mipi dsi dcs read with 0 parameters + */ + +static int mdss_dsi_dcs_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + if (cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return -EINVAL; + } + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_BTA; + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_READ); + if (cm->last) + *hp |= DSI_HDR_LAST; + + *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */ + *hp |= DSI_HDR_DATA2(0); + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_cm_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_CM_ON); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_cm_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_CM_OFF); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_peripheral_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_ON); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_peripheral_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_OFF); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_set_max_pktsize(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + if (cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return 0; + } + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_MAX_PKTSIZE); + if (cm->last) + *hp |= DSI_HDR_LAST; + + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(cm->payload[1]); + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_null_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_NULL_PKT); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mdss_dsi_blank_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + u32 *hp; + + mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_BLANK_PKT); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +/* + * prepare cmd buffer to be txed + */ +int mdss_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + int len = 0; + + switch (cm->dtype) { + case DTYPE_GEN_WRITE: + case DTYPE_GEN_WRITE1: + case DTYPE_GEN_WRITE2: + len = mdss_dsi_generic_swrite(dp, cm); + break; + case DTYPE_GEN_LWRITE: + len = mdss_dsi_generic_lwrite(dp, cm); + break; + case DTYPE_GEN_READ: + case DTYPE_GEN_READ1: + case DTYPE_GEN_READ2: + len = mdss_dsi_generic_read(dp, cm); + break; + case DTYPE_DCS_LWRITE: + len = mdss_dsi_dcs_lwrite(dp, cm); + break; + case DTYPE_DCS_WRITE: + len = mdss_dsi_dcs_swrite(dp, cm); + break; + case DTYPE_DCS_WRITE1: + len = mdss_dsi_dcs_swrite1(dp, cm); + break; + case DTYPE_DCS_READ: + len = mdss_dsi_dcs_read(dp, cm); + break; + case DTYPE_MAX_PKTSIZE: + len = mdss_dsi_set_max_pktsize(dp, cm); + break; + case DTYPE_NULL_PKT: + len = mdss_dsi_null_pkt(dp, cm); + break; + case DTYPE_BLANK_PKT: + len = mdss_dsi_blank_pkt(dp, cm); + break; + case DTYPE_CM_ON: + len = mdss_dsi_cm_on(dp, cm); + break; + case DTYPE_CM_OFF: + len = mdss_dsi_cm_off(dp, cm); + break; + case DTYPE_PERIPHERAL_ON: + len = mdss_dsi_peripheral_on(dp, cm); + break; + case DTYPE_PERIPHERAL_OFF: + len = mdss_dsi_peripheral_off(dp, cm); + break; + default: + pr_debug("%s: dtype=%x NOT supported\n", + __func__, cm->dtype); + break; + + } + + return len; +} + +/* + * mdss_dsi_short_read1_resp: 1 parameter + */ +static int mdss_dsi_short_read1_resp(struct dsi_buf *rp) +{ + /* strip out dcs type */ + rp->data++; + rp->len = 1; + return rp->len; +} + +/* + * mdss_dsi_short_read2_resp: 2 parameter + */ +static int mdss_dsi_short_read2_resp(struct dsi_buf *rp) +{ + /* strip out dcs type */ + rp->data++; + rp->len = 2; + return rp->len; +} + +static int mdss_dsi_long_read_resp(struct dsi_buf *rp) +{ + short len; + + len = rp->data[2]; + len <<= 8; + len |= rp->data[1]; + /* strip out dcs header */ + rp->data += 4; + rp->len -= 4; + /* strip out 2 bytes of checksum */ + rp->len -= 2; + return len; +} + +void mdss_dsi_host_init(struct mipi_panel_info *pinfo, + struct mdss_panel_data *pdata) +{ + u32 dsi_ctrl, intr_ctrl; + u32 data; + + pinfo->rgb_swap = DSI_RGB_SWAP_RGB; + + if (pinfo->mode == DSI_VIDEO_MODE) { + data = 0; + if (pinfo->pulse_mode_hsa_he) + data |= BIT(28); + if (pinfo->hfp_power_stop) + data |= BIT(24); + if (pinfo->hbp_power_stop) + data |= BIT(20); + if (pinfo->hsa_power_stop) + data |= BIT(16); + if (pinfo->eof_bllp_power_stop) + data |= BIT(15); + if (pinfo->bllp_power_stop) + data |= BIT(12); + data |= ((pinfo->traffic_mode & 0x03) << 8); + data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */ + data |= (pinfo->vc & 0x03); + MIPI_OUTP((pdata->dsi_base) + 0x0010, data); + + data = 0; + data |= ((pinfo->rgb_swap & 0x07) << 12); + if (pinfo->b_sel) + data |= BIT(8); + if (pinfo->g_sel) + data |= BIT(4); + if (pinfo->r_sel) + data |= BIT(0); + MIPI_OUTP((pdata->dsi_base) + 0x0020, data); + } else if (pinfo->mode == DSI_CMD_MODE) { + data = 0; + data |= ((pinfo->interleave_max & 0x0f) << 20); + data |= ((pinfo->rgb_swap & 0x07) << 16); + if (pinfo->b_sel) + data |= BIT(12); + if (pinfo->g_sel) + data |= BIT(8); + if (pinfo->r_sel) + data |= BIT(4); + data |= (pinfo->dst_format & 0x0f); /* 4 bits */ + MIPI_OUTP((pdata->dsi_base) + 0x003c, data); + + /* DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL */ + data = pinfo->wr_mem_continue & 0x0ff; + data <<= 8; + data |= (pinfo->wr_mem_start & 0x0ff); + if (pinfo->insert_dcs_cmd) + data |= BIT(16); + MIPI_OUTP((pdata->dsi_base) + 0x0044, data); + } else + pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode); + + dsi_ctrl = BIT(8) | BIT(2); /* clock enable & cmd mode */ + intr_ctrl = 0; + intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_CMD_MDP_DONE_MASK); + + if (pinfo->crc_check) + dsi_ctrl |= BIT(24); + if (pinfo->ecc_check) + dsi_ctrl |= BIT(20); + if (pinfo->data_lane3) + dsi_ctrl |= BIT(7); + if (pinfo->data_lane2) + dsi_ctrl |= BIT(6); + if (pinfo->data_lane1) + dsi_ctrl |= BIT(5); + if (pinfo->data_lane0) + dsi_ctrl |= BIT(4); + + /* from frame buffer, low power mode */ + /* DSI_COMMAND_MODE_DMA_CTRL */ + MIPI_OUTP((pdata->dsi_base) + 0x3C, 0x14000000); + + data = 0; + if (pinfo->te_sel) + data |= BIT(31); + data |= pinfo->mdp_trigger << 4;/* cmd mdp trigger */ + data |= pinfo->dma_trigger; /* cmd dma trigger */ + data |= (pinfo->stream & 0x01) << 8; + MIPI_OUTP((pdata->dsi_base) + 0x0084, data); /* DSI_TRIG_CTRL */ + + /* DSI_LAN_SWAP_CTRL */ + MIPI_OUTP((pdata->dsi_base) + 0x00b0, pinfo->dlane_swap); + + /* clock out ctrl */ + data = pinfo->t_clk_post & 0x3f; /* 6 bits */ + data <<= 8; + data |= pinfo->t_clk_pre & 0x3f; /* 6 bits */ + /* DSI_CLKOUT_TIMING_CTRL */ + MIPI_OUTP((pdata->dsi_base) + 0xc4, data); + + data = 0; + if (pinfo->rx_eot_ignore) + data |= BIT(4); + if (pinfo->tx_eot_append) + data |= BIT(0); + MIPI_OUTP((pdata->dsi_base) + 0x00cc, data); /* DSI_EOT_PACKET_CTRL */ + + + /* allow only ack-err-status to generate interrupt */ + /* DSI_ERR_INT_MASK0 */ + MIPI_OUTP((pdata->dsi_base) + 0x010c, 0x13ff3fe0); + + intr_ctrl |= DSI_INTR_ERROR_MASK; + MIPI_OUTP((pdata->dsi_base) + 0x0110, intr_ctrl); /* DSI_INTL_CTRL */ + + /* turn esc, byte, dsi, pclk, sclk, hclk on */ + MIPI_OUTP((pdata->dsi_base) + 0x11c, 0x23f); /* DSI_CLK_CTRL */ + + dsi_ctrl |= BIT(0); /* enable dsi */ + MIPI_OUTP((pdata->dsi_base) + 0x0004, dsi_ctrl); + + wmb(); +} + +void mipi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata) +{ + u32 data = MIPI_INP((pdata->dsi_base) + 0x3c); + + if (mode == 0) + data &= ~BIT(26); + else + data |= BIT(26); + + MIPI_OUTP((pdata->dsi_base) + 0x3c, data); +} + +void mdss_dsi_sw_reset(struct mdss_panel_data *pdata) +{ + MIPI_OUTP((pdata->dsi_base) + 0x118, 0x01); + wmb(); + MIPI_OUTP((pdata->dsi_base) + 0x118, 0x00); + wmb(); +} + +void mdss_dsi_controller_cfg(int enable, + struct mdss_panel_data *pdata) +{ + + u32 dsi_ctrl; + u32 status; + u32 sleep_us = 1000; + u32 timeout_us = 16000; + + /* Check for CMD_MODE_DMA_BUSY */ + if (readl_poll_timeout(((pdata->dsi_base) + 0x0008), + status, + ((status & 0x02) == 0), + sleep_us, timeout_us)) + pr_info("%s: DSI status=%x failed\n", __func__, status); + + /* Check for x_HS_FIFO_EMPTY */ + if (readl_poll_timeout(((pdata->dsi_base) + 0x000c), + status, + ((status & 0x11111000) == 0x11111000), + sleep_us, timeout_us)) + pr_info("%s: FIFO status=%x failed\n", __func__, status); + + dsi_ctrl = MIPI_INP((pdata->dsi_base) + 0x0004); + if (enable) + dsi_ctrl |= 0x01; + else + dsi_ctrl &= ~0x01; + + MIPI_OUTP((pdata->dsi_base) + 0x0004, dsi_ctrl); + wmb(); +} + +void mdss_dsi_op_mode_config(int mode, + struct mdss_panel_data *pdata) +{ + + u32 dsi_ctrl, intr_ctrl; + + dsi_ctrl = MIPI_INP((pdata->dsi_base) + 0x0004); + dsi_ctrl &= ~0x07; + if (mode == DSI_VIDEO_MODE) { + dsi_ctrl |= 0x03; + intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK; + } else { /* command mode */ + dsi_ctrl |= 0x05; + intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK | + DSI_INTR_CMD_MDP_DONE_MASK; + } + + pr_debug("%s: dsi_ctrl=%x intr=%x\n", __func__, dsi_ctrl, intr_ctrl); + + MIPI_OUTP((pdata->dsi_base) + 0x0110, intr_ctrl); /* DSI_INTL_CTRL */ + MIPI_OUTP((pdata->dsi_base) + 0x0004, dsi_ctrl); + wmb(); +} + +void mdss_dsi_cmd_mdp_start(void) +{ + unsigned long flag; + + spin_lock_irqsave(&dsi_mdp_lock, flag); + mdss_dsi_enable_irq(); + dsi_mdp_busy = true; + spin_unlock_irqrestore(&dsi_mdp_lock, flag); +} + + +void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata) +{ + u32 status; + int timeout_us = 10000; + + MIPI_OUTP((pdata->dsi_base) + 0x098, 0x01); /* trigger */ + wmb(); + + /* Check for CMD_MODE_DMA_BUSY */ + if (readl_poll_timeout(((pdata->dsi_base) + 0x0008), + status, ((status & 0x0010) == 0), + 0, timeout_us)) + pr_info("%s: DSI status=%x failed\n", __func__, status); + + mdss_dsi_ack_err_status((pdata->dsi_base)); + + pr_debug("%s: BTA done, status = %d\n", __func__, status); +} + +int mdss_dsi_cmd_reg_tx(u32 data, + struct mdss_panel_data *pdata) +{ + int i; + char *bp; + + bp = (char *)&data; + pr_debug("%s: ", __func__); + for (i = 0; i < 4; i++) + pr_debug("%x ", *bp++); + + pr_debug("\n"); + + MIPI_OUTP((pdata->dsi_base) + 0x0084, 0x04);/* sw trigger */ + MIPI_OUTP((pdata->dsi_base) + 0x0004, 0x135); + + wmb(); + + MIPI_OUTP((pdata->dsi_base) + 0x03c, data); + wmb(); + MIPI_OUTP((pdata->dsi_base) + 0x090, 0x01); /* trigger */ + wmb(); + + udelay(300); + + return 4; +} + +/* + * mdss_dsi_cmds_tx: + * ov_mutex need to be acquired before call this function. + */ +int mdss_dsi_cmds_tx(struct mdss_panel_data *pdata, + struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt) +{ + struct dsi_cmd_desc *cm; + u32 dsi_ctrl, ctrl; + int i, video_mode; + unsigned long flag; + + /* turn on cmd mode + * for video mode, do not send cmds more than + * one pixel line, since it only transmit it + * during BLLP. + */ + dsi_ctrl = MIPI_INP((pdata->dsi_base) + 0x0004); + video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */ + if (video_mode) { + ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */ + MIPI_OUTP((pdata->dsi_base) + 0x0004, ctrl); + } + + spin_lock_irqsave(&dsi_mdp_lock, flag); + mdss_dsi_enable_irq(); + dsi_mdp_busy = true; + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + cm = cmds; + mdss_dsi_buf_init(tp); + for (i = 0; i < cnt; i++) { + mdss_dsi_buf_init(tp); + mdss_dsi_cmd_dma_add(tp, cm); + mdss_dsi_cmd_dma_tx(tp, pdata); + if (cm->wait) + msleep(cm->wait); + cm++; + } + + spin_lock_irqsave(&dsi_mdp_lock, flag); + dsi_mdp_busy = false; + mdss_dsi_disable_irq(); + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (video_mode) + MIPI_OUTP((pdata->dsi_base) + 0x0004, dsi_ctrl); /* restore */ + + return cnt; +} + +/* MDSS_DSI_MRPS, Maximum Return Packet Size */ +static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */ + +static struct dsi_cmd_desc pkt_size_cmd[] = { + {DTYPE_MAX_PKTSIZE, 1, 0, 0, 0, + sizeof(max_pktsize), max_pktsize} +}; + +/* + * DSI panel reply with MAX_RETURN_PACKET_SIZE bytes of data + * plus DCS header, ECC and CRC for DCS long read response + * mdss_dsi_controller only have 4x32 bits register ( 16 bytes) to + * hold data per transaction. + * MDSS_DSI_LEN equal to 8 + * len should be either 4 or 8 + * any return data more than MDSS_DSI_LEN need to be break down + * to multiple transactions. + * + * ov_mutex need to be acquired before call this function. + */ +int mdss_dsi_cmds_rx(struct mdss_panel_data *pdata, + struct dsi_buf *tp, struct dsi_buf *rp, + struct dsi_cmd_desc *cmds, int rlen) +{ + int cnt, len, diff, pkt_size; + unsigned long flag; + char cmd; + + if (pdata->panel_info.mipi.no_max_pkt_size) + rlen = ALIGN(rlen, 4); /* Only support rlen = 4*n */ + + len = rlen; + diff = 0; + + if (len <= 2) + cnt = 4; /* short read */ + else { + if (len > MDSS_DSI_LEN) + len = MDSS_DSI_LEN; /* 8 bytes at most */ + + len = ALIGN(len, 4); /* len 4 bytes align */ + diff = len - rlen; + /* + * add extra 2 bytes to len to have overall + * packet size is multipe by 4. This also make + * sure 4 bytes dcs headerlocates within a + * 32 bits register after shift in. + * after all, len should be either 6 or 10. + */ + len += 2; + cnt = len + 6; /* 4 bytes header + 2 bytes crc */ + } + + spin_lock_irqsave(&dsi_mdp_lock, flag); + mdss_dsi_enable_irq(); + dsi_mdp_busy = true; + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (!pdata->panel_info.mipi.no_max_pkt_size) { + /* packet size need to be set at every read */ + pkt_size = len; + max_pktsize[0] = pkt_size; + mdss_dsi_buf_init(tp); + mdss_dsi_cmd_dma_add(tp, pkt_size_cmd); + mdss_dsi_cmd_dma_tx(tp, pdata); + } + + mdss_dsi_buf_init(tp); + mdss_dsi_cmd_dma_add(tp, cmds); + + /* transmit read comamnd to client */ + mdss_dsi_cmd_dma_tx(tp, pdata); + /* + * once cmd_dma_done interrupt received, + * return data from client is ready and stored + * at RDBK_DATA register already + */ + mdss_dsi_buf_init(rp); + if (pdata->panel_info.mipi.no_max_pkt_size) { + /* + * expect rlen = n * 4 + * short alignement for start addr + */ + rp->data += 2; + } + + mdss_dsi_cmd_dma_rx(rp, cnt, pdata); + + spin_lock_irqsave(&dsi_mdp_lock, flag); + dsi_mdp_busy = false; + mdss_dsi_disable_irq(); + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (pdata->panel_info.mipi.no_max_pkt_size) { + /* + * remove extra 2 bytes from previous + * rx transaction at shift register + * which was inserted during copy + * shift registers to rx buffer + * rx payload start from long alignment addr + */ + rp->data += 2; + } + + cmd = rp->data[0]; + switch (cmd) { + case DTYPE_ACK_ERR_RESP: + pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__); + break; + case DTYPE_GEN_READ1_RESP: + case DTYPE_DCS_READ1_RESP: + mdss_dsi_short_read1_resp(rp); + break; + case DTYPE_GEN_READ2_RESP: + case DTYPE_DCS_READ2_RESP: + mdss_dsi_short_read2_resp(rp); + break; + case DTYPE_GEN_LREAD_RESP: + case DTYPE_DCS_LREAD_RESP: + mdss_dsi_long_read_resp(rp); + rp->len -= 2; /* extra 2 bytes added */ + rp->len -= diff; /* align bytes */ + break; + default: + break; + } + + return rp->len; +} + +int mdss_dsi_cmd_dma_tx(struct dsi_buf *tp, + struct mdss_panel_data *pdata) +{ + int len; + int i; + char *bp; + + bp = tp->data; + + pr_debug("%s: ", __func__); + for (i = 0; i < tp->len; i++) + pr_debug("%x ", *bp++); + + pr_debug("\n"); + + len = tp->len; + len += 3; + len &= ~0x03; /* multipled by 4 */ + + tp->dmap = dma_map_single(&dsi_dev, tp->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(&dsi_dev, tp->dmap)) + pr_err("%s: dmap mapp failed\n", __func__); + + INIT_COMPLETION(dsi_dma_comp); + + MIPI_OUTP((pdata->dsi_base) + 0x048, tp->dmap); + MIPI_OUTP((pdata->dsi_base) + 0x04c, len); + wmb(); + + MIPI_OUTP((pdata->dsi_base) + 0x090, 0x01); /* trigger */ + wmb(); + + wait_for_completion(&dsi_dma_comp); + + dma_unmap_single(&dsi_dev, tp->dmap, len, DMA_TO_DEVICE); + tp->dmap = 0; + return tp->len; +} + +int mdss_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen, + struct mdss_panel_data *pdata) +{ + u32 *lp, data; + int i, off, cnt; + + lp = (u32 *)rp->data; + cnt = rlen; + cnt += 3; + cnt >>= 2; + + if (cnt > 4) + cnt = 4; /* 4 x 32 bits registers only */ + + off = 0x06c; /* DSI_RDBK_DATA0 */ + off += ((cnt - 1) * 4); + + + for (i = 0; i < cnt; i++) { + data = (u32)MIPI_INP((pdata->dsi_base) + off); + *lp++ = ntohl(data); /* to network byte order */ + off -= 4; + rp->len += sizeof(*lp); + } + + return rlen; +} + +void mdss_dsi_ack_err_status(unsigned char *dsi_base) +{ + u32 status; + + status = MIPI_INP(dsi_base + 0x0068);/* DSI_ACK_ERR_STATUS */ + + if (status) { + MIPI_OUTP(dsi_base + 0x0068, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mdss_dsi_timeout_status(unsigned char *dsi_base) +{ + u32 status; + + status = MIPI_INP(dsi_base + 0x00c0);/* DSI_TIMEOUT_STATUS */ + if (status & 0x0111) { + MIPI_OUTP(dsi_base + 0x00c0, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mdss_dsi_dln0_phy_err(unsigned char *dsi_base) +{ + u32 status; + + status = MIPI_INP(dsi_base + 0x00b4);/* DSI_DLN0_PHY_ERR */ + + if (status & 0x011111) { + MIPI_OUTP(dsi_base + 0x00b4, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mdss_dsi_fifo_status(unsigned char *dsi_base) +{ + u32 status; + + status = MIPI_INP(dsi_base + 0x000c);/* DSI_FIFO_STATUS */ + + if (status & 0x44444489) { + MIPI_OUTP(dsi_base + 0x000c, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mdss_dsi_status(unsigned char *dsi_base) +{ + u32 status; + + status = MIPI_INP(dsi_base + 0x0008);/* DSI_STATUS */ + + if (status & 0x80000000) { + MIPI_OUTP(dsi_base + 0x0008, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mdss_dsi_error(unsigned char *dsi_base) +{ + /* DSI_ERR_INT_MASK0 */ + mdss_dsi_ack_err_status(dsi_base); /* mask0, 0x01f */ + mdss_dsi_timeout_status(dsi_base); /* mask0, 0x0e0 */ + mdss_dsi_fifo_status(dsi_base); /* mask0, 0x133d00 */ + mdss_dsi_status(dsi_base); /* mask0, 0xc0100 */ + mdss_dsi_dln0_phy_err(dsi_base); /* mask0, 0x3e00000 */ +} + + +irqreturn_t mdss_dsi_isr(int irq, void *ptr) +{ + u32 isr; + unsigned char *dsi_base; + + dsi_base = mdss_dsi_get_base_adr(); + if (!dsi_base) + pr_err("%s:%d DSI base adr no Initialized", + __func__, __LINE__); + + isr = MIPI_INP(dsi_base + 0x0110);/* DSI_INTR_CTRL */ + MIPI_OUTP(dsi_base + 0x0110, isr); + + if (isr & DSI_INTR_ERROR) + mdss_dsi_error(dsi_base); + + if (isr & DSI_INTR_VIDEO_DONE) { + /* + * do something here + */ + } + + if (isr & DSI_INTR_CMD_DMA_DONE) + complete(&dsi_dma_comp); + + if (isr & DSI_INTR_CMD_MDP_DONE) { + spin_lock(&dsi_mdp_lock); + dsi_mdp_busy = false; + mdss_dsi_disable_irq_nosync(); + spin_unlock(&dsi_mdp_lock); + } + + return IRQ_HANDLED; +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c new file mode 100644 index 000000000000..5f7802b5397a --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -0,0 +1,358 @@ +/* Copyright (c) 2012, The Linux Foundation. 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 <linux/module.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include "mdss_dsi.h" + +#define DT_CMD_HDR 6 + +static struct dsi_buf dsi_panel_tx_buf; +static struct dsi_buf dsi_panel_rx_buf; + +static struct dsi_cmd_desc *dsi_panel_on_cmds; +static struct dsi_cmd_desc *dsi_panel_off_cmds; +static int num_of_on_cmds; +static int num_of_off_cmds; +static char *on_cmds, *off_cmds; + +static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) +{ + struct mipi_panel_info *mipi; + + mipi = &pdata->panel_info.mipi; + + pr_debug("%s:%d, debug info (mode) : %d\n", __func__, __LINE__, + mipi->mode); + + if (mipi->mode == DSI_VIDEO_MODE) { + mdss_dsi_cmds_tx(pdata, &dsi_panel_tx_buf, dsi_panel_on_cmds, + num_of_on_cmds); + } else { + pr_err("%s:%d, CMD MODE NOT SUPPORTED", __func__, __LINE__); + return -EINVAL; + } + + return 0; +} + +static int mdss_dsi_panel_off(struct mdss_panel_data *pdata) +{ + struct mipi_panel_info *mipi; + + mipi = &pdata->panel_info.mipi; + + pr_debug("%s:%d, debug info\n", __func__, __LINE__); + + if (mipi->mode == DSI_VIDEO_MODE) { + mdss_dsi_cmds_tx(pdata, &dsi_panel_tx_buf, dsi_panel_off_cmds, + num_of_off_cmds); + } else { + pr_debug("%s:%d, CMD mode not supported", __func__, __LINE__); + return -EINVAL; + } + + return 0; +} + +static int mdss_panel_parse_dt(struct platform_device *pdev, + struct mdss_panel_common_pdata *panel_data) +{ + struct device_node *np = pdev->dev.of_node; + u32 res[6], tmp; + int rc, i, len; + int cmd_plen, data_offset; + const char *data; + + rc = of_property_read_u32_array(np, "qcom,mdss-pan-res", res, 2); + if (rc) { + pr_err("%s:%d, panel resolution not specified\n", + __func__, __LINE__); + return -EINVAL; + } + panel_data->panel_info.xres = (!rc ? res[0] : 640); + panel_data->panel_info.yres = (!rc ? res[1] : 480); + + rc = of_property_read_u32(np, "qcom,mdss-pan-bpp", &tmp); + if (rc) { + pr_err("%s:%d, panel bpp not specified\n", + __func__, __LINE__); + return -EINVAL; + } + panel_data->panel_info.bpp = (!rc ? tmp : 24); + + rc = of_property_read_u32_array(np, + "qcom,mdss-pan-porch-values", res, 6); + panel_data->panel_info.lcdc.h_back_porch = (!rc ? res[0] : 6); + panel_data->panel_info.lcdc.h_pulse_width = (!rc ? res[1] : 2); + panel_data->panel_info.lcdc.h_front_porch = (!rc ? res[2] : 6); + panel_data->panel_info.lcdc.v_back_porch = (!rc ? res[3] : 6); + panel_data->panel_info.lcdc.v_pulse_width = (!rc ? res[4] : 2); + panel_data->panel_info.lcdc.v_front_porch = (!rc ? res[5] : 6); + + rc = of_property_read_u32(np, + "qcom,mdss-pan-underflow-clr", &tmp); + panel_data->panel_info.lcdc.underflow_clr = (!rc ? tmp : 0xff); + + rc = of_property_read_u32_array(np, + "qcom,mdss-pan-bl-levels", res, 2); + panel_data->panel_info.bl_min = (!rc ? res[0] : 0); + panel_data->panel_info.bl_max = (!rc ? res[1] : 255); + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-mode", &tmp); + panel_data->panel_info.mipi.mode = (!rc ? tmp : DSI_VIDEO_MODE); + + rc = of_property_read_u32(np, + "qcom,mdss-pan-dsi-h-pulse-mode", &tmp); + panel_data->panel_info.mipi.pulse_mode_hsa_he = (!rc ? tmp : false); + + rc = of_property_read_u32_array(np, + "qcom,mdss-pan-dsi-h-power-stop", res, 3); + panel_data->panel_info.mipi.hbp_power_stop = (!rc ? res[0] : false); + panel_data->panel_info.mipi.hsa_power_stop = (!rc ? res[1] : false); + panel_data->panel_info.mipi.hfp_power_stop = (!rc ? res[2] : false); + + rc = of_property_read_u32_array(np, + "qcom,mdss-pan-dsi-bllp-power-stop", res, 2); + panel_data->panel_info.mipi.bllp_power_stop = + (!rc ? res[0] : false); + panel_data->panel_info.mipi.eof_bllp_power_stop = + (!rc ? res[1] : false); + + rc = of_property_read_u32(np, + "qcom,mdss-pan-dsi-traffic-mode", &tmp); + panel_data->panel_info.mipi.traffic_mode = + (!rc ? tmp : DSI_NON_BURST_SYNCH_PULSE); + + rc = of_property_read_u32(np, + "qcom,mdss-pan-dsi-dst-format", &tmp); + panel_data->panel_info.mipi.dst_format = + (!rc ? tmp : DSI_VIDEO_DST_FORMAT_RGB888); + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-vc", &tmp); + panel_data->panel_info.mipi.vc = (!rc ? tmp : 0); + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-rgb-swap", &tmp); + panel_data->panel_info.mipi.rgb_swap = (!rc ? tmp : DSI_RGB_SWAP_RGB); + + rc = of_property_read_u32_array(np, + "qcom,mdss-pan-dsi-data-lanes", res, 4); + panel_data->panel_info.mipi.data_lane0 = (!rc ? res[0] : true); + panel_data->panel_info.mipi.data_lane1 = (!rc ? res[1] : false); + panel_data->panel_info.mipi.data_lane2 = (!rc ? res[2] : false); + panel_data->panel_info.mipi.data_lane3 = (!rc ? res[3] : false); + + rc = of_property_read_u32_array(np, "qcom,mdss-pan-dsi-t-clk", res, 2); + panel_data->panel_info.mipi.t_clk_pre = (!rc ? res[0] : 0x24); + panel_data->panel_info.mipi.t_clk_post = (!rc ? res[1] : 0x03); + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-stream", &tmp); + panel_data->panel_info.mipi.stream = (!rc ? tmp : 0); + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-mdp-tr", &tmp); + panel_data->panel_info.mipi.mdp_trigger = + (!rc ? tmp : DSI_CMD_TRIGGER_SW); + if (panel_data->panel_info.mipi.mdp_trigger > 6) { + pr_err("%s:%d, Invalid mdp trigger. Forcing to sw trigger", + __func__, __LINE__); + panel_data->panel_info.mipi.mdp_trigger = + DSI_CMD_TRIGGER_SW; + } + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-dma-tr", &tmp); + panel_data->panel_info.mipi.dma_trigger = + (!rc ? tmp : DSI_CMD_TRIGGER_SW); + if (panel_data->panel_info.mipi.dma_trigger > 6) { + pr_err("%s:%d, Invalid dma trigger. Forcing to sw trigger", + __func__, __LINE__); + panel_data->panel_info.mipi.dma_trigger = + DSI_CMD_TRIGGER_SW; + } + + rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-frame-rate", &tmp); + panel_data->panel_info.mipi.frame_rate = (!rc ? tmp : 60); + + data = of_get_property(np, "qcom,panel-on-cmds", &len); + if (!data) { + pr_err("%s:%d, Unable to read ON cmds", __func__, __LINE__); + goto error; + } + + on_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL); + if (!on_cmds) + return -ENOMEM; + + memcpy(on_cmds, data, len); + + data_offset = 0; + cmd_plen = 0; + while ((len - data_offset) >= DT_CMD_HDR) { + data_offset += (DT_CMD_HDR - 1); + cmd_plen = on_cmds[data_offset++]; + data_offset += cmd_plen; + num_of_on_cmds++; + } + if (!num_of_on_cmds) { + pr_err("%s:%d, No ON cmds specified", __func__, __LINE__); + goto error; + } + + dsi_panel_on_cmds = + kzalloc((num_of_on_cmds * sizeof(struct dsi_cmd_desc)), + GFP_KERNEL); + if (!dsi_panel_on_cmds) + return -ENOMEM; + + data_offset = 0; + for (i = 0; i < num_of_on_cmds; i++) { + dsi_panel_on_cmds[i].dtype = on_cmds[data_offset++]; + dsi_panel_on_cmds[i].last = on_cmds[data_offset++]; + dsi_panel_on_cmds[i].vc = on_cmds[data_offset++]; + dsi_panel_on_cmds[i].ack = on_cmds[data_offset++]; + dsi_panel_on_cmds[i].wait = on_cmds[data_offset++]; + dsi_panel_on_cmds[i].dlen = on_cmds[data_offset++]; + dsi_panel_on_cmds[i].payload = &on_cmds[data_offset]; + data_offset += (dsi_panel_on_cmds[i].dlen); + } + + if (data_offset != len) { + pr_err("%s:%d, Incorrect ON command entries", + __func__, __LINE__); + goto error; + } + + data = of_get_property(np, "qcom,panel-off-cmds", &len); + if (!data) { + pr_err("%s:%d, Unable to read OFF cmds", __func__, __LINE__); + goto error; + } + + off_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL); + if (!off_cmds) + return -ENOMEM; + + memcpy(off_cmds, data, len); + + data_offset = 0; + cmd_plen = 0; + while ((len - data_offset) >= DT_CMD_HDR) { + data_offset += (DT_CMD_HDR - 1); + cmd_plen = off_cmds[data_offset++]; + data_offset += cmd_plen; + num_of_off_cmds++; + } + if (!num_of_off_cmds) { + pr_err("%s:%d, No OFF cmds specified", __func__, __LINE__); + goto error; + } + + dsi_panel_off_cmds = kzalloc(num_of_off_cmds + * sizeof(struct dsi_cmd_desc), + GFP_KERNEL); + if (!dsi_panel_off_cmds) + return -ENOMEM; + + data_offset = 0; + for (i = 0; i < num_of_off_cmds; i++) { + dsi_panel_off_cmds[i].dtype = off_cmds[data_offset++]; + dsi_panel_off_cmds[i].last = off_cmds[data_offset++]; + dsi_panel_off_cmds[i].vc = off_cmds[data_offset++]; + dsi_panel_off_cmds[i].ack = off_cmds[data_offset++]; + dsi_panel_off_cmds[i].wait = off_cmds[data_offset++]; + dsi_panel_off_cmds[i].dlen = off_cmds[data_offset++]; + dsi_panel_off_cmds[i].payload = &off_cmds[data_offset]; + data_offset += (dsi_panel_off_cmds[i].dlen); + } + + if (data_offset != len) { + pr_err("%s:%d, Incorrect OFF command entries", + __func__, __LINE__); + goto error; + } + + return 0; +error: + kfree(dsi_panel_on_cmds); + kfree(dsi_panel_off_cmds); + kfree(on_cmds); + kfree(off_cmds); + + return -EINVAL; +} + +static int mdss_dsi_panel_probe(struct platform_device *pdev) +{ + int rc = 0; + struct mdss_panel_common_pdata *vendor_pdata = NULL; + static const char *panel_name; + + if (pdev->dev.parent == NULL) { + pr_err("%s: parent device missing\n", __func__); + return -ENODEV; + } + + pr_debug("%s:%d, debug info id=%d", __func__, __LINE__, pdev->id); + if (!pdev->dev.of_node) + return -ENODEV; + + panel_name = of_get_property(pdev->dev.of_node, "label", NULL); + if (!panel_name) + pr_info("%s:%d, panel name not specified\n", + __func__, __LINE__); + else + pr_info("%s: Panel Name = %s\n", __func__, panel_name); + + vendor_pdata = devm_kzalloc(&pdev->dev, + sizeof(*vendor_pdata), GFP_KERNEL); + if (!vendor_pdata) + return -ENOMEM; + + rc = mdss_panel_parse_dt(pdev, vendor_pdata); + if (rc) { + devm_kfree(&pdev->dev, vendor_pdata); + vendor_pdata = NULL; + return rc; + } + vendor_pdata->on = mdss_dsi_panel_on; + vendor_pdata->off = mdss_dsi_panel_off; + + rc = dsi_panel_device_register(pdev, vendor_pdata); + if (rc) + return rc; + + return 0; +} + +static const struct of_device_id mdss_dsi_panel_match[] = { + {.compatible = "qcom,mdss-dsi-panel"}, + {} +}; + +static struct platform_driver this_driver = { + .probe = mdss_dsi_panel_probe, + .driver = { + .name = "dsi_panel", + .of_match_table = mdss_dsi_panel_match, + }, +}; + +static int __init mdss_dsi_panel_init(void) +{ + mdss_dsi_buf_alloc(&dsi_panel_tx_buf, DSI_BUF_SIZE); + mdss_dsi_buf_alloc(&dsi_panel_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} +module_init(mdss_dsi_panel_init); diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 068bbf8f19d2..d013a4f8e724 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -375,6 +375,7 @@ enum mdss_mpd_intf_index { #define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8 #define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC #define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0 +#define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F enum mdss_mdp_pingpong_index { MDSS_MDP_PINGPONG0, diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 69ea04ee15d5..4541dada60c6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -135,6 +135,8 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, p->hsync_skew); MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_POLARITY_CTL, polarity_ctl); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT, + MDSS_MDP_PANEL_FORMAT_RGB888); return 0; } @@ -297,14 +299,14 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) itp.underflow_clr = pinfo->lcdc.underflow_clr; itp.hsync_skew = pinfo->lcdc.hsync_skew; - itp.xres = fbi->var.xres; - itp.yres = fbi->var.yres; - itp.h_back_porch = fbi->var.left_margin; - itp.h_front_porch = fbi->var.right_margin; - itp.v_back_porch = fbi->var.upper_margin; - itp.v_front_porch = fbi->var.lower_margin; - itp.hsync_pulse_width = fbi->var.hsync_len; - itp.vsync_pulse_width = fbi->var.vsync_len; + itp.xres = pinfo->xres; + itp.yres = pinfo->yres; + itp.h_back_porch = pinfo->lcdc.h_back_porch; + itp.h_front_porch = pinfo->lcdc.h_front_porch; + itp.v_back_porch = pinfo->lcdc.v_back_porch; + itp.v_front_porch = pinfo->lcdc.h_front_porch; + itp.hsync_pulse_width = pinfo->lcdc.h_pulse_width; + itp.vsync_pulse_width = pinfo->lcdc.v_pulse_width; if (mdss_mdp_video_timegen_setup(ctl, &itp)) { pr_err("unable to get timing parameters\n"); diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 0bc253e7bb63..7ee382985af5 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -167,6 +167,7 @@ struct mdss_panel_info { struct mdss_panel_data { struct mdss_panel_info panel_info; void (*set_backlight) (u32 bl_level); + unsigned char *dsi_base; /* function entry chain */ int (*on) (struct mdss_panel_data *pdata); diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c new file mode 100644 index 000000000000..b92a6caf8fb3 --- /dev/null +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2012, The Linux Foundation. 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 <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/clk.h> +#include <mach/msm_iomap.h> + +#include "mdss_dsi.h" + +#define SW_RESET BIT(2) +#define SW_RESET_PLL BIT(0) +#define PWRDN_B BIT(7) + +static struct dsi_clk_desc dsi_pclk; + +static struct clk *dsi_byte_div_clk; +static struct clk *dsi_esc_clk; + +int mdss_dsi_clk_on; + +int mdss_dsi_clk_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dsi_byte_div_clk = clk_get(dev, "byte_clk"); + if (IS_ERR(dsi_byte_div_clk)) { + pr_err("can't find dsi_byte_div_clk\n"); + dsi_byte_div_clk = NULL; + goto mdss_dsi_clk_err; + } + + dsi_esc_clk = clk_get(dev, "core_clk"); + if (IS_ERR(dsi_esc_clk)) { + printk(KERN_ERR "can't find dsi_esc_clk\n"); + dsi_esc_clk = NULL; + goto mdss_dsi_clk_err; + } + + return 0; + +mdss_dsi_clk_err: + mdss_dsi_clk_deinit(dev); + return -EPERM; +} + +void mdss_dsi_clk_deinit(struct device *dev) +{ + if (dsi_byte_div_clk) + clk_put(dsi_byte_div_clk); + if (dsi_esc_clk) + clk_put(dsi_esc_clk); +} + +#define PREF_DIV_RATIO 27 +struct dsiphy_pll_divider_config pll_divider_config; + +int mdss_dsi_clk_div_config(u8 bpp, u8 lanes, + u32 *expected_dsi_pclk) +{ + u32 fb_divider, rate, vco; + u32 div_ratio = 0; + u32 pll_analog_posDiv = 1; + struct dsi_clk_mnd_table const *mnd_entry = mnd_table; + if (pll_divider_config.clk_rate == 0) + pll_divider_config.clk_rate = 454000000; + + rate = (pll_divider_config.clk_rate / 2) + / 1000000; /* Half Bit Clock In Mhz */ + + if (rate < 43) { + vco = rate * 16; + div_ratio = 16; + pll_analog_posDiv = 8; + } else if (rate < 85) { + vco = rate * 8; + div_ratio = 8; + pll_analog_posDiv = 4; + } else if (rate < 170) { + vco = rate * 4; + div_ratio = 4; + pll_analog_posDiv = 2; + } else if (rate < 340) { + vco = rate * 2; + div_ratio = 2; + pll_analog_posDiv = 1; + } else { + /* DSI PLL Direct path configuration */ + vco = rate * 1; + div_ratio = 1; + pll_analog_posDiv = 1; + } + + /* find the mnd settings from mnd_table entry */ + for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { + if (((mnd_entry->lanes) == lanes) && + ((mnd_entry->bpp) == bpp)) + break; + } + + if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) { + pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n", + __func__, lanes, bpp); + return -EINVAL; + } + fb_divider = ((vco * PREF_DIV_RATIO) / 27); + pll_divider_config.fb_divider = fb_divider; + pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO; + pll_divider_config.bit_clk_divider = div_ratio; + pll_divider_config.byte_clk_divider = + pll_divider_config.bit_clk_divider * 8; + pll_divider_config.analog_posDiv = pll_analog_posDiv; + pll_divider_config.digital_posDiv = + (mnd_entry->pll_digital_posDiv) * div_ratio; + + if ((mnd_entry->pclk_d == 0) + || (mnd_entry->pclk_m == 1)) { + dsi_pclk.mnd_mode = 0; + dsi_pclk.src = 0x3; + dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1); + } else { + dsi_pclk.mnd_mode = 2; + dsi_pclk.src = 0x3; + dsi_pclk.m = mnd_entry->pclk_m; + dsi_pclk.n = mnd_entry->pclk_n; + dsi_pclk.d = mnd_entry->pclk_d; + } + *expected_dsi_pclk = (((pll_divider_config.clk_rate) * lanes) + / (8 * bpp)); + + return 0; +} + +void cont_splash_clk_ctrl(int enable) +{ + static int cont_splash_clks_enabled; + if (enable && !cont_splash_clks_enabled) { + clk_prepare_enable(dsi_byte_div_clk); + clk_prepare_enable(dsi_esc_clk); + cont_splash_clks_enabled = 1; + } else if (!enable && cont_splash_clks_enabled) { + clk_disable_unprepare(dsi_byte_div_clk); + clk_disable_unprepare(dsi_esc_clk); + cont_splash_clks_enabled = 0; + } +} + +void mdss_dsi_prepare_clocks(void) +{ + clk_prepare(dsi_byte_div_clk); + clk_prepare(dsi_esc_clk); +} + +void mdss_dsi_unprepare_clocks(void) +{ + clk_unprepare(dsi_esc_clk); + clk_unprepare(dsi_byte_div_clk); +} + +void mdss_dsi_clk_enable(void) +{ + if (mdss_dsi_clk_on) { + pr_info("%s: mdss_dsi_clks already ON\n", __func__); + return; + } + + if (clk_set_rate(dsi_byte_div_clk, 1) < 0) /* divided by 1 */ + pr_err("%s: dsi_byte_div_clk - clk_set_rate failed\n", + __func__); + if (clk_set_rate(dsi_esc_clk, 2) < 0) /* divided by 2 */ + pr_err("%s: dsi_esc_clk - clk_set_rate failed\n", + __func__); + clk_enable(dsi_byte_div_clk); + clk_enable(dsi_esc_clk); + mdss_dsi_clk_on = 1; +} + +void mdss_dsi_clk_disable(void) +{ + if (mdss_dsi_clk_on == 0) { + pr_info("%s: mdss_dsi_clks already OFF\n", __func__); + return; + } + clk_disable(dsi_esc_clk); + clk_disable(dsi_byte_div_clk); + mdss_dsi_clk_on = 0; +} |
