| /* 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); |
| |
| static struct mdss_dsi_phy_ctrl phy_params; |
| |
| 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; |
| |
| 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); |
| |
| if (enable) { |
| gpio_set_value((ctrl_pdata->rst_gpio), 1); |
| msleep(20); |
| gpio_set_value((ctrl_pdata->rst_gpio), 0); |
| udelay(200); |
| gpio_set_value((ctrl_pdata->rst_gpio), 1); |
| msleep(20); |
| if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) |
| gpio_set_value((ctrl_pdata->disp_en_gpio), 1); |
| } 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 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; |
| } |
| |
| pcmds->link_state = DSI_LP_MODE; /* default */ |
| |
| data = of_get_property(np, link_key, NULL); |
| if (!strncmp(data, "DSI_HS_MODE", 11)) |
| pcmds->link_state = DSI_HS_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_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; |
| u32 fbc_res[7]; |
| int rc, i, len; |
| const char *data; |
| static const char *bl_ctrl_type, *pdest; |
| bool fbc_enabled = false; |
| |
| 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_array(np, "qcom,mdss-pan-active-res", res, 2); |
| if (rc == 0) { |
| panel_data->panel_info.lcdc.xres_pad = |
| panel_data->panel_info.xres - res[0]; |
| panel_data->panel_info.lcdc.yres_pad = |
| panel_data->panel_info.yres - res[1]; |
| } |
| |
| 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); |
| |
| pdest = of_get_property(pdev->dev.of_node, |
| "qcom,mdss-pan-dest", NULL); |
| if (strlen(pdest) != 9) { |
| pr_err("%s: Unknown pdest specified\n", __func__); |
| return -EINVAL; |
| } |
| if (!strncmp(pdest, "display_1", 9)) |
| panel_data->panel_info.pdest = DISPLAY_1; |
| else if (!strncmp(pdest, "display_2", 9)) |
| panel_data->panel_info.pdest = DISPLAY_2; |
| else { |
| pr_debug("%s: pdest not specified. Set Default\n", |
| __func__); |
| panel_data->panel_info.pdest = DISPLAY_1; |
| } |
| |
| 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); |
| |
| bl_ctrl_type = of_get_property(pdev->dev.of_node, |
| "qcom,mdss-pan-bl-ctrl", NULL); |
| if ((bl_ctrl_type) && (!strncmp(bl_ctrl_type, "bl_ctrl_wled", 12))) { |
| led_trigger_register_simple("bkl-trigger", &bl_led_trigger); |
| pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", __func__); |
| |
| panel_data->panel_info.bklt_ctrl = BL_WLED; |
| } else if (!strncmp(bl_ctrl_type, "bl_ctrl_pwm", 11)) { |
| panel_data->panel_info.bklt_ctrl = BL_PWM; |
| |
| rc = of_property_read_u32(np, "qcom,pwm-period", &tmp); |
| if (rc) { |
| pr_err("%s:%d, Error, panel pwm_period\n", |
| __func__, __LINE__); |
| return -EINVAL; |
| } |
| panel_data->panel_info.pwm_period = tmp; |
| |
| rc = of_property_read_u32(np, "qcom,pwm-lpg-channel", &tmp); |
| if (rc) { |
| pr_err("%s:%d, Error, dsi lpg channel\n", |
| __func__, __LINE__); |
| return -EINVAL; |
| } |
| panel_data->panel_info.pwm_lpg_chan = tmp; |
| |
| tmp = of_get_named_gpio(np, "qcom,pwm-pmic-gpio", 0); |
| panel_data->panel_info.pwm_pmic_gpio = tmp; |
| } else if (!strncmp(bl_ctrl_type, "bl_ctrl_dcs", 11)) { |
| panel_data->panel_info.bklt_ctrl = BL_DCS_CMD; |
| } else { |
| pr_debug("%s: Unknown backlight control\n", __func__); |
| panel_data->panel_info.bklt_ctrl = UNKNOWN_CTRL; |
| } |
| |
| 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-vsync-enable", &tmp); |
| panel_data->panel_info.mipi.vsync_enable = (!rc ? tmp : 0); |
| |
| rc = of_property_read_u32(np, "qcom,mdss-hw-vsync-mode", &tmp); |
| panel_data->panel_info.mipi.hw_vsync_mode = (!rc ? tmp : 0); |
| |
| |
| 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-insert-dcs-cmd", &tmp); |
| panel_data->panel_info.mipi.insert_dcs_cmd = |
| (!rc ? tmp : 1); |
| |
| rc = of_property_read_u32(np, |
| "qcom,mdss-pan-wr-mem-continue", &tmp); |
| panel_data->panel_info.mipi.wr_mem_continue = |
| (!rc ? tmp : 0x3c); |
| |
| rc = of_property_read_u32(np, |
| "qcom,mdss-pan-wr-mem-start", &tmp); |
| panel_data->panel_info.mipi.wr_mem_start = |
| (!rc ? tmp : 0x2c); |
| |
| rc = of_property_read_u32(np, |
| "qcom,mdss-pan-te-sel", &tmp); |
| panel_data->panel_info.mipi.te_sel = |
| (!rc ? tmp : 1); |
| |
| 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(np, "qcom,mdss-pan-dsi-dlane-swap", &tmp); |
| panel_data->panel_info.mipi.dlane_swap = (!rc ? tmp : 0); |
| |
| 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); |
| |
| rc = of_property_read_u32(np, "qcom,mdss-pan-clk-rate", &tmp); |
| panel_data->panel_info.clk_rate = (!rc ? tmp : 0); |
| |
| data = of_get_property(np, "qcom,panel-phy-regulatorSettings", &len); |
| if ((!data) || (len != 7)) { |
| pr_err("%s:%d, Unable to read Phy regulator settings", |
| __func__, __LINE__); |
| goto error; |
| } |
| for (i = 0; i < len; i++) |
| phy_params.regulator[i] = data[i]; |
| |
| data = of_get_property(np, "qcom,panel-phy-timingSettings", &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++) |
| phy_params.timing[i] = data[i]; |
| |
| data = of_get_property(np, "qcom,panel-phy-strengthCtrl", &len); |
| if ((!data) || (len != 2)) { |
| pr_err("%s:%d, Unable to read Phy Strength ctrl settings", |
| __func__, __LINE__); |
| goto error; |
| } |
| phy_params.strength[0] = data[0]; |
| phy_params.strength[1] = data[1]; |
| |
| data = of_get_property(np, "qcom,panel-phy-bistCtrl", &len); |
| if ((!data) || (len != 6)) { |
| pr_err("%s:%d, Unable to read Phy Bist Ctrl settings", |
| __func__, __LINE__); |
| goto error; |
| } |
| for (i = 0; i < len; i++) |
| phy_params.bistCtrl[i] = data[i]; |
| |
| data = of_get_property(np, "qcom,panel-phy-laneConfig", &len); |
| if ((!data) || (len != 45)) { |
| pr_err("%s:%d, Unable to read Phy lane configure settings", |
| __func__, __LINE__); |
| goto error; |
| } |
| for (i = 0; i < len; i++) |
| phy_params.laneCfg[i] = data[i]; |
| |
| panel_data->panel_info.mipi.dsi_phy_db = &phy_params; |
| |
| fbc_enabled = of_property_read_bool(np, |
| "qcom,fbc-enabled"); |
| if (fbc_enabled) { |
| pr_debug("%s:%d FBC panel enabled.\n", __func__, __LINE__); |
| panel_data->panel_info.fbc.enabled = 1; |
| |
| rc = of_property_read_u32_array(np, |
| "qcom,fbc-mode", fbc_res, 7); |
| panel_data->panel_info.fbc.target_bpp = |
| (!rc ? fbc_res[0] : panel_data->panel_info.bpp); |
| panel_data->panel_info.fbc.comp_mode = (!rc ? fbc_res[1] : 0); |
| panel_data->panel_info.fbc.qerr_enable = |
| (!rc ? fbc_res[2] : 0); |
| panel_data->panel_info.fbc.cd_bias = (!rc ? fbc_res[3] : 0); |
| panel_data->panel_info.fbc.pat_enable = (!rc ? fbc_res[4] : 0); |
| panel_data->panel_info.fbc.vlc_enable = (!rc ? fbc_res[5] : 0); |
| panel_data->panel_info.fbc.bflc_enable = |
| (!rc ? fbc_res[6] : 0); |
| |
| rc = of_property_read_u32_array(np, |
| "qcom,fbc-budget-ctl", fbc_res, 3); |
| panel_data->panel_info.fbc.line_x_budget = |
| (!rc ? fbc_res[0] : 0); |
| panel_data->panel_info.fbc.block_x_budget = |
| (!rc ? fbc_res[1] : 0); |
| panel_data->panel_info.fbc.block_budget = |
| (!rc ? fbc_res[2] : 0); |
| |
| rc = of_property_read_u32_array(np, |
| "qcom,fbc-lossy-mode", fbc_res, 4); |
| panel_data->panel_info.fbc.lossless_mode_thd = |
| (!rc ? fbc_res[0] : 0); |
| panel_data->panel_info.fbc.lossy_mode_thd = |
| (!rc ? fbc_res[1] : 0); |
| panel_data->panel_info.fbc.lossy_rgb_thd = |
| (!rc ? fbc_res[2] : 0); |
| panel_data->panel_info.fbc.lossy_mode_idx = |
| (!rc ? fbc_res[3] : 0); |
| |
| } else { |
| pr_debug("%s:%d Panel does not support FBC.\n", |
| __func__, __LINE__); |
| panel_data->panel_info.fbc.enabled = 0; |
| panel_data->panel_info.fbc.target_bpp = |
| panel_data->panel_info.bpp; |
| } |
| |
| mdss_dsi_parse_dcs_cmds(np, &panel_data->on_cmds, |
| "qcom,panel-on-cmds", "qcom,on-cmds-dsi-state"); |
| |
| mdss_dsi_parse_dcs_cmds(np, &panel_data->off_cmds, |
| "qcom,panel-off-cmds", "qcom,off-cmds-dsi-state"); |
| |
| return 0; |
| |
| error: |
| return -EINVAL; |
| } |
| |
| static int __devinit mdss_dsi_panel_probe(struct platform_device *pdev) |
| { |
| int rc = 0; |
| static struct mdss_panel_common_pdata vendor_pdata; |
| static const char *panel_name; |
| |
| 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); |
| |
| rc = mdss_panel_parse_dt(pdev, &vendor_pdata); |
| if (rc) |
| return rc; |
| |
| vendor_pdata.on = mdss_dsi_panel_on; |
| vendor_pdata.off = mdss_dsi_panel_off; |
| vendor_pdata.bl_fnc = mdss_dsi_panel_bl_ctrl; |
| |
| 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) |
| { |
| return platform_driver_register(&this_driver); |
| } |
| module_init(mdss_dsi_panel_init); |