blob: 262b7bd366b218ab924e728ba5eebc964c809c9e [file] [log] [blame]
/* Copyright (c) 2012-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/module.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/qpnp/pin.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include <linux/pwm.h>
#include <linux/err.h>
#include "mdss_dsi.h"
#define DT_CMD_HDR 6
DEFINE_LED_TRIGGER(bl_led_trigger);
void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl)
{
int ret;
if (!gpio_is_valid(ctrl->pwm_pmic_gpio)) {
pr_err("%s: pwm_pmic_gpio=%d Invalid\n", __func__,
ctrl->pwm_pmic_gpio);
ctrl->pwm_pmic_gpio = -1;
return;
}
ret = gpio_request(ctrl->pwm_pmic_gpio, "disp_pwm");
if (ret) {
pr_err("%s: pwm_pmic_gpio=%d request failed\n", __func__,
ctrl->pwm_pmic_gpio);
ctrl->pwm_pmic_gpio = -1;
return;
}
ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt");
if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) {
pr_err("%s: lpg_chan=%d pwm request failed", __func__,
ctrl->pwm_lpg_chan);
gpio_free(ctrl->pwm_pmic_gpio);
ctrl->pwm_pmic_gpio = -1;
}
}
static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level)
{
int ret;
u32 duty;
if (ctrl->pwm_bl == NULL) {
pr_err("%s: no PWM\n", __func__);
return;
}
duty = level * ctrl->pwm_period;
duty /= ctrl->bklt_max;
pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n",
__func__, ctrl->bklt_ctrl, ctrl->pwm_period,
ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan);
pr_debug("%s: ndx=%d level=%d duty=%d\n", __func__,
ctrl->ndx, level, duty);
ret = pwm_config(ctrl->pwm_bl, duty, ctrl->pwm_period);
if (ret) {
pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
return;
}
ret = pwm_enable(ctrl->pwm_bl);
if (ret)
pr_err("%s: pwm_enable() failed err=%d\n", __func__, ret);
}
static char dcs_cmd[2] = {0x54, 0x00}; /* DTYPE_DCS_READ */
static struct dsi_cmd_desc dcs_read_cmd = {
{DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(dcs_cmd)},
dcs_cmd
};
u32 mdss_dsi_dcs_read(struct mdss_dsi_ctrl_pdata *ctrl,
char cmd0, char cmd1)
{
struct dcs_cmd_req cmdreq;
dcs_cmd[0] = cmd0;
dcs_cmd[1] = cmd1;
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = &dcs_read_cmd;
cmdreq.cmds_cnt = 1;
cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT;
cmdreq.rlen = 1;
cmdreq.cb = NULL; /* call back */
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
/*
* blocked here, until call back called
*/
return 0;
}
static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsi_panel_cmds *pcmds)
{
struct dcs_cmd_req cmdreq;
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = pcmds->cmds;
cmdreq.cmds_cnt = pcmds->cmd_cnt;
cmdreq.flags = CMD_REQ_COMMIT;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
}
static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */
static struct dsi_cmd_desc backlight_cmd = {
{DTYPE_DCS_WRITE1, 1, 0, 0, 1, sizeof(led_pwm1)},
led_pwm1
};
static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level)
{
struct dcs_cmd_req cmdreq;
pr_debug("%s: level=%d\n", __func__, level);
led_pwm1[1] = (unsigned char)level;
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = &backlight_cmd;
cmdreq.cmds_cnt = 1;
cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
}
void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_panel_info *pinfo = NULL;
int i;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return;
}
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) {
pr_debug("%s:%d, reset line not configured\n",
__func__, __LINE__);
}
if (!gpio_is_valid(ctrl_pdata->rst_gpio)) {
pr_debug("%s:%d, reset line not configured\n",
__func__, __LINE__);
return;
}
pr_debug("%s: enable = %d\n", __func__, enable);
pinfo = &(ctrl_pdata->panel_data.panel_info);
if (enable) {
for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) {
gpio_set_value((ctrl_pdata->rst_gpio),
pdata->panel_info.rst_seq[i]);
if (pdata->panel_info.rst_seq[++i])
usleep(pdata->panel_info.rst_seq[i] * 1000);
}
if (gpio_is_valid(ctrl_pdata->disp_en_gpio))
gpio_set_value((ctrl_pdata->disp_en_gpio), 1);
if (gpio_is_valid(ctrl_pdata->mode_gpio)) {
if (pinfo->mode_gpio_state == MODE_GPIO_HIGH)
gpio_set_value((ctrl_pdata->mode_gpio), 1);
else if (pinfo->mode_gpio_state == MODE_GPIO_LOW)
gpio_set_value((ctrl_pdata->mode_gpio), 0);
}
if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
pr_debug("%s: Panel Not properly turned OFF\n",
__func__);
ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
pr_debug("%s: Reset panel done\n", __func__);
}
} else {
gpio_set_value((ctrl_pdata->rst_gpio), 0);
if (gpio_is_valid(ctrl_pdata->disp_en_gpio))
gpio_set_value((ctrl_pdata->disp_en_gpio), 0);
}
}
static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */
static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */
static struct dsi_cmd_desc partial_update_enable_cmd[] = {
{{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(caset)}, caset},
{{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset)}, paset},
};
static int mdss_dsi_panel_partial_update(struct mdss_panel_data *pdata)
{
struct mipi_panel_info *mipi;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct dcs_cmd_req cmdreq;
int rc = 0;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return -EINVAL;
}
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
mipi = &pdata->panel_info.mipi;
pr_debug("%s: ctrl=%p ndx=%d\n", __func__, ctrl, ctrl->ndx);
caset[1] = (((pdata->panel_info.roi_x) & 0xFF00) >> 8);
caset[2] = (((pdata->panel_info.roi_x) & 0xFF));
caset[3] = (((pdata->panel_info.roi_x - 1 + pdata->panel_info.roi_w)
& 0xFF00) >> 8);
caset[4] = (((pdata->panel_info.roi_x - 1 + pdata->panel_info.roi_w)
& 0xFF));
partial_update_enable_cmd[0].payload = caset;
paset[1] = (((pdata->panel_info.roi_y) & 0xFF00) >> 8);
paset[2] = (((pdata->panel_info.roi_y) & 0xFF));
paset[3] = (((pdata->panel_info.roi_y - 1 + pdata->panel_info.roi_h)
& 0xFF00) >> 8);
paset[4] = (((pdata->panel_info.roi_y - 1 + pdata->panel_info.roi_h)
& 0xFF));
partial_update_enable_cmd[1].payload = paset;
pr_debug("%s: enabling partial update\n", __func__);
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = partial_update_enable_cmd;
cmdreq.cmds_cnt = 2;
cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
return rc;
}
static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
u32 bl_level)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return;
}
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
switch (ctrl_pdata->bklt_ctrl) {
case BL_WLED:
led_trigger_event(bl_led_trigger, bl_level);
break;
case BL_PWM:
mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level);
break;
case BL_DCS_CMD:
mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);
break;
default:
pr_err("%s: Unknown bl_ctrl configuration\n",
__func__);
break;
}
}
static int mdss_dsi_panel_on(struct mdss_panel_data *pdata)
{
struct mipi_panel_info *mipi;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return -EINVAL;
}
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
mipi = &pdata->panel_info.mipi;
pr_debug("%s: ctrl=%p ndx=%d\n", __func__, ctrl, ctrl->ndx);
if (ctrl->on_cmds.cmd_cnt)
mdss_dsi_panel_cmds_send(ctrl, &ctrl->on_cmds);
pr_debug("%s:-\n", __func__);
return 0;
}
static int mdss_dsi_panel_off(struct mdss_panel_data *pdata)
{
struct mipi_panel_info *mipi;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return -EINVAL;
}
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
pr_debug("%s: ctrl=%p ndx=%d\n", __func__, ctrl, ctrl->ndx);
mipi = &pdata->panel_info.mipi;
if (ctrl->off_cmds.cmd_cnt)
mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds);
pr_debug("%s:-\n", __func__);
return 0;
}
static int mdss_dsi_parse_dcs_cmds(struct device_node *np,
struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key)
{
const char *data;
int blen = 0, len;
char *buf, *bp;
struct dsi_ctrl_hdr *dchdr;
int i, cnt;
data = of_get_property(np, cmd_key, &blen);
if (!data) {
pr_err("%s: failed, key=%s\n", __func__, cmd_key);
return -ENOMEM;
}
buf = kzalloc(sizeof(char) * blen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, data, blen);
/* scan dcs commands */
bp = buf;
len = blen;
cnt = 0;
while (len > sizeof(*dchdr)) {
dchdr = (struct dsi_ctrl_hdr *)bp;
dchdr->dlen = ntohs(dchdr->dlen);
if (dchdr->dlen > len) {
pr_err("%s: dtsi cmd=%x error, len=%d",
__func__, dchdr->dtype, dchdr->dlen);
return -ENOMEM;
}
bp += sizeof(*dchdr);
len -= sizeof(*dchdr);
bp += dchdr->dlen;
len -= dchdr->dlen;
cnt++;
}
if (len != 0) {
pr_err("%s: dcs_cmd=%x len=%d error!",
__func__, buf[0], blen);
kfree(buf);
return -ENOMEM;
}
pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc),
GFP_KERNEL);
if (!pcmds->cmds)
return -ENOMEM;
pcmds->cmd_cnt = cnt;
pcmds->buf = buf;
pcmds->blen = blen;
bp = buf;
len = blen;
for (i = 0; i < cnt; i++) {
dchdr = (struct dsi_ctrl_hdr *)bp;
len -= sizeof(*dchdr);
bp += sizeof(*dchdr);
pcmds->cmds[i].dchdr = *dchdr;
pcmds->cmds[i].payload = bp;
bp += dchdr->dlen;
len -= dchdr->dlen;
}
data = of_get_property(np, link_key, NULL);
if (!strncmp(data, "dsi_hs_mode", 11))
pcmds->link_state = DSI_HS_MODE;
else
pcmds->link_state = DSI_LP_MODE;
pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__,
pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state);
return 0;
}
static int mdss_panel_dt_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing,
char *dst_format)
{
int rc = 0;
switch (bpp) {
case 3:
*dst_format = DSI_CMD_DST_FORMAT_RGB111;
break;
case 8:
*dst_format = DSI_CMD_DST_FORMAT_RGB332;
break;
case 12:
*dst_format = DSI_CMD_DST_FORMAT_RGB444;
break;
case 16:
switch (mipi_mode) {
case DSI_VIDEO_MODE:
*dst_format = DSI_VIDEO_DST_FORMAT_RGB565;
break;
case DSI_CMD_MODE:
*dst_format = DSI_CMD_DST_FORMAT_RGB565;
break;
default:
*dst_format = DSI_VIDEO_DST_FORMAT_RGB565;
break;
}
break;
case 18:
switch (mipi_mode) {
case DSI_VIDEO_MODE:
if (pixel_packing == 0)
*dst_format = DSI_VIDEO_DST_FORMAT_RGB666;
else
*dst_format = DSI_VIDEO_DST_FORMAT_RGB666_LOOSE;
break;
case DSI_CMD_MODE:
*dst_format = DSI_CMD_DST_FORMAT_RGB666;
break;
default:
if (pixel_packing == 0)
*dst_format = DSI_VIDEO_DST_FORMAT_RGB666;
else
*dst_format = DSI_VIDEO_DST_FORMAT_RGB666_LOOSE;
break;
}
break;
case 24:
switch (mipi_mode) {
case DSI_VIDEO_MODE:
*dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
break;
case DSI_CMD_MODE:
*dst_format = DSI_CMD_DST_FORMAT_RGB888;
break;
default:
*dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
break;
}
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int mdss_dsi_parse_fbc_params(struct device_node *np,
struct mdss_panel_info *panel_info)
{
int rc, fbc_enabled = 0;
u32 tmp;
fbc_enabled = of_property_read_bool(np, "qcom,mdss-dsi-fbc-enable");
if (fbc_enabled) {
pr_debug("%s:%d FBC panel enabled.\n", __func__, __LINE__);
panel_info->fbc.enabled = 1;
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bpp", &tmp);
panel_info->fbc.target_bpp = (!rc ? tmp : panel_info->bpp);
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-packing",
&tmp);
panel_info->fbc.comp_mode = (!rc ? tmp : 0);
panel_info->fbc.qerr_enable = of_property_read_bool(np,
"qcom,mdss-dsi-fbc-quant-error");
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bias", &tmp);
panel_info->fbc.cd_bias = (!rc ? tmp : 0);
panel_info->fbc.pat_enable = of_property_read_bool(np,
"qcom,mdss-dsi-fbc-pat-mode");
panel_info->fbc.vlc_enable = of_property_read_bool(np,
"qcom,mdss-dsi-fbc-vlc-mode");
panel_info->fbc.bflc_enable = of_property_read_bool(np,
"qcom,mdss-dsi-fbc-bflc-mode");
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-h-line-budget",
&tmp);
panel_info->fbc.line_x_budget = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-budget-ctrl",
&tmp);
panel_info->fbc.block_x_budget = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-block-budget",
&tmp);
panel_info->fbc.block_budget = (!rc ? tmp : 0);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-fbc-lossless-threshold", &tmp);
panel_info->fbc.lossless_mode_thd = (!rc ? tmp : 0);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-fbc-lossy-threshold", &tmp);
panel_info->fbc.lossy_mode_thd = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-rgb-threshold",
&tmp);
panel_info->fbc.lossy_rgb_thd = (!rc ? tmp : 0);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-fbc-lossy-mode-idx", &tmp);
panel_info->fbc.lossy_mode_idx = (!rc ? tmp : 0);
} else {
pr_debug("%s:%d Panel does not support FBC.\n",
__func__, __LINE__);
panel_info->fbc.enabled = 0;
panel_info->fbc.target_bpp =
panel_info->bpp;
}
return 0;
}
static int mdss_dsi_parse_reset_seq(struct device_node *np,
u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len,
const char *name)
{
int num = 0, i;
int rc;
struct property *data;
u32 tmp[MDSS_DSI_RST_SEQ_LEN];
*rst_len = 0;
data = of_find_property(np, name, &num);
num /= sizeof(u32);
if (!data || !num || num > MDSS_DSI_RST_SEQ_LEN || num % 2) {
pr_debug("%s:%d, error reading %s, length found = %d\n",
__func__, __LINE__, name, num);
} else {
rc = of_property_read_u32_array(np, name, tmp, num);
if (rc)
pr_debug("%s:%d, error reading %s, rc = %d\n",
__func__, __LINE__, name, rc);
else {
for (i = 0; i < num; ++i)
rst_seq[i] = tmp[i];
*rst_len = num;
}
}
return 0;
}
static int mdss_panel_parse_dt(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
u32 tmp;
int rc, i, len;
const char *data;
static const char *pdest;
struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-width", &tmp);
if (rc) {
pr_err("%s:%d, panel width not specified\n",
__func__, __LINE__);
return -EINVAL;
}
pinfo->xres = (!rc ? tmp : 640);
rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-height", &tmp);
if (rc) {
pr_err("%s:%d, panel height not specified\n",
__func__, __LINE__);
return -EINVAL;
}
pinfo->yres = (!rc ? tmp : 480);
rc = of_property_read_u32(np,
"qcom,mdss-pan-physical-width-dimension", &tmp);
pinfo->physical_width = (!rc ? tmp : 0);
rc = of_property_read_u32(np,
"qcom,mdss-pan-physical-height-dimension", &tmp);
pinfo->physical_height = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-left-border", &tmp);
pinfo->lcdc.xres_pad = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-right-border", &tmp);
if (!rc)
pinfo->lcdc.xres_pad += tmp;
rc = of_property_read_u32(np, "qcom,mdss-dsi-v-top-border", &tmp);
pinfo->lcdc.yres_pad = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-v-bottom-border", &tmp);
if (!rc)
pinfo->lcdc.yres_pad += tmp;
rc = of_property_read_u32(np, "qcom,mdss-dsi-bpp", &tmp);
if (rc) {
pr_err("%s:%d, bpp not specified\n", __func__, __LINE__);
return -EINVAL;
}
pinfo->bpp = (!rc ? tmp : 24);
pinfo->mipi.mode = DSI_VIDEO_MODE;
data = of_get_property(np, "qcom,mdss-dsi-panel-type", NULL);
if (data && !strncmp(data, "dsi_cmd_mode", 12))
pinfo->mipi.mode = DSI_CMD_MODE;
rc = of_property_read_u32(np, "qcom,mdss-dsi-pixel-packing", &tmp);
tmp = (!rc ? tmp : 0);
rc = mdss_panel_dt_get_dst_fmt(pinfo->bpp,
pinfo->mipi.mode, tmp,
&(pinfo->mipi.dst_format));
if (rc) {
pr_debug("%s: problem determining dst format. Set Default\n",
__func__);
pinfo->mipi.dst_format =
DSI_VIDEO_DST_FORMAT_RGB888;
}
pdest = of_get_property(np,
"qcom,mdss-dsi-panel-destination", NULL);
if (strlen(pdest) != 9) {
pr_err("%s: Unknown pdest specified\n", __func__);
return -EINVAL;
}
if (!strncmp(pdest, "display_1", 9))
pinfo->pdest = DISPLAY_1;
else if (!strncmp(pdest, "display_2", 9))
pinfo->pdest = DISPLAY_2;
else {
pr_debug("%s: pdest not specified. Set Default\n",
__func__);
pinfo->pdest = DISPLAY_1;
}
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp);
pinfo->lcdc.h_front_porch = (!rc ? tmp : 6);
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-back-porch", &tmp);
pinfo->lcdc.h_back_porch = (!rc ? tmp : 6);
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-pulse-width", &tmp);
pinfo->lcdc.h_pulse_width = (!rc ? tmp : 2);
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-sync-skew", &tmp);
pinfo->lcdc.hsync_skew = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-v-back-porch", &tmp);
pinfo->lcdc.v_back_porch = (!rc ? tmp : 6);
rc = of_property_read_u32(np, "qcom,mdss-dsi-v-front-porch", &tmp);
pinfo->lcdc.v_front_porch = (!rc ? tmp : 6);
rc = of_property_read_u32(np, "qcom,mdss-dsi-v-pulse-width", &tmp);
pinfo->lcdc.v_pulse_width = (!rc ? tmp : 2);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-underflow-color", &tmp);
pinfo->lcdc.underflow_clr = (!rc ? tmp : 0xff);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-border-color", &tmp);
pinfo->lcdc.border_clr = (!rc ? tmp : 0);
pinfo->bklt_ctrl = UNKNOWN_CTRL;
data = of_get_property(np, "qcom,mdss-dsi-bl-pmic-control-type", NULL);
if (data) {
if (!strncmp(data, "bl_ctrl_wled", 12)) {
led_trigger_register_simple("bkl-trigger",
&bl_led_trigger);
pr_debug("%s: SUCCESS-> WLED TRIGGER register\n",
__func__);
ctrl_pdata->bklt_ctrl = BL_WLED;
} else if (!strncmp(data, "bl_ctrl_pwm", 11)) {
ctrl_pdata->bklt_ctrl = BL_PWM;
rc = of_property_read_u32(np,
"qcom,mdss-dsi-bl-pmic-pwm-frequency", &tmp);
if (rc) {
pr_err("%s:%d, Error, panel pwm_period\n",
__func__, __LINE__);
return -EINVAL;
}
ctrl_pdata->pwm_period = tmp;
rc = of_property_read_u32(np,
"qcom,mdss-dsi-bl-pmic-bank-select", &tmp);
if (rc) {
pr_err("%s:%d, Error, dsi lpg channel\n",
__func__, __LINE__);
return -EINVAL;
}
ctrl_pdata->pwm_lpg_chan = tmp;
tmp = of_get_named_gpio(np,
"qcom,mdss-dsi-pwm-gpio", 0);
ctrl_pdata->pwm_pmic_gpio = tmp;
} else if (!strncmp(data, "bl_ctrl_dcs", 11)) {
ctrl_pdata->bklt_ctrl = BL_DCS_CMD;
}
}
rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp);
pinfo->bl_min = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp);
pinfo->bl_max = (!rc ? tmp : 255);
ctrl_pdata->bklt_max = pinfo->bl_max;
rc = of_property_read_u32(np, "qcom,mdss-dsi-interleave-mode", &tmp);
pinfo->mipi.interleave_mode = (!rc ? tmp : 0);
pinfo->mipi.vsync_enable = of_property_read_bool(np,
"qcom,mdss-dsi-te-check-enable");
pinfo->mipi.hw_vsync_mode = of_property_read_bool(np,
"qcom,mdss-dsi-te-using-te-pin");
rc = of_property_read_u32(np,
"qcom,mdss-dsi-h-sync-pulse", &tmp);
pinfo->mipi.pulse_mode_hsa_he = (!rc ? tmp : false);
pinfo->mipi.hfp_power_stop = of_property_read_bool(np,
"qcom,mdss-dsi-hfp-power-mode");
pinfo->mipi.hsa_power_stop = of_property_read_bool(np,
"qcom,mdss-dsi-hsa-power-mode");
pinfo->mipi.hbp_power_stop = of_property_read_bool(np,
"qcom,mdss-dsi-hbp-power-mode");
pinfo->mipi.bllp_power_stop = of_property_read_bool(np,
"qcom,mdss-dsi-bllp-power-mode");
pinfo->mipi.eof_bllp_power_stop = of_property_read_bool(
np, "qcom,mdss-dsi-bllp-eof-power-mode");
rc = of_property_read_u32(np,
"qcom,mdss-dsi-traffic-mode", &tmp);
pinfo->mipi.traffic_mode =
(!rc ? tmp : DSI_NON_BURST_SYNCH_PULSE);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-te-dcs-command", &tmp);
pinfo->mipi.insert_dcs_cmd =
(!rc ? tmp : 1);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-te-v-sync-continue-lines", &tmp);
pinfo->mipi.wr_mem_continue =
(!rc ? tmp : 0x3c);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line", &tmp);
pinfo->mipi.wr_mem_start =
(!rc ? tmp : 0x2c);
rc = of_property_read_u32(np,
"qcom,mdss-dsi-te-pin-select", &tmp);
pinfo->mipi.te_sel =
(!rc ? tmp : 1);
rc = of_property_read_u32(np, "qcom,mdss-dsi-virtual-channel-id", &tmp);
pinfo->mipi.vc = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-color-order", &tmp);
pinfo->mipi.rgb_swap = (!rc ? tmp : DSI_RGB_SWAP_RGB);
pinfo->mipi.data_lane0 = of_property_read_bool(np,
"qcom,mdss-dsi-lane-0-state");
pinfo->mipi.data_lane1 = of_property_read_bool(np,
"qcom,mdss-dsi-lane-1-state");
pinfo->mipi.data_lane2 = of_property_read_bool(np,
"qcom,mdss-dsi-lane-2-state");
pinfo->mipi.data_lane3 = of_property_read_bool(np,
"qcom,mdss-dsi-lane-3-state");
rc = of_property_read_u32(np, "qcom,mdss-dsi-lane-map", &tmp);
pinfo->mipi.dlane_swap = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-pre", &tmp);
pinfo->mipi.t_clk_pre = (!rc ? tmp : 0x24);
rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp);
pinfo->mipi.t_clk_post = (!rc ? tmp : 0x03);
rc = of_property_read_u32(np, "qcom,mdss-dsi-stream", &tmp);
pinfo->mipi.stream = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-mdp-trigger", &tmp);
pinfo->mipi.mdp_trigger =
(!rc ? tmp : DSI_CMD_TRIGGER_SW);
if (pinfo->mipi.mdp_trigger > 6) {
pr_err("%s:%d, Invalid mdp trigger. Forcing to sw trigger",
__func__, __LINE__);
pinfo->mipi.mdp_trigger =
DSI_CMD_TRIGGER_SW;
}
rc = of_property_read_u32(np, "qcom,mdss-dsi-dma-trigger", &tmp);
pinfo->mipi.dma_trigger =
(!rc ? tmp : DSI_CMD_TRIGGER_SW);
if (pinfo->mipi.dma_trigger > 6) {
pr_err("%s:%d, Invalid dma trigger. Forcing to sw trigger",
__func__, __LINE__);
pinfo->mipi.dma_trigger =
DSI_CMD_TRIGGER_SW;
}
data = of_get_property(np, "qcom,mdss-dsi-panel-mode-gpio-state", &tmp);
if (data) {
if (!strcmp(data, "high"))
pinfo->mode_gpio_state = MODE_GPIO_HIGH;
else if (!strcmp(data, "low"))
pinfo->mode_gpio_state = MODE_GPIO_LOW;
} else {
pinfo->mode_gpio_state = MODE_GPIO_NOT_VALID;
}
rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-frame-rate", &tmp);
pinfo->mipi.frame_rate = (!rc ? tmp : 60);
rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-clock-rate", &tmp);
pinfo->clk_rate = (!rc ? tmp : 0);
data = of_get_property(np, "qcom,mdss-dsi-panel-timings", &len);
if ((!data) || (len != 12)) {
pr_err("%s:%d, Unable to read Phy timing settings",
__func__, __LINE__);
goto error;
}
for (i = 0; i < len; i++)
pinfo->mipi.dsi_phy_db.timing[i] = data[i];
mdss_dsi_parse_fbc_params(np, pinfo);
mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len),
"qcom,mdss-dsi-reset-sequence");
mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->on_cmds,
"qcom,mdss-dsi-on-command", "qcom,mdss-dsi-on-command-state");
mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds,
"qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state");
return 0;
error:
return -EINVAL;
}
int mdss_dsi_panel_init(struct device_node *node,
struct mdss_dsi_ctrl_pdata *ctrl_pdata,
bool cmd_cfg_cont_splash)
{
int rc = 0;
static const char *panel_name;
bool cont_splash_enabled;
bool partial_update_enabled;
if (!node) {
pr_err("%s: no panel node\n", __func__);
return -ENODEV;
}
pr_debug("%s:%d\n", __func__, __LINE__);
panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", 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);
rc = mdss_panel_parse_dt(node, ctrl_pdata);
if (rc) {
pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__);
return rc;
}
if (cmd_cfg_cont_splash)
cont_splash_enabled = of_property_read_bool(node,
"qcom,cont-splash-enabled");
else
cont_splash_enabled = false;
if (!cont_splash_enabled) {
pr_info("%s:%d Continuous splash flag not found.\n",
__func__, __LINE__);
ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 0;
} else {
pr_info("%s:%d Continuous splash flag enabled.\n",
__func__, __LINE__);
ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 1;
}
partial_update_enabled = of_property_read_bool(node,
"qcom,partial-update-enabled");
if (partial_update_enabled) {
pr_info("%s:%d Partial update enabled.\n", __func__, __LINE__);
ctrl_pdata->panel_data.panel_info.partial_update_enabled = 1;
ctrl_pdata->partial_update_fnc = mdss_dsi_panel_partial_update;
} else {
pr_info("%s:%d Partial update disabled.\n", __func__, __LINE__);
ctrl_pdata->panel_data.panel_info.partial_update_enabled = 0;
ctrl_pdata->partial_update_fnc = NULL;
}
ctrl_pdata->on = mdss_dsi_panel_on;
ctrl_pdata->off = mdss_dsi_panel_off;
ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl;
return 0;
}