M7350/kernel/drivers/video/msm/mdss/dsi_io_v2.c
2024-09-09 08:57:42 +00:00

388 lines
10 KiB
C

/* Copyright (c) 2013, 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/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/clk/msm-clk.h>
#include "dsi_v2.h"
#include "dsi_io_v2.h"
#include "dsi_host_v2.h"
struct msm_dsi_io_private {
struct clk *dsi_byte_clk;
struct clk *dsi_esc_clk;
struct clk *dsi_pixel_clk;
struct clk *dsi_ahb_clk;
struct clk *dsi_clk;
int msm_dsi_clk_on;
int msm_dsi_ahb_clk_on;
};
static struct msm_dsi_io_private *dsi_io_private;
#define DSI_VDDA_VOLTAGE 1200000
void msm_dsi_ahb_ctrl(int enable)
{
if (enable) {
dsi_io_private->msm_dsi_ahb_clk_on++;
if (dsi_io_private->msm_dsi_ahb_clk_on == 1)
clk_enable(dsi_io_private->dsi_ahb_clk);
} else {
dsi_io_private->msm_dsi_ahb_clk_on--;
if (dsi_io_private->msm_dsi_ahb_clk_on == 0)
clk_disable(dsi_io_private->dsi_ahb_clk);
}
}
int msm_dsi_io_init(struct platform_device *pdev, struct dss_module_power *mp)
{
int rc;
if (!dsi_io_private) {
dsi_io_private = kzalloc(sizeof(struct msm_dsi_io_private),
GFP_KERNEL);
if (!dsi_io_private) {
pr_err("fail to alloc dsi io private data structure\n");
return -ENOMEM;
}
}
rc = msm_dsi_clk_init(pdev);
if (rc) {
pr_err("fail to initialize DSI clock\n");
return rc;
}
rc = msm_dss_config_vreg(&pdev->dev, mp->vreg_config,
mp->num_vreg, 1);
if (rc) {
pr_err("fail to initialize DSI regulator\n");
return rc;
}
return 0;
}
void msm_dsi_io_deinit(struct platform_device *pdev,
struct dss_module_power *mp)
{
if (dsi_io_private) {
msm_dsi_clk_deinit();
msm_dss_config_vreg(&pdev->dev, mp->vreg_config,
mp->num_vreg, 0);
kfree(dsi_io_private);
dsi_io_private = NULL;
}
}
int msm_dsi_clk_init(struct platform_device *dev)
{
int rc = 0;
dsi_io_private->dsi_clk = clk_get(&dev->dev, "dsi_clk");
if (IS_ERR(dsi_io_private->dsi_clk)) {
pr_err("can't find dsi core_clk\n");
rc = PTR_ERR(dsi_io_private->dsi_clk);
dsi_io_private->dsi_clk = NULL;
return rc;
}
dsi_io_private->dsi_byte_clk = clk_get(&dev->dev, "byte_clk");
if (IS_ERR(dsi_io_private->dsi_byte_clk)) {
pr_err("can't find dsi byte_clk\n");
rc = PTR_ERR(dsi_io_private->dsi_byte_clk);
dsi_io_private->dsi_byte_clk = NULL;
return rc;
}
dsi_io_private->dsi_esc_clk = clk_get(&dev->dev, "esc_clk");
if (IS_ERR(dsi_io_private->dsi_esc_clk)) {
pr_err("can't find dsi esc_clk\n");
rc = PTR_ERR(dsi_io_private->dsi_esc_clk);
dsi_io_private->dsi_esc_clk = NULL;
return rc;
}
dsi_io_private->dsi_pixel_clk = clk_get(&dev->dev, "pixel_clk");
if (IS_ERR(dsi_io_private->dsi_pixel_clk)) {
pr_err("can't find dsi pixel\n");
rc = PTR_ERR(dsi_io_private->dsi_pixel_clk);
dsi_io_private->dsi_pixel_clk = NULL;
return rc;
}
dsi_io_private->dsi_ahb_clk = clk_get(&dev->dev, "iface_clk");
if (IS_ERR(dsi_io_private->dsi_ahb_clk)) {
pr_err("can't find dsi iface_clk\n");
rc = PTR_ERR(dsi_io_private->dsi_ahb_clk);
dsi_io_private->dsi_ahb_clk = NULL;
return rc;
}
clk_prepare(dsi_io_private->dsi_ahb_clk);
return 0;
}
void msm_dsi_clk_deinit(void)
{
if (dsi_io_private->dsi_clk) {
clk_put(dsi_io_private->dsi_clk);
dsi_io_private->dsi_clk = NULL;
}
if (dsi_io_private->dsi_byte_clk) {
clk_put(dsi_io_private->dsi_byte_clk);
dsi_io_private->dsi_byte_clk = NULL;
}
if (dsi_io_private->dsi_esc_clk) {
clk_put(dsi_io_private->dsi_esc_clk);
dsi_io_private->dsi_esc_clk = NULL;
}
if (dsi_io_private->dsi_pixel_clk) {
clk_put(dsi_io_private->dsi_pixel_clk);
dsi_io_private->dsi_pixel_clk = NULL;
}
if (dsi_io_private->dsi_ahb_clk) {
clk_unprepare(dsi_io_private->dsi_ahb_clk);
clk_put(dsi_io_private->dsi_ahb_clk);
dsi_io_private->dsi_ahb_clk = NULL;
}
}
int msm_dsi_prepare_clocks(void)
{
clk_prepare(dsi_io_private->dsi_clk);
clk_prepare(dsi_io_private->dsi_byte_clk);
clk_prepare(dsi_io_private->dsi_esc_clk);
clk_prepare(dsi_io_private->dsi_pixel_clk);
return 0;
}
int msm_dsi_unprepare_clocks(void)
{
clk_unprepare(dsi_io_private->dsi_clk);
clk_unprepare(dsi_io_private->dsi_esc_clk);
clk_unprepare(dsi_io_private->dsi_byte_clk);
clk_unprepare(dsi_io_private->dsi_pixel_clk);
return 0;
}
int msm_dsi_clk_set_rate(unsigned long esc_rate,
unsigned long dsi_rate,
unsigned long byte_rate,
unsigned long pixel_rate)
{
int rc;
rc = clk_set_rate(dsi_io_private->dsi_clk, dsi_rate);
if (rc) {
pr_err("dsi_esc_clk - clk_set_rate failed =%d\n", rc);
return rc;
}
rc = clk_set_rate(dsi_io_private->dsi_esc_clk, esc_rate);
if (rc) {
pr_err("dsi_esc_clk - clk_set_rate failed =%d\n", rc);
return rc;
}
rc = clk_set_rate(dsi_io_private->dsi_byte_clk, byte_rate);
if (rc) {
pr_err("dsi_byte_clk - clk_set_rate faile = %dd\n", rc);
return rc;
}
rc = clk_set_rate(dsi_io_private->dsi_pixel_clk, pixel_rate);
if (rc) {
pr_err("dsi_pixel_clk - clk_set_rate failed = %d\n", rc);
return rc;
}
return 0;
}
int msm_dsi_clk_enable(void)
{
if (dsi_io_private->msm_dsi_clk_on) {
pr_debug("dsi_clks on already\n");
return 0;
}
clk_enable(dsi_io_private->dsi_clk);
clk_enable(dsi_io_private->dsi_esc_clk);
clk_enable(dsi_io_private->dsi_byte_clk);
clk_enable(dsi_io_private->dsi_pixel_clk);
dsi_io_private->msm_dsi_clk_on = 1;
return 0;
}
int msm_dsi_clk_disable(void)
{
if (dsi_io_private->msm_dsi_clk_on == 0) {
pr_debug("mdss_dsi_clks already OFF\n");
return 0;
}
clk_disable(dsi_io_private->dsi_clk);
clk_disable(dsi_io_private->dsi_byte_clk);
clk_disable(dsi_io_private->dsi_esc_clk);
clk_disable(dsi_io_private->dsi_pixel_clk);
dsi_io_private->msm_dsi_clk_on = 0;
return 0;
}
static void msm_dsi_phy_strength_init(unsigned char *ctrl_base,
struct mdss_dsi_phy_ctrl *pd)
{
MIPI_OUTP(ctrl_base + DSI_DSIPHY_STRENGTH_CTRL_0, pd->strength[0]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_STRENGTH_CTRL_2, pd->strength[1]);
}
static void msm_dsi_phy_ctrl_init(unsigned char *ctrl_base,
struct mdss_panel_data *pdata)
{
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x5f);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_3, 0x10);
}
static void msm_dsi_phy_regulator_init(unsigned char *ctrl_base,
struct mdss_dsi_phy_ctrl *pd)
{
MIPI_OUTP(ctrl_base + DSI_DSIPHY_LDO_CNTRL, 0x25);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, pd->regulator[0]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_1, pd->regulator[1]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_2, pd->regulator[2]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_3, pd->regulator[3]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_4, pd->regulator[4]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CAL_PWR_CFG,
pd->regulator[5]);
}
static int msm_dsi_phy_calibration(unsigned char *ctrl_base)
{
int i = 0, term_cnt = 5000, ret = 0, cal_busy;
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_SW_CFG2, 0x0);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG1, 0x5a);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG3, 0x10);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG4, 0x01);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG0, 0x01);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_TRIGGER, 0x01);
usleep_range(5000, 5000); /*per DSI controller spec*/
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_TRIGGER, 0x00);
cal_busy = MIPI_INP(ctrl_base + DSI_DSIPHY_REGULATOR_CAL_STATUS0);
while (cal_busy & 0x10) {
i++;
if (i > term_cnt) {
ret = -EINVAL;
pr_err("msm_dsi_phy_calibration error\n");
break;
}
cal_busy = MIPI_INP(ctrl_base +
DSI_DSIPHY_REGULATOR_CAL_STATUS0);
}
return ret;
}
static void msm_dsi_phy_lane_init(unsigned char *ctrl_base,
struct mdss_dsi_phy_ctrl *pd)
{
int ln, index;
/*CFG0, CFG1, CFG2, TEST_DATAPATH, TEST_STR0, TEST_STR1*/
for (ln = 0; ln < 5; ln++) {
unsigned char *off = ctrl_base + 0x0300 + (ln * 0x40);
index = ln * 6;
MIPI_OUTP(off, pd->lanecfg[index]);
MIPI_OUTP(off + 4, pd->lanecfg[index + 1]);
MIPI_OUTP(off + 8, pd->lanecfg[index + 2]);
MIPI_OUTP(off + 12, pd->lanecfg[index + 3]);
MIPI_OUTP(off + 20, pd->lanecfg[index + 4]);
MIPI_OUTP(off + 24, pd->lanecfg[index + 5]);
}
wmb();
}
static void msm_dsi_phy_timing_init(unsigned char *ctrl_base,
struct mdss_dsi_phy_ctrl *pd)
{
int i, off = DSI_DSIPHY_TIMING_CTRL_0;
for (i = 0; i < 12; i++) {
MIPI_OUTP(ctrl_base + off, pd->timing[i]);
off += 4;
}
wmb();
}
static void msm_dsi_phy_bist_init(unsigned char *ctrl_base,
struct mdss_dsi_phy_ctrl *pd)
{
MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL4, pd->bistctrl[4]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL1, pd->bistctrl[1]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL0, pd->bistctrl[0]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL4, 0);
wmb();
}
int msm_dsi_phy_init(unsigned char *ctrl_base,
struct mdss_panel_data *pdata)
{
struct mdss_dsi_phy_ctrl *pd;
pd = &(pdata->panel_info.mipi.dsi_phy_db);
msm_dsi_phy_strength_init(ctrl_base, pd);
msm_dsi_phy_ctrl_init(ctrl_base, pdata);
msm_dsi_phy_regulator_init(ctrl_base, pd);
msm_dsi_phy_calibration(ctrl_base);
msm_dsi_phy_lane_init(ctrl_base, pd);
msm_dsi_phy_timing_init(ctrl_base, pd);
msm_dsi_phy_bist_init(ctrl_base, pd);
return 0;
}
void msm_dsi_phy_sw_reset(unsigned char *ctrl_base)
{
/* start phy sw reset */
MIPI_OUTP(ctrl_base + DSI_PHY_SW_RESET, 0x0001);
udelay(1000); /*per DSI controller spec*/
wmb();
/* end phy sw reset */
MIPI_OUTP(ctrl_base + DSI_PHY_SW_RESET, 0x0000);
udelay(100); /*per DSI controller spec*/
wmb();
}
void msm_dsi_phy_off(unsigned char *ctrl_base)
{
MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x05f);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, 0x02);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x00);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_1, 0x7f);
MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0);
}