blob: 8f4f4d51ccfdc3d0b5753e829ed907afeb341175 [file] [log] [blame]
/* Copyright (c) 2012, Code Aurora Forum. 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 <linux/err.h>
#include <linux/regulator/consumer.h>
#include "mdss.h"
#include "mdss_panel.h"
#include "mdss_dsi.h"
static struct mdss_dsi_drv_pdata dsi_drv;
static unsigned char *mdss_dsi_base;
static int mdss_dsi_regulator_init(struct platform_device *pdev)
{
int ret;
dsi_drv.vdd_vreg = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(dsi_drv.vdd_vreg)) {
pr_err("could not get 8941_l22, rc = %ld\n",
PTR_ERR(dsi_drv.vdd_vreg));
return -ENODEV;
}
ret = regulator_set_voltage(dsi_drv.vdd_vreg, 3000000, 3000000);
if (ret) {
pr_err("vdd_vreg->set_voltage failed, rc=%d\n", ret);
return -EINVAL;
}
dsi_drv.vdd_io_vreg = devm_regulator_get(&pdev->dev, "vdd_io");
if (IS_ERR(dsi_drv.vdd_io_vreg)) {
pr_err("could not get 8941_l12, rc = %ld\n",
PTR_ERR(dsi_drv.vdd_io_vreg));
return -ENODEV;
}
ret = regulator_set_voltage(dsi_drv.vdd_io_vreg, 1800000, 1800000);
if (ret) {
pr_err("vdd_io_vreg->set_voltage failed, rc=%d\n", ret);
return -EINVAL;
}
dsi_drv.dsi_vreg = devm_regulator_get(&pdev->dev, "vreg");
if (IS_ERR(dsi_drv.dsi_vreg)) {
pr_err("could not get 8941_l2, rc = %ld\n",
PTR_ERR(dsi_drv.dsi_vreg));
return -ENODEV;
}
ret = regulator_set_voltage(dsi_drv.dsi_vreg, 1200000, 1200000);
if (ret) {
pr_err("dsi_vreg->set_voltage failed, rc=%d\n", ret);
return -EINVAL;
}
return 0;
}
static int mdss_dsi_panel_power_on(int enable)
{
int ret;
pr_debug("%s: enable=%d\n", __func__, enable);
if (enable) {
ret = regulator_set_optimum_mode(dsi_drv.vdd_vreg, 100000);
if (ret < 0) {
pr_err("%s: vdd_vreg set regulator mode failed.\n",
__func__);
return ret;
}
ret = regulator_set_optimum_mode(dsi_drv.vdd_io_vreg, 100000);
if (ret < 0) {
pr_err("%s: vdd_io_vreg set regulator mode failed.\n",
__func__);
return ret;
}
ret = regulator_set_optimum_mode(dsi_drv.dsi_vreg, 100000);
if (ret < 0) {
pr_err("%s: dsi_vreg set regulator mode failed.\n",
__func__);
return ret;
}
ret = regulator_enable(dsi_drv.vdd_io_vreg);
if (ret) {
pr_err("%s: Failed to enable regulator.\n", __func__);
return ret;
}
msleep(20);
wmb();
ret = regulator_enable(dsi_drv.vdd_vreg);
if (ret) {
pr_err("%s: Failed to enable regulator.\n", __func__);
return ret;
}
msleep(20);
wmb();
ret = regulator_enable(dsi_drv.dsi_vreg);
if (ret) {
pr_err("%s: Failed to enable regulator.\n", __func__);
return ret;
}
mdss_dsi_panel_reset(1);
} else {
mdss_dsi_panel_reset(0);
ret = regulator_disable(dsi_drv.vdd_vreg);
if (ret) {
pr_err("%s: Failed to disable regulator.\n", __func__);
return ret;
}
ret = regulator_disable(dsi_drv.dsi_vreg);
if (ret) {
pr_err("%s: Failed to disable regulator.\n", __func__);
return ret;
}
ret = regulator_disable(dsi_drv.vdd_io_vreg);
if (ret) {
pr_err("%s: Failed to disable regulator.\n", __func__);
return ret;
}
ret = regulator_set_optimum_mode(dsi_drv.vdd_vreg, 100);
if (ret < 0) {
pr_err("%s: vdd_vreg set regulator mode failed.\n",
__func__);
return ret;
}
ret = regulator_set_optimum_mode(dsi_drv.vdd_io_vreg, 100);
if (ret < 0) {
pr_err("%s: vdd_io_vreg set regulator mode failed.\n",
__func__);
return ret;
}
ret = regulator_set_optimum_mode(dsi_drv.dsi_vreg, 100);
if (ret < 0) {
pr_err("%s: dsi_vreg set regulator mode failed.\n",
__func__);
return ret;
}
}
return 0;
}
static int mdss_dsi_ctrl_unprepare(struct mdss_panel_data *pdata)
{
struct mdss_panel_info *pinfo;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
int ret = 0;
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
if (!ctrl_pdata) {
pr_err("%s: Invalid input data\n", __func__);
return -EINVAL;
}
pinfo = &pdata->panel_info;
mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);
ret = ctrl_pdata->off(pdata);
if (ret) {
pr_err("%s: Panel OFF failed\n", __func__);
return ret;
}
return ret;
}
static int mdss_dsi_off(struct mdss_panel_data *pdata)
{
int ret = 0;
mdss_dsi_clk_disable(pdata);
mdss_dsi_unprepare_clocks();
/* disable DSI controller */
mdss_dsi_controller_cfg(0, pdata);
ret = mdss_dsi_panel_power_on(0);
if (ret) {
pr_err("%s: Panel power off failed\n", __func__);
return ret;
}
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;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
if (!ctrl_pdata) {
pr_err("%s: Invalid input data\n", __func__);
return -EINVAL;
}
pinfo = &pdata->panel_info;
ret = mdss_dsi_panel_power_on(1);
if (ret) {
pr_err("%s: Panel power on failed\n", __func__);
return ret;
}
mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base));
mdss_dsi_phy_init(pdata);
mdss_dsi_prepare_clocks();
mdss_dsi_clk_enable(pdata);
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((ctrl_pdata->ctrl_base) + 0x24,
((hspw + hbp + width + dummy_xres) << 16 |
(hspw + hbp)));
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x28,
((vspw + vbp + height + dummy_yres) << 16 |
(vspw + vbp)));
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
(vspw + vbp + height + dummy_yres +
vfp - 1) << 16 | (hspw + hbp +
width + dummy_xres + hfp - 1));
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x30, (hspw << 16));
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x34, 0);
MIPI_OUTP((ctrl_pdata->ctrl_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((ctrl_pdata->ctrl_base) + 0x60, data);
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x58, data);
/* DSI_COMMAND_MODE_MDP_STREAM_TOTAL */
data = height << 16 | width;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x64, data);
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x5C, data);
}
mdss_dsi_sw_reset(pdata);
mdss_dsi_host_init(mipi, pdata);
if (mipi->force_clk_lane_hs) {
u32 tmp;
tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac);
tmp |= (1<<28);
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp);
wmb();
}
ret = ctrl_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;
}
static int mdss_dsi_resource_initialized;
static int __devinit 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;
}
}
rc = mdss_dsi_regulator_init(pdev);
if (rc) {
dev_err(&pdev->dev,
"%s: failed to init regulator, rc=%d\n",
__func__, rc);
iounmap(mdss_dsi_base);
return rc;
}
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 __devexit 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,
char backlight_ctrl)
{
struct mipi_panel_info *mipi;
int rc;
u8 lanes = 0, bpp;
u32 h_period, v_period, dsi_pclk_rate;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
h_period = ((panel_data->panel_info.lcdc.h_pulse_width)
+ (panel_data->panel_info.lcdc.h_back_porch)
+ (panel_data->panel_info.xres)
+ (panel_data->panel_info.lcdc.h_front_porch));
v_period = ((panel_data->panel_info.lcdc.v_pulse_width)
+ (panel_data->panel_info.lcdc.v_back_porch)
+ (panel_data->panel_info.yres)
+ (panel_data->panel_info.lcdc.v_front_porch));
mipi = &panel_data->panel_info.mipi;
panel_data->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_data->panel_info.type == MIPI_VIDEO_PANEL &&
!panel_data->panel_info.clk_rate) {
h_period += panel_data->panel_info.lcdc.xres_pad;
v_period += panel_data->panel_info.lcdc.yres_pad;
if (lanes > 0) {
panel_data->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_data->panel_info.clk_rate =
(h_period * v_period
* (mipi->frame_rate) * bpp * 8);
}
}
pll_divider_config.clk_rate = panel_data->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;
ctrl_pdata = devm_kzalloc(&pdev->dev,
sizeof(struct mdss_dsi_ctrl_pdata), GFP_KERNEL);
if (!ctrl_pdata)
return -ENOMEM;
(ctrl_pdata->panel_data).on = mdss_dsi_on;
(ctrl_pdata->panel_data).off = mdss_dsi_off;
(ctrl_pdata->panel_data).intf_unprepare = mdss_dsi_ctrl_unprepare;
memcpy(&((ctrl_pdata->panel_data).panel_info),
&(panel_data->panel_info),
sizeof(struct mdss_panel_info));
mdss_dsi_irq_handler_config(ctrl_pdata);
(ctrl_pdata->panel_data).set_backlight = panel_data->bl_fnc;
(ctrl_pdata->ctrl_base) = mdss_dsi_base;
(ctrl_pdata->bl_ctrl) = backlight_ctrl;
/*
* register in mdp driver
*/
rc = mdss_register_panel(&(ctrl_pdata->panel_data));
if (rc) {
dev_err(&pdev->dev, "unable to register MIPI DSI panel\n");
devm_kfree(&pdev->dev, ctrl_pdata);
return rc;
}
ctrl_pdata->on = panel_data->on;
ctrl_pdata->off = panel_data->off;
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 = __devexit_p(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>");