| /* 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; |
| } |