Loading drivers/base/class.c +6 −1 Original line number Diff line number Diff line Loading @@ -9,6 +9,11 @@ * This file is released under the GPLv2 * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #include <linux/device.h> #include <linux/module.h> Loading Loading @@ -83,7 +88,7 @@ static struct kobj_type class_ktype = { }; /* Hotplug events for classes go to the class subsys */ static struct kset *class_kset; struct kset *class_kset; int class_create_file_ns(struct class *cls, const struct class_attribute *attr, Loading drivers/video/fbdev/msm/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,15 @@ config FB_MSM_MDSS_WRITEBACK The MDSS Writeback Panel provides support for routing the output of MDSS frame buffer driver and MDP processing to memory. config FB_MSM_MDSS_SPECIFIC_PANEL depends on FB_MSM_MDSS bool prompt "MDSS Specific panel" default n ---help--- The MDSS Specific Panel provides support for specific panel driver file. config FB_MSM_MDSS_HDMI_PANEL depends on FB_MSM_MDSS select MSM_EXT_DISPLAY Loading drivers/video/fbdev/msm/Makefile +7 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o endif mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o ifeq ($(CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL),y) mdss-dsi-objs += mdss_dsi_panel_driver.o mdss-dsi-objs += mdss_dsi_panel_debugfs.o endif mdss-dsi-objs += mdss_dsi_panel.o mdss-dsi-objs += msm_mdss_io_8974.o mdss-dsi-objs += mdss_dsi_phy.o Loading Loading @@ -63,4 +67,7 @@ obj-$(CONFIG_FB_MSM_QPIC) += mdss-qpic.o obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o ifeq ($(CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL),y) obj-$(CONFIG_FB_MSM_MDSS) += incell.o endif obj-$(CONFIG_COMPAT) += mdss_compat_utils.o drivers/video/fbdev/msm/incell.c 0 → 100644 +650 −0 Original line number Diff line number Diff line /* drivers/video/fbdev/msm/incell.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. */ /* * Copyright (C) 2016 Sony Mobile Communications Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. */ #include <linux/incell.h> #include <linux/fb.h> #include <linux/string.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/device.h> #include <linux/mdss_io_util.h> #include "mdss_fb.h" #include "mdss_mdp.h" #include "mdss_dsi.h" #include "mdss_dsi_panel_driver.h" struct incell_ctrl *incell; struct incell_ctrl incell_buf; struct incell_ctrl *incell_get_info(void) { return incell; } int incell_get_power_status(incell_pw_status *power_status) { struct incell_ctrl *incell = incell_get_info(); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return INCELL_ERROR; } pr_debug("%s: status = 0x%x\n", __func__, (incell->state)); if (mdss_dsi_panel_driver_is_power_on(incell->state)) { power_status->display_power = INCELL_POWER_ON; power_status->touch_power = INCELL_POWER_ON; } else { power_status->display_power = INCELL_POWER_OFF; power_status->touch_power = INCELL_POWER_OFF; } return INCELL_OK; } static int incell_driver_power_off(struct fb_info *info) { int ret = INCELL_ERROR; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; struct msm_fb_data_type *mfd = NULL; struct incell_ctrl *incell = incell_get_info(); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return ret; } if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); return ret; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); return ret; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); return ret; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); return ret; } if (!mdss_fb_is_power_on(mfd)) { mdss_dsi_panel_driver_power_off_ctrl(incell); mdss_dsi_panel_driver_state_change_off(incell); ret = mdss_dsi_panel_driver_power_off(pdata); if (ret) { pr_err("%s: Failed to power off ret=%d\n", __func__, ret); ret = INCELL_ERROR; return ret; } } else { ret = info->fbops->fb_blank(FB_BLANK_POWERDOWN, info); if (ret) { pr_err("%s: fb_blank(blank) FAIL ret=%d\n", __func__, ret); ret = INCELL_ERROR; return ret; } } ret = INCELL_OK; return ret; } static int incell_driver_send_power_on_seq(struct mdss_panel_data *pdata) { int ret = INCELL_ERROR; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_specific_pdata *spec_pdata = NULL; ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); spec_pdata = ctrl_pdata->spec_pdata; if (!ctrl_pdata || !spec_pdata) { pr_err("%s: Invalid input data\n", __func__); goto end; } ret = mdss_dsi_panel_driver_power_on(pdata); if (ret) { pr_err("%s: Failed to power on ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } if (mdss_dsi_pinctrl_set_state(ctrl_pdata, true)) pr_debug("%s: reset enable: pinctrl not enabled\n", __func__); mdss_dsi_panel_reset(pdata, 1); if (incell->incell_intf_operation == INCELL_TOUCH_RUN && incell->intf_mode == INCELL_DISPLAY_HW_RESET) { mdss_dsi_panel_driver_reset_touch(pdata, 1); mdss_dsi_panel_driver_state_change_on(incell); } else { if (!ctrl_pdata->on) goto end; ret = ctrl_pdata->on(pdata); if (ret) { pr_err("%s: Failed to send on ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } if (!ctrl_pdata->post_panel_on) goto end; ret = ctrl_pdata->post_panel_on(pdata); if (ret) { pr_err("%s: Failed to send post-on ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } } ret = INCELL_OK; end: return ret; } static int incell_driver_send_power_on_fb(struct fb_info *info) { int ret = INCELL_ERROR; if (!(info->fbops->fb_open) || !(info->fbops->fb_blank) || !(info->fbops->fb_release)) { pr_err("%s: Invalid operations\n", __func__); goto end; } ret = info->fbops->fb_blank(FB_BLANK_UNBLANK, info); if (ret) { pr_err("%s: fb_blank(blank) FAIL ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } ret = INCELL_OK; end: return ret; } static int incell_driver_power_on(struct fb_info *info) { int ret = INCELL_ERROR; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; struct msm_fb_data_type *mfd = NULL; struct fb_specific_data *spec_mfd = NULL; if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); goto end; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); goto end; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); goto end; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); goto end; } spec_mfd = &mfd->spec_mfd; if (mdss_fb_is_power_on(mfd)) ret = incell_driver_send_power_on_seq(pdata); else ret = incell_driver_send_power_on_fb(info); if (spec_mfd->off_sts) spec_mfd->off_sts = false; end: return ret; } static int incell_display_hw_reset(struct incell_ctrl *incell, struct fb_info *info) { int ret = INCELL_ERROR; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; if (!incell) { pr_err("%s: Invalid incell data\n", __func__); goto end; } if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); goto end; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); goto end; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); goto end; } /* Power off if LCD is on. */ if (mdss_dsi_panel_driver_is_power_on(incell->state)) /* * It calls directly power off to DSI layer, * the case of FB off. */ ret = incell_driver_power_off(info); else pr_debug("%s: Skip LCD off 0x%x\n", __func__, (incell->state)); if (!mdss_dsi_panel_driver_is_system_on(incell->state) && !mdss_dsi_panel_driver_is_power_on(incell->state)) { pr_debug("%s: LCD on in DSI layer. sts:0x%x\n", __func__, (incell->state)); ret = incell_driver_send_power_on_seq(pdata); } else { pr_debug("%s: LCD on in FB layer. sts:0x%x\n", __func__, (incell->state)); ret = incell_driver_send_power_on_fb(info); } ret = INCELL_OK; end: return ret; } static int incell_display_off(struct incell_ctrl *incell, struct fb_info *info) { int ret = INCELL_ERROR; if (!mdss_dsi_panel_driver_is_power_on(incell->state)) { pr_err("%s: LCD is already off. sts:0x%x\n", __func__, (incell->state)); ret = INCELL_EALREADY; } else { pr_debug("%s: incell panel sts:0x%x\n", __func__, (incell->state)); } if (ret == INCELL_EALREADY) { pr_err("%s: Already power off ret=%d\n", __func__, ret); return ret; } ret = incell_driver_power_off(info); return ret; } int incell_control_mode(incell_intf_mode mode, bool force) { int ret = INCELL_ERROR; struct fb_info *info = NULL; struct msm_fb_data_type *mfd = NULL; struct fb_specific_data *spec_mfd = NULL; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_specific_pdata *spec_pdata = NULL; struct incell_ctrl *incell = incell_get_info(); pr_notice("%s: START - %s:%s\n", __func__, ((mode == INCELL_DISPLAY_HW_RESET) ? "INCELL_DISPLAY_HW_RESET" : ((mode == INCELL_DISPLAY_OFF) ? "INCELL_DISPLAY_OFF" : "INCELL_DISPLAY_ON")), ((force) ? "force" : "unforce")); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return ret; } info = registered_fb[0]; if (!info) { pr_err("%s: Invalid fb data\n", __func__); return ret; } if (!(info->fbops->fb_blank) || !(info->fbops->fb_release) || !(info->fbops->fb_open)) { pr_err("%s: Invalid operations\n", __func__); return ret; } if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); return ret; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); return ret; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); return ret; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); return ret; } spec_mfd = &mfd->spec_mfd; ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); spec_pdata = ctrl_pdata->spec_pdata; if (!ctrl_pdata || !spec_pdata) { pr_err("%s: Invalid input data\n", __func__); return ret; } /* * It returns "INCELL_ALREADY_LOCKED" * the case of not setting "INCELL_FORCE" flag. */ if (mdss_dsi_panel_driver_is_power_lock(incell->state)) { if (force == INCELL_UNFORCE) { ret = INCELL_ALREADY_LOCKED; pr_err("%s: Already power locked ret=%d\n", __func__, ret); return ret; } } if (incell->worker_state != INCELL_WORKER_OFF) { ret = INCELL_EBUSY; pr_err("%s: worker scheduling ret=%d\n", __func__, ret); return ret; } if (incell->incell_intf_operation == INCELL_TOUCH_RUN) { ret = INCELL_EBUSY; pr_err("%s: touch I/F not finished ret=%d\n", __func__, ret); return ret; } incell->incell_intf_operation = INCELL_TOUCH_RUN; if (!mutex_trylock(&info->lock)) { incell->incell_intf_operation = INCELL_TOUCH_IDLE; pr_err("%s: mutex_locked ret=%d\n", __func__, ret); ret = INCELL_EBUSY; return ret; } incell->intf_mode = mode; switch (mode) { case INCELL_DISPLAY_ON: incell->intf_mode = INCELL_DISPLAY_HW_RESET; case INCELL_DISPLAY_HW_RESET: spec_mfd->off_sts = true; ret = incell_display_hw_reset(incell, info); spec_mfd->off_sts = false; break; case INCELL_DISPLAY_OFF: spec_mfd->off_sts = true; ret = incell_display_off(incell, info); break; default: pr_err("%s: Invalid mode for touch interface %d\n", __func__, (int)(mode)); break; } mutex_unlock(&info->lock); incell->incell_intf_operation = INCELL_TOUCH_IDLE; pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, (incell->state)); return ret; } static void incell_panel_power_worker(struct work_struct *work) { struct incell_ctrl *incell = incell_get_info(); struct fb_info *info = registered_fb[0]; pr_notice("%s: START - incell.status:0x%x\n", __func__, (incell->state)); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return; } if (!info) { pr_err("%s: Invalid fb data\n", __func__); return; } mutex_lock(&info->lock); incell->worker_state = INCELL_WORKER_ON; if (incell->state == INCELL_WORK_NEED_P_OFF) incell_driver_power_off(info); else if (incell->state == INCELL_WORK_NEED_P_ON || incell->state == INCELL_WORK_NEED_P_ON_EWU) incell_driver_power_on(info); incell->worker_state = INCELL_WORKER_OFF; mutex_unlock(&info->lock); pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, (incell->state)); } static void incell_panel_power_worker_scheduling(incell_pw_lock lock, struct incell_ctrl *incell) { unsigned char state = incell->state; if (lock == INCELL_DISPLAY_POWER_LOCK) return; if (state != INCELL_WORK_NEED_P_OFF && state != INCELL_WORK_NEED_P_ON && state != INCELL_WORK_NEED_P_ON_EWU) return; incell->worker_state = INCELL_WORKER_PENDING; schedule_work(&incell->incell_work); pr_notice("%s: incell worker scheduled - incell.status:0x%x\n", __func__, (incell->state)); } void incell_panel_power_worker_canceling(struct incell_ctrl *incell) { struct fb_info *info = registered_fb[0]; struct msm_fb_data_type *mfd = NULL; struct fb_specific_data *spec_mfd = NULL; cancel_work_sync(&incell->incell_work); pr_notice("%s: incell worker canceled - incell.status:0x%x\n", __func__, (incell->state)); if (!info) { pr_err("%s: Invalid fb data\n", __func__); goto end; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); goto end; } spec_mfd = &mfd->spec_mfd; if (spec_mfd->off_sts) spec_mfd->off_sts = false; end: incell->worker_state = INCELL_WORKER_OFF; } static int incell_power_lock(unsigned char *state) { if (mdss_dsi_panel_driver_is_power_lock(*state)) { pr_err("%s: Power state already locked", __func__); return INCELL_ALREADY_LOCKED; } *state |= INCELL_LOCK_STATE_ON; return INCELL_OK; } static int incell_power_unlock(unsigned char *state) { if (!mdss_dsi_panel_driver_is_power_lock(*state)) { pr_err("%s: Power state already unlocked", __func__); return INCELL_ALREADY_UNLOCKED; } *state &= INCELL_LOCK_STATE_OFF; return INCELL_OK; } int incell_power_lock_ctrl(incell_pw_lock lock, incell_pw_status *power_status) { int ret = INCELL_ERROR; struct incell_ctrl *incell = incell_get_info(); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return ret; } if (incell->worker_state != INCELL_WORKER_OFF) { ret = INCELL_EBUSY; pr_err("%s: worker scheduling ret=%d\n", __func__, ret); return ret; } if (incell->incell_intf_operation == INCELL_TOUCH_RUN) { ret = INCELL_EBUSY; pr_err("%s: touch I/F not finished ret=%d\n", __func__, ret); return ret; } incell->incell_intf_operation = INCELL_TOUCH_RUN; pr_debug("%s: status:0x%x --->\n", __func__, (incell->state)); mdss_dsi_panel_driver_update_incell_bk(incell); if (lock == INCELL_DISPLAY_POWER_LOCK) ret = incell_power_lock(&(incell->state)); else ret = incell_power_unlock(&(incell->state)); pr_debug("%s: ---> status:0x%x\n", __func__, (incell->state)); incell_get_power_status(power_status); incell_panel_power_worker_scheduling(lock, incell); incell->incell_intf_operation = INCELL_TOUCH_IDLE; return ret; } void incell_ewu_mode_ctrl(incell_ewu_mode ewu) { struct incell_ctrl *incell = incell_get_info(); pr_notice("%s: START - %s\n", __func__, ((ewu == INCELL_DISPLAY_EWU_DISABLE) ? "INCELL_DISPLAY_EWU_DISABLE" : "INCELL_DISPLAY_EWU_ENABLE")); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return; } pr_debug("%s: EWU mode is %s\n", __func__, ewu == INCELL_DISPLAY_EWU_ENABLE ? "on":"off"); pr_debug("%s: status:0x%x --->\n", __func__, (incell->state)); mdss_dsi_panel_driver_update_incell_bk(incell); if (ewu == INCELL_DISPLAY_EWU_ENABLE) incell->state |= INCELL_EWU_STATE_ON; else incell->state &= INCELL_EWU_STATE_OFF; pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, (incell->state)); } void incell_driver_init(void) { memset(&incell_buf, 0, sizeof(struct incell_ctrl)); incell = &incell_buf; incell->state = INCELL_INIT_STATE_KERNEL; incell->incell_intf_operation = INCELL_TOUCH_IDLE; incell->worker_state = INCELL_WORKER_OFF; INIT_WORK(&incell->incell_work, incell_panel_power_worker); } drivers/video/fbdev/msm/mdss.h +9 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #ifndef MDSS_H #define MDSS_H Loading Loading @@ -37,6 +42,10 @@ #define MDSS_PINCTRL_STATE_DEFAULT "mdss_default" #define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep" #ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL #define MDSS_PINCTRL_STATE_TOUCH_ACTIVE "mdss_touch_active" #define MDSS_PINCTRL_STATE_TOUCH_SUSPEND "mdss_touch_suspend" #endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ enum mdss_mdp_clk_type { MDSS_CLK_AHB, Loading Loading
drivers/base/class.c +6 −1 Original line number Diff line number Diff line Loading @@ -9,6 +9,11 @@ * This file is released under the GPLv2 * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #include <linux/device.h> #include <linux/module.h> Loading Loading @@ -83,7 +88,7 @@ static struct kobj_type class_ktype = { }; /* Hotplug events for classes go to the class subsys */ static struct kset *class_kset; struct kset *class_kset; int class_create_file_ns(struct class *cls, const struct class_attribute *attr, Loading
drivers/video/fbdev/msm/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,15 @@ config FB_MSM_MDSS_WRITEBACK The MDSS Writeback Panel provides support for routing the output of MDSS frame buffer driver and MDP processing to memory. config FB_MSM_MDSS_SPECIFIC_PANEL depends on FB_MSM_MDSS bool prompt "MDSS Specific panel" default n ---help--- The MDSS Specific Panel provides support for specific panel driver file. config FB_MSM_MDSS_HDMI_PANEL depends on FB_MSM_MDSS select MSM_EXT_DISPLAY Loading
drivers/video/fbdev/msm/Makefile +7 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o endif mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o ifeq ($(CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL),y) mdss-dsi-objs += mdss_dsi_panel_driver.o mdss-dsi-objs += mdss_dsi_panel_debugfs.o endif mdss-dsi-objs += mdss_dsi_panel.o mdss-dsi-objs += msm_mdss_io_8974.o mdss-dsi-objs += mdss_dsi_phy.o Loading Loading @@ -63,4 +67,7 @@ obj-$(CONFIG_FB_MSM_QPIC) += mdss-qpic.o obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o ifeq ($(CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL),y) obj-$(CONFIG_FB_MSM_MDSS) += incell.o endif obj-$(CONFIG_COMPAT) += mdss_compat_utils.o
drivers/video/fbdev/msm/incell.c 0 → 100644 +650 −0 Original line number Diff line number Diff line /* drivers/video/fbdev/msm/incell.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. */ /* * Copyright (C) 2016 Sony Mobile Communications Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. */ #include <linux/incell.h> #include <linux/fb.h> #include <linux/string.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/device.h> #include <linux/mdss_io_util.h> #include "mdss_fb.h" #include "mdss_mdp.h" #include "mdss_dsi.h" #include "mdss_dsi_panel_driver.h" struct incell_ctrl *incell; struct incell_ctrl incell_buf; struct incell_ctrl *incell_get_info(void) { return incell; } int incell_get_power_status(incell_pw_status *power_status) { struct incell_ctrl *incell = incell_get_info(); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return INCELL_ERROR; } pr_debug("%s: status = 0x%x\n", __func__, (incell->state)); if (mdss_dsi_panel_driver_is_power_on(incell->state)) { power_status->display_power = INCELL_POWER_ON; power_status->touch_power = INCELL_POWER_ON; } else { power_status->display_power = INCELL_POWER_OFF; power_status->touch_power = INCELL_POWER_OFF; } return INCELL_OK; } static int incell_driver_power_off(struct fb_info *info) { int ret = INCELL_ERROR; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; struct msm_fb_data_type *mfd = NULL; struct incell_ctrl *incell = incell_get_info(); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return ret; } if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); return ret; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); return ret; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); return ret; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); return ret; } if (!mdss_fb_is_power_on(mfd)) { mdss_dsi_panel_driver_power_off_ctrl(incell); mdss_dsi_panel_driver_state_change_off(incell); ret = mdss_dsi_panel_driver_power_off(pdata); if (ret) { pr_err("%s: Failed to power off ret=%d\n", __func__, ret); ret = INCELL_ERROR; return ret; } } else { ret = info->fbops->fb_blank(FB_BLANK_POWERDOWN, info); if (ret) { pr_err("%s: fb_blank(blank) FAIL ret=%d\n", __func__, ret); ret = INCELL_ERROR; return ret; } } ret = INCELL_OK; return ret; } static int incell_driver_send_power_on_seq(struct mdss_panel_data *pdata) { int ret = INCELL_ERROR; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_specific_pdata *spec_pdata = NULL; ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); spec_pdata = ctrl_pdata->spec_pdata; if (!ctrl_pdata || !spec_pdata) { pr_err("%s: Invalid input data\n", __func__); goto end; } ret = mdss_dsi_panel_driver_power_on(pdata); if (ret) { pr_err("%s: Failed to power on ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } if (mdss_dsi_pinctrl_set_state(ctrl_pdata, true)) pr_debug("%s: reset enable: pinctrl not enabled\n", __func__); mdss_dsi_panel_reset(pdata, 1); if (incell->incell_intf_operation == INCELL_TOUCH_RUN && incell->intf_mode == INCELL_DISPLAY_HW_RESET) { mdss_dsi_panel_driver_reset_touch(pdata, 1); mdss_dsi_panel_driver_state_change_on(incell); } else { if (!ctrl_pdata->on) goto end; ret = ctrl_pdata->on(pdata); if (ret) { pr_err("%s: Failed to send on ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } if (!ctrl_pdata->post_panel_on) goto end; ret = ctrl_pdata->post_panel_on(pdata); if (ret) { pr_err("%s: Failed to send post-on ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } } ret = INCELL_OK; end: return ret; } static int incell_driver_send_power_on_fb(struct fb_info *info) { int ret = INCELL_ERROR; if (!(info->fbops->fb_open) || !(info->fbops->fb_blank) || !(info->fbops->fb_release)) { pr_err("%s: Invalid operations\n", __func__); goto end; } ret = info->fbops->fb_blank(FB_BLANK_UNBLANK, info); if (ret) { pr_err("%s: fb_blank(blank) FAIL ret=%d\n", __func__, ret); ret = INCELL_ERROR; goto end; } ret = INCELL_OK; end: return ret; } static int incell_driver_power_on(struct fb_info *info) { int ret = INCELL_ERROR; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; struct msm_fb_data_type *mfd = NULL; struct fb_specific_data *spec_mfd = NULL; if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); goto end; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); goto end; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); goto end; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); goto end; } spec_mfd = &mfd->spec_mfd; if (mdss_fb_is_power_on(mfd)) ret = incell_driver_send_power_on_seq(pdata); else ret = incell_driver_send_power_on_fb(info); if (spec_mfd->off_sts) spec_mfd->off_sts = false; end: return ret; } static int incell_display_hw_reset(struct incell_ctrl *incell, struct fb_info *info) { int ret = INCELL_ERROR; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; if (!incell) { pr_err("%s: Invalid incell data\n", __func__); goto end; } if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); goto end; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); goto end; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); goto end; } /* Power off if LCD is on. */ if (mdss_dsi_panel_driver_is_power_on(incell->state)) /* * It calls directly power off to DSI layer, * the case of FB off. */ ret = incell_driver_power_off(info); else pr_debug("%s: Skip LCD off 0x%x\n", __func__, (incell->state)); if (!mdss_dsi_panel_driver_is_system_on(incell->state) && !mdss_dsi_panel_driver_is_power_on(incell->state)) { pr_debug("%s: LCD on in DSI layer. sts:0x%x\n", __func__, (incell->state)); ret = incell_driver_send_power_on_seq(pdata); } else { pr_debug("%s: LCD on in FB layer. sts:0x%x\n", __func__, (incell->state)); ret = incell_driver_send_power_on_fb(info); } ret = INCELL_OK; end: return ret; } static int incell_display_off(struct incell_ctrl *incell, struct fb_info *info) { int ret = INCELL_ERROR; if (!mdss_dsi_panel_driver_is_power_on(incell->state)) { pr_err("%s: LCD is already off. sts:0x%x\n", __func__, (incell->state)); ret = INCELL_EALREADY; } else { pr_debug("%s: incell panel sts:0x%x\n", __func__, (incell->state)); } if (ret == INCELL_EALREADY) { pr_err("%s: Already power off ret=%d\n", __func__, ret); return ret; } ret = incell_driver_power_off(info); return ret; } int incell_control_mode(incell_intf_mode mode, bool force) { int ret = INCELL_ERROR; struct fb_info *info = NULL; struct msm_fb_data_type *mfd = NULL; struct fb_specific_data *spec_mfd = NULL; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_mdp_ctl *ctl = NULL; struct mdss_panel_data *pdata = NULL; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_specific_pdata *spec_pdata = NULL; struct incell_ctrl *incell = incell_get_info(); pr_notice("%s: START - %s:%s\n", __func__, ((mode == INCELL_DISPLAY_HW_RESET) ? "INCELL_DISPLAY_HW_RESET" : ((mode == INCELL_DISPLAY_OFF) ? "INCELL_DISPLAY_OFF" : "INCELL_DISPLAY_ON")), ((force) ? "force" : "unforce")); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return ret; } info = registered_fb[0]; if (!info) { pr_err("%s: Invalid fb data\n", __func__); return ret; } if (!(info->fbops->fb_blank) || !(info->fbops->fb_release) || !(info->fbops->fb_open)) { pr_err("%s: Invalid operations\n", __func__); return ret; } if (!mdata) { pr_err("%s: Invalid mdata\n", __func__); return ret; } ctl = mdata->ctl_off; if (!ctl) { pr_err("%s: Invalid ctl data\n", __func__); return ret; } pdata = ctl->panel_data; if (!pdata) { pr_err("%s: Invalid panel data\n", __func__); return ret; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); return ret; } spec_mfd = &mfd->spec_mfd; ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); spec_pdata = ctrl_pdata->spec_pdata; if (!ctrl_pdata || !spec_pdata) { pr_err("%s: Invalid input data\n", __func__); return ret; } /* * It returns "INCELL_ALREADY_LOCKED" * the case of not setting "INCELL_FORCE" flag. */ if (mdss_dsi_panel_driver_is_power_lock(incell->state)) { if (force == INCELL_UNFORCE) { ret = INCELL_ALREADY_LOCKED; pr_err("%s: Already power locked ret=%d\n", __func__, ret); return ret; } } if (incell->worker_state != INCELL_WORKER_OFF) { ret = INCELL_EBUSY; pr_err("%s: worker scheduling ret=%d\n", __func__, ret); return ret; } if (incell->incell_intf_operation == INCELL_TOUCH_RUN) { ret = INCELL_EBUSY; pr_err("%s: touch I/F not finished ret=%d\n", __func__, ret); return ret; } incell->incell_intf_operation = INCELL_TOUCH_RUN; if (!mutex_trylock(&info->lock)) { incell->incell_intf_operation = INCELL_TOUCH_IDLE; pr_err("%s: mutex_locked ret=%d\n", __func__, ret); ret = INCELL_EBUSY; return ret; } incell->intf_mode = mode; switch (mode) { case INCELL_DISPLAY_ON: incell->intf_mode = INCELL_DISPLAY_HW_RESET; case INCELL_DISPLAY_HW_RESET: spec_mfd->off_sts = true; ret = incell_display_hw_reset(incell, info); spec_mfd->off_sts = false; break; case INCELL_DISPLAY_OFF: spec_mfd->off_sts = true; ret = incell_display_off(incell, info); break; default: pr_err("%s: Invalid mode for touch interface %d\n", __func__, (int)(mode)); break; } mutex_unlock(&info->lock); incell->incell_intf_operation = INCELL_TOUCH_IDLE; pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, (incell->state)); return ret; } static void incell_panel_power_worker(struct work_struct *work) { struct incell_ctrl *incell = incell_get_info(); struct fb_info *info = registered_fb[0]; pr_notice("%s: START - incell.status:0x%x\n", __func__, (incell->state)); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return; } if (!info) { pr_err("%s: Invalid fb data\n", __func__); return; } mutex_lock(&info->lock); incell->worker_state = INCELL_WORKER_ON; if (incell->state == INCELL_WORK_NEED_P_OFF) incell_driver_power_off(info); else if (incell->state == INCELL_WORK_NEED_P_ON || incell->state == INCELL_WORK_NEED_P_ON_EWU) incell_driver_power_on(info); incell->worker_state = INCELL_WORKER_OFF; mutex_unlock(&info->lock); pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, (incell->state)); } static void incell_panel_power_worker_scheduling(incell_pw_lock lock, struct incell_ctrl *incell) { unsigned char state = incell->state; if (lock == INCELL_DISPLAY_POWER_LOCK) return; if (state != INCELL_WORK_NEED_P_OFF && state != INCELL_WORK_NEED_P_ON && state != INCELL_WORK_NEED_P_ON_EWU) return; incell->worker_state = INCELL_WORKER_PENDING; schedule_work(&incell->incell_work); pr_notice("%s: incell worker scheduled - incell.status:0x%x\n", __func__, (incell->state)); } void incell_panel_power_worker_canceling(struct incell_ctrl *incell) { struct fb_info *info = registered_fb[0]; struct msm_fb_data_type *mfd = NULL; struct fb_specific_data *spec_mfd = NULL; cancel_work_sync(&incell->incell_work); pr_notice("%s: incell worker canceled - incell.status:0x%x\n", __func__, (incell->state)); if (!info) { pr_err("%s: Invalid fb data\n", __func__); goto end; } mfd = (struct msm_fb_data_type *)info->par; if (!mfd) { pr_err("%s: Invalid msm data\n", __func__); goto end; } spec_mfd = &mfd->spec_mfd; if (spec_mfd->off_sts) spec_mfd->off_sts = false; end: incell->worker_state = INCELL_WORKER_OFF; } static int incell_power_lock(unsigned char *state) { if (mdss_dsi_panel_driver_is_power_lock(*state)) { pr_err("%s: Power state already locked", __func__); return INCELL_ALREADY_LOCKED; } *state |= INCELL_LOCK_STATE_ON; return INCELL_OK; } static int incell_power_unlock(unsigned char *state) { if (!mdss_dsi_panel_driver_is_power_lock(*state)) { pr_err("%s: Power state already unlocked", __func__); return INCELL_ALREADY_UNLOCKED; } *state &= INCELL_LOCK_STATE_OFF; return INCELL_OK; } int incell_power_lock_ctrl(incell_pw_lock lock, incell_pw_status *power_status) { int ret = INCELL_ERROR; struct incell_ctrl *incell = incell_get_info(); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return ret; } if (incell->worker_state != INCELL_WORKER_OFF) { ret = INCELL_EBUSY; pr_err("%s: worker scheduling ret=%d\n", __func__, ret); return ret; } if (incell->incell_intf_operation == INCELL_TOUCH_RUN) { ret = INCELL_EBUSY; pr_err("%s: touch I/F not finished ret=%d\n", __func__, ret); return ret; } incell->incell_intf_operation = INCELL_TOUCH_RUN; pr_debug("%s: status:0x%x --->\n", __func__, (incell->state)); mdss_dsi_panel_driver_update_incell_bk(incell); if (lock == INCELL_DISPLAY_POWER_LOCK) ret = incell_power_lock(&(incell->state)); else ret = incell_power_unlock(&(incell->state)); pr_debug("%s: ---> status:0x%x\n", __func__, (incell->state)); incell_get_power_status(power_status); incell_panel_power_worker_scheduling(lock, incell); incell->incell_intf_operation = INCELL_TOUCH_IDLE; return ret; } void incell_ewu_mode_ctrl(incell_ewu_mode ewu) { struct incell_ctrl *incell = incell_get_info(); pr_notice("%s: START - %s\n", __func__, ((ewu == INCELL_DISPLAY_EWU_DISABLE) ? "INCELL_DISPLAY_EWU_DISABLE" : "INCELL_DISPLAY_EWU_ENABLE")); if (!incell) { pr_err("%s: Invalid incell data\n", __func__); return; } pr_debug("%s: EWU mode is %s\n", __func__, ewu == INCELL_DISPLAY_EWU_ENABLE ? "on":"off"); pr_debug("%s: status:0x%x --->\n", __func__, (incell->state)); mdss_dsi_panel_driver_update_incell_bk(incell); if (ewu == INCELL_DISPLAY_EWU_ENABLE) incell->state |= INCELL_EWU_STATE_ON; else incell->state &= INCELL_EWU_STATE_OFF; pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, (incell->state)); } void incell_driver_init(void) { memset(&incell_buf, 0, sizeof(struct incell_ctrl)); incell = &incell_buf; incell->state = INCELL_INIT_STATE_KERNEL; incell->incell_intf_operation = INCELL_TOUCH_IDLE; incell->worker_state = INCELL_WORKER_OFF; INIT_WORK(&incell->incell_work, incell_panel_power_worker); }
drivers/video/fbdev/msm/mdss.h +9 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #ifndef MDSS_H #define MDSS_H Loading Loading @@ -37,6 +42,10 @@ #define MDSS_PINCTRL_STATE_DEFAULT "mdss_default" #define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep" #ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL #define MDSS_PINCTRL_STATE_TOUCH_ACTIVE "mdss_touch_active" #define MDSS_PINCTRL_STATE_TOUCH_SUSPEND "mdss_touch_suspend" #endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ enum mdss_mdp_clk_type { MDSS_CLK_AHB, Loading