Import S928BXXU3AXH7 display-driver changes

This commit is contained in:
David Wronek 2024-10-06 19:51:20 +02:00
parent 336a9b0781
commit f6312f1936
147 changed files with 501358 additions and 40 deletions

View File

@ -0,0 +1,2 @@
config PANEL_E3_S6E3HAE_AMB681AZ01_WQHD
bool "LSI S6E3HAE LDI"

View File

@ -0,0 +1 @@
CONFIG_PANEL_E3_S6E3HAE_AMB681AZ01_WQHD=y

View File

@ -4,6 +4,10 @@ export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DRM_MSM_DP=y
export CONFIG_DRM_MSM_DP_MST=y
ifneq ($(CONFIG_SEC_DISPLAYPORT),)
export CONFIG_SECDP=y
export CONFIG_SECDP_BIGDATA=y
endif
export CONFIG_DSI_PARSER=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_DRM_SDE_RSC=y
@ -15,4 +19,4 @@ export CONFIG_HDCP_QSEECOM=y
export CONFIG_DRM_SDE_VM=y
export CONFIG_QTI_HW_FENCE=y
export CONFIG_QCOM_SPEC_SYNC=y
export CONFIG_QCOM_WCD939X_I2C=y
export CONFIG_QCOM_WCD939X_I2C=n

View File

@ -10,6 +10,20 @@
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DRM_MSM_DP 1
#define CONFIG_DRM_MSM_DP_MST 1
#if IS_ENABLED(CONFIG_SEC_DISPLAYPORT)
#define CONFIG_SECDP 1
#define CONFIG_SECDP_LOGGER 1
#define CONFIG_SECDP_BIGDATA 1
#if IS_ENABLED(CONFIG_SWITCH) || IS_ENABLED(CONFIG_ANDROID_SWITCH)
#define CONFIG_SECDP_SWITCH 1
#endif
#if IS_ENABLED(CONFIG_SEC_FORCE_ERR)
#define CONFIG_SECDP_DBG 1
#endif
#if IS_ENABLED(CONFIG_SEC_FACTORY)
#define CONFIG_SECDP_FACTORY_DPSWITCH_TEST
#endif
#endif
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_SDE_WB 1
#define CONFIG_DRM_SDE_RSC 1
@ -23,4 +37,4 @@
#define CONFIG_DRM_SDE_VM 1
#define CONFIG_QTI_HW_FENCE 1
#define CONFIG_QCOM_SPEC_SYNC 1
#define CONFIG_QCOM_WCD939X_I2C 1
#define CONFIG_QCOM_WCD939X_I2C 0

View File

@ -12,3 +12,7 @@ ifeq ($(DISPLAY_DLKM_ENABLE), true)
endif
DISPLAY_MODULES_DRIVER := msm_drm.ko
# ifdef CONFIG_DISPLAY_SAMSUNG
PRODUCT_COPY_FILES += $(foreach f,$(wildcard vendor/qcom/opensource/display-drivers/msm/samsung/panel_data_file/*),$(f):/vendor/firmware/$(notdir $(f)))
# endif // CONFIG_DISPLAY_SAMSUNG

View File

@ -3,4 +3,7 @@
header-y += msm_hdcp.h
header-y += sde_io_util.h
header-y += sde_rsc.h
header-y += sec_displayport.h
header-y += secdp_bigdata.h
header-y += secdp_logger.h

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef __SEC_DISPLAYPORT_H
#define __SEC_DISPLAYPORT_H
/*
* this function waits for completion of dp disconnection.
* return : zero if dp resource is released completely.
* non-zero if waiting timeout happens.
*/
int secdp_wait_for_disconnect_complete(void);
/**
* if SSUSB gets reset, it need to call this callback to let DP know, since
* DP shares PHY with SSUSB (combo PHY)
* return 0 if success
* return -1 otherwise
*/
int secdp_pdic_reset_cb(bool reset);
#endif/*__SEC_DISPLAYPORT_H*/

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef DISPLAYPORT_BIGDATA_H
#define DISPLAYPORT_BIGDATA_H
#include <linux/platform_device.h>
#include <stdarg.h>
enum DP_BD_ITEM_LIST {
ERR_AUX,
ERR_EDID,
ERR_HDCP_AUTH,
ERR_LINK_TRAIN,
ERR_INF_IRQHPD,
BD_LINK_CONFIGURE,
BD_ADAPTER_HWID,
BD_ADAPTER_FWVER,
BD_ADAPTER_TYPE,
BD_MAX_LANE_COUNT,
BD_MAX_LINK_RATE,
BD_CUR_LANE_COUNT,
BD_CUR_LINK_RATE,
BD_HDCP_VER,
BD_ORIENTATION,
BD_RESOLUTION,
BD_EDID,
BD_ADT_VID,
BD_ADT_PID,
BD_DP_MODE,
BD_SINK_NAME,
BD_AUD_CH,
BD_AUD_FREQ,
BD_AUD_BIT,
BD_ITEM_MAX,
};
void secdp_bigdata_save_item(enum DP_BD_ITEM_LIST item, ...);
void secdp_bigdata_inc_error_cnt(enum DP_BD_ITEM_LIST err);
void secdp_bigdata_clr_error_cnt(enum DP_BD_ITEM_LIST err);
void secdp_bigdata_connection(void);
void secdp_bigdata_disconnection(void);
void secdp_bigdata_init(struct class *dp_class);
ssize_t _secdp_bigdata_show(struct class *class,
struct class_attribute *attr, char *buf);
ssize_t _secdp_bigdata_store(struct class *dev,
struct class_attribute *attr, const char *buf, size_t size);
#endif /* DISPLAYPORT_BIGDATA_H */

View File

@ -0,0 +1,94 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef _SECDP_LOGGER_H_
#define _SECDP_LOGGER_H_
#ifdef CONFIG_SECDP_LOGGER
void secdp_logger_set_max_count(int count);
void secdp_logger_print(const char *fmt, ...);
void secdp_logger_hex_dump(void *buf, void *pref, size_t len);
int secdp_logger_init(void);
void secdp_logger_deinit(void);
void secdp_logger_set_mode_max_count(unsigned int count);
void secdp_logger_dec_mode_count(void);
void secdp_logger_print_mode(const char *fmt, ...);
#define secdp_pr_debug(fmt, ...) \
printk(KERN_DEBUG "[msm-dp] %s: " pr_fmt(fmt), __func__, ##__VA_ARGS__)
#define secdp_proc_err(fmt, ...) \
do { \
printk(KERN_ERR "[msm-dp] %s: " pr_fmt(fmt), __func__, ##__VA_ARGS__); \
secdp_logger_print(fmt, ##__VA_ARGS__); \
} while (0)
#define secdp_proc_info(fmt, ...) \
do { \
printk(KERN_INFO "[msm-dp] %s: " pr_fmt(fmt), __func__, ##__VA_ARGS__); \
secdp_logger_print(fmt, ##__VA_ARGS__); \
} while (0)
#define secdp_proc_warn(fmt, ...) \
do { \
printk(KERN_WARNING "[msm-dp] %s: " pr_fmt(fmt), __func__, ##__VA_ARGS__); \
secdp_logger_print(fmt, ##__VA_ARGS__); \
} while (0)
#define secdp_proc_info_mode(fmt, ...) \
do { \
printk(KERN_INFO "[msm-dp] %s: " pr_fmt(fmt), __func__, ##__VA_ARGS__); \
secdp_logger_print_mode(fmt, ##__VA_ARGS__); \
} while (0)
#define DP_INFO_M(fmt, ...) secdp_proc_info_mode(fmt, ##__VA_ARGS__)
#ifdef pr_debug
#undef pr_debug
#define pr_debug secdp_pr_debug
#endif
#ifdef pr_err
#undef pr_err
#define pr_err secdp_proc_err
#endif
#ifdef pr_info
#undef pr_info
#define pr_info secdp_proc_info
#endif
#ifdef pr_warn
#undef pr_warn
#define pr_warn secdp_proc_warn
#endif
#else/* !CONFIG_SECDP_LOGGER */
#define secdp_logger_set_max_count(x) do {} while (0)
#define secdp_logger_print(fmt, ...) do {} while (0)
#define secdp_logger_hex_dump(buf, pref, len) do {} while (0)
#define secdp_logger_init(void) do {} while (0)
#ifdef pr_debug
#undef pr_debug
#define pr_debug pr_info
#endif
#endif
#endif/*_SECDP_LOGGER_H_*/

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef _SECDP_LOGGER_EX_H_
#define _SECDP_LOGGER_EX_H_
/*
* if .c has its own pr_fmt, then use this instead of secdp_logger.h
*/
#ifdef CONFIG_SECDP_LOGGER
void secdp_logger_print(const char *fmt, ...);
#define secdp_proc_info_ex(fmt, ...) \
do { \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__); \
secdp_logger_print(fmt, ##__VA_ARGS__); \
} while (0)
#define secdp_pr_info_ex(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
#ifdef pr_debug
#undef pr_debug
#define pr_debug secdp_pr_info_ex
#endif
#ifdef pr_err
#undef pr_err
#define pr_err secdp_proc_info_ex
#endif
#ifdef pr_info
#undef pr_info
#define pr_info secdp_proc_info_ex
#endif
#ifdef pr_warn
#undef pr_warn
#define pr_warn secdp_proc_info_ex
#endif
#endif/*CONFIG_SECDP_LOGGER*/
#endif/*_SECDP_LOGGER_EX_H_*/

View File

@ -0,0 +1,268 @@
# SPDX-License-Identifier: GPL-2.0-only
KDIR := $(TOP)/kernel_platform/msm-kernel
ifeq ($(CONFIG_ARCH_WAIPIO), y)
ifeq ($(CONFIG_ARCH_QTI_VM), y)
include $(DISPLAY_ROOT)/config/gki_waipiodisptui.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_waipiodisptuiconf.h
else
include $(DISPLAY_ROOT)/config/gki_waipiodisp.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_waipiodispconf.h
endif
endif
ifeq ($(CONFIG_ARCH_NEO), y)
include $(DISPLAY_ROOT)/config/gki_neodisp.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_neodispconf.h
endif
ifeq ($(CONFIG_ARCH_PARROT), y)
include $(DISPLAY_ROOT)/config/gki_parrotdisp.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_parrotdispconf.h
endif
ifeq ($(CONFIG_ARCH_PINEAPPLE), y)
ifeq ($(CONFIG_ARCH_QTI_VM), y)
include $(DISPLAY_ROOT)/config/gki_pineappledisptui.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_pineappledisptuiconf.h
else
include $(DISPLAY_ROOT)/config/gki_pineappledisp.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_pineappledispconf.h
endif
endif
ifeq ($(CONFIG_ARCH_KALAMA), y)
ifeq ($(CONFIG_ARCH_QTI_VM), y)
include $(DISPLAY_ROOT)/config/gki_kalamadisptui.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_kalamadisptuiconf.h
else
include $(DISPLAY_ROOT)/config/gki_kalamadisp.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_kalamadispconf.h
endif
endif
ifeq (y, $(findstring y, $(CONFIG_ARCH_SA8155) $(CONFIG_ARCH_SA6155) $(CONFIG_ARCH_SA8195)))
include $(DISPLAY_ROOT)/config/augen3disp.conf
LINUX_INC += -include $(DISPLAY_ROOT)/config/augen3dispconf.h
endif
LINUX_INC += -I$(KERNEL_SRC)/include/linux \
-I$(KERNEL_SRC)/include/linux/drm
LINUX_INC += -I$(DISPLAY_ROOT) \
-I$(DISPLAY_ROOT)/include \
-I$(KERNEL_ROOT)/drivers/clk/qcom \
-I$(KERNEL_SRC)/drivers/clk/qcom \
-I$(DISPLAY_ROOT)/include/linux \
-I$(DISPLAY_ROOT)/rotator \
-I$(DISPLAY_ROOT)/msm \
-I$(DISPLAY_ROOT)/msm/dp \
-I$(DISPLAY_ROOT)/msm/dsi \
-I$(DISPLAY_ROOT)/msm/sde \
-I$(DISPLAY_ROOT)/include/uapi/display \
CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \
-DANI_LITTLE_BIT_ENDIAN \
-DDOT11F_LITTLE_ENDIAN_HOST \
-DANI_COMPILER_TYPE_GCC \
-DANI_OS_TYPE_ANDROID=6 \
-DPTT_SOCK_SVC_ENABLE \
-Wall\
-Werror\
-D__linux__
KBUILD_CPPFLAGS += $(CDEFINES)
ccflags-y += $(LINUX_INC)
# CONFIG_DISPLAY_SAMSUNG start
ccflags-y += -I$(DISPLAY_ROOT)/msm/samsung
# CONFIG_DISPLAY_SAMSUNG end
ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
EXTRA_CFLAGS += -Wmaybe-uninitialized
endif
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/display-drivers/hdcp/Module.symvers
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/display-drivers/msm/Module.symvers
ifeq ($(call cc-option-yn, -Wheader-guard),y)
EXTRA_CFLAGS += -Wheader-guard
endif
ccflags-y += -Wformat-extra-args -Wstrict-prototypes -Wformat-insufficient-args \
-Wformat-invalid-specifier -Wformat-zero-length -Wnonnull
ifneq ($(MODNAME), qdsp6v2)
CHIP_NAME ?= $(MODNAME)
CDEFINES += -DMULTI_IF_NAME=\"$(CHIP_NAME)\"
endif
######### CONFIG_DRM_MSM ########
obj-m += msm_drm.o
msm_drm-$(CONFIG_HDCP_QSEECOM) := ../hdcp/msm_hdcp.o \
dp/dp_hdcp2p2.o \
sde_hdcp_1x.o \
sde_hdcp_2x.o
msm_drm-$(CONFIG_MSM_SDE_ROTATOR) += ../rotator/sde_rotator_dev.o \
../rotator/sde_rotator_dev.o \
../rotator/sde_rotator_core.o \
../rotator/sde_rotator_base.o \
../rotator/sde_rotator_formats.o \
../rotator/sde_rotator_util.o \
../rotator/sde_rotator_io_util.o \
../rotator/sde_rotator_smmu.o \
../rotator/sde_rotator_r1_wb.o \
../rotator/sde_rotator_r1_pipe.o \
../rotator/sde_rotator_r1_ctl.o \
../rotator/sde_rotator_r1.o \
../rotator/sde_rotator_r3.o
ifeq ($(CONFIG_MSM_SDE_ROTATOR), y)
msm_drm-$(CONFIG_SYNC_FILE) += ../rotator/sde_rotator_sync.o
msm_drm-$(CONFIG_DEBUG_FS) += ../rotator/sde_rotator_debug.o \
../rotator/sde_rotator_r1_debug.o \
../rotator/sde_rotator_r3_debug.o
endif
msm_drm-$(CONFIG_DRM_SDE_VM) += sde/sde_vm_common.o \
sde/sde_vm_primary.o \
sde/sde_vm_trusted.o \
sde/sde_vm_msgq.o
msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \
dp/dp_parser.o \
dp/dp_power.o \
dp/dp_catalog.o \
dp/dp_catalog_v420.o \
dp/dp_catalog_v200.o \
dp/dp_aux.o \
dp/dp_panel.o \
dp/dp_link.o \
dp/dp_ctrl.o \
dp/dp_audio.o \
dp/dp_debug.o \
dp/dp_hpd.o \
dp/dp_aux_bridge.o \
dp/dp_bridge_hpd.o \
dp/dp_mst_sim.o \
dp/dp_mst_sim_helper.o \
dp/dp_gpio_hpd.o \
dp/dp_lphw_hpd.o \
dp/dp_display.o \
dp/dp_drm.o \
dp/dp_pll.o \
dp/dp_pll_5nm.o \
dp/dp_pll_4nm.o
msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o
msm_drm-$(CONFIG_DRM_MSM_DP_USBPD_LEGACY) += dp/dp_usbpd.o
# sec displayport
msm_drm-$(CONFIG_SECDP) += dp/secdp_sysfs.o \
dp/secdp_logger.o \
dp/secdp_unit_test.o
msm_drm-$(CONFIG_SECDP_BIGDATA) += dp/secdp_bigdata.o
msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
sde/sde_encoder.o \
sde/sde_encoder_dce.o \
sde/sde_encoder_phys_vid.o \
sde/sde_encoder_phys_cmd.o \
sde/sde_irq.o sde/sde_core_irq.o \
sde/sde_core_perf.o \
sde/sde_rm.o \
sde/sde_kms_utils.o \
sde/sde_kms.o \
sde/sde_plane.o \
sde/sde_connector.o \
sde/sde_color_processing.o \
sde/sde_vbif.o \
sde_dbg.o \
sde_dbg_evtlog.o \
sde_io_util.o \
sde_vm_event.o \
sde/sde_hw_reg_dma_v1_color_proc.o \
sde/sde_hw_color_proc_v4.o \
sde/sde_hw_ad4.o \
sde/sde_hw_uidle.o \
sde_edid_parser.o \
sde/sde_hw_catalog.o \
sde/sde_hw_cdm.o \
sde/sde_hw_dspp.o \
sde/sde_hw_intf.o \
sde/sde_hw_lm.o \
sde/sde_hw_ctl.o \
sde/sde_hw_util.o \
sde/sde_hw_sspp.o \
sde/sde_hw_wb.o \
sde/sde_hw_pingpong.o \
sde/sde_hw_top.o \
sde/sde_hw_interrupts.o \
sde/sde_hw_vbif.o \
sde/sde_formats.o \
sde_power_handle.o \
sde/sde_hw_color_processing_v1_7.o \
sde/sde_reg_dma.o \
sde/sde_hw_reg_dma_v1.o \
sde/sde_hw_dsc.o \
sde/sde_hw_dsc_1_2.o \
sde/sde_hw_vdc.o \
sde/sde_hw_ds.o \
sde/sde_fence.o \
sde/sde_hw_qdss.o \
sde_dsc_helper.o \
sde_vdc_helper.o \
sde/sde_hw_dnsc_blur.o \
sde/sde_hw_rc.o
msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
sde/sde_encoder_phys_wb.o
msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \
sde_rsc_hw.o \
sde_rsc_hw_v3.o
msm_drm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi_phy.o \
dsi/dsi_pwr.o \
dsi/dsi_phy.o \
dsi/dsi_phy_hw_v3_0.o \
dsi/dsi_phy_hw_v4_0.o \
dsi/dsi_phy_hw_v5_0.o \
dsi/dsi_phy_timing_calc.o \
dsi/dsi_phy_timing_v3_0.o \
dsi/dsi_phy_timing_v4_0.o \
dsi/dsi_pll.o \
dsi/dsi_pll_5nm.o \
dsi/dsi_pll_4nm.o \
dsi/dsi_ctrl_hw_cmn.o \
dsi/dsi_ctrl_hw_2_2.o \
dsi/dsi_ctrl.o \
dsi/dsi_catalog.o \
dsi/dsi_drm.o \
dsi/dsi_display.o \
dsi/dsi_panel.o \
dsi/dsi_clk_manager.o \
dsi/dsi_display_test.o
msm_drm-$(CONFIG_DSI_PARSER) += dsi/dsi_parser.o
msm_drm-$(CONFIG_THERMAL_OF) += msm_cooling_device.o
msm_drm-$(CONFIG_DRM_MSM) += msm_atomic.o \
msm_fb.o \
msm_drv.o \
msm_gem.o \
msm_gem_prime.o \
msm_gem_vma.o \
msm_smmu.o \
msm_prop.o
CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"

View File

@ -86,6 +86,10 @@ KBUILD_CPPFLAGS += $(CDEFINES)
ccflags-y += $(LINUX_INC)
# CONFIG_DISPLAY_SAMSUNG start
ccflags-y += -I$(DISPLAY_ROOT)/msm/samsung
# CONFIG_DISPLAY_SAMSUNG end
ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
EXTRA_CFLAGS += -Wmaybe-uninitialized
endif
@ -169,6 +173,13 @@ msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o
msm_drm-$(CONFIG_DRM_MSM_DP_USBPD_LEGACY) += dp/dp_usbpd.o
# sec displayport
msm_drm-$(CONFIG_SECDP) += dp/secdp_sysfs.o \
dp/secdp_logger.o \
dp/secdp_unit_test.o
msm_drm-$(CONFIG_SECDP_BIGDATA) += dp/secdp_bigdata.o
msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
sde/sde_encoder.o \
sde/sde_encoder_dce.o \
@ -263,3 +274,72 @@ msm_drm-$(CONFIG_DRM_MSM) += msm_atomic.o \
msm_smmu.o \
msm_prop.o
include $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01.conf
ccflags-y += -include $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01.h
XXD := /usr/bin/xxd
SED := /bin/sed
#Translate .dat file to .h to cover the case which can not use request_firmware(Recovery Mode)
CLEAR_TMP := $(shell rm -f E3_S6E3HAE_AMB681AZ01_PDF_DATA)
COPY_TO_HERE := $(shell cp -vf $(DISPLAY_ROOT)/msm/samsung/panel_data_file/E3_S6E3HAE_AMB681AZ01.dat E3_S6E3HAE_AMB681AZ01_PDF_DATA)
DATA_TO_HEX := $(shell $(XXD) -i E3_S6E3HAE_AMB681AZ01_PDF_DATA > $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01_PDF.h)
ADD_NULL_CHR := $(shell $(SED) -i -e 's/\([0-9a-f]\)$$/\0, 0x00/' $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01_PDF.h)
msm_drm-$(CONFIG_PANEL_E3_S6E3HAE_AMB681AZ01_WQHD) += samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01_panel.o
include $(DISPLAY_ROOT)/msm/samsung/panel_common.conf
ccflags-y += -include $(DISPLAY_ROOT)/msm/samsung/panel_common_conf.h
msm_drm-$(CONFIG_DISPLAY_SAMSUNG) += samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.o
include $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01.conf
ccflags-y += -include $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01.h
XXD := /usr/bin/xxd
SED := /bin/sed
#Translate .dat file to .h to cover the case which can not use request_firmware(Recovery Mode)
CLEAR_TMP := $(shell rm -f E3_S6E3HAF_AMB679FN01_PDF_DATA)
COPY_TO_HERE := $(shell cp -vf $(DISPLAY_ROOT)/msm/samsung/panel_data_file/E3_S6E3HAF_AMB679FN01.dat E3_S6E3HAF_AMB679FN01_PDF_DATA)
DATA_TO_HEX := $(shell $(XXD) -i E3_S6E3HAF_AMB679FN01_PDF_DATA > $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01_PDF.h)
ADD_NULL_CHR := $(shell $(SED) -i -e 's/\([0-9a-f]\)$$/\0, 0x00/' $(DISPLAY_ROOT)/msm/samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01_PDF.h)
msm_drm-$(CONFIG_PANEL_E3_S6E3HAF_AMB679FN01_WQHD) += samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01_panel.o
include $(DISPLAY_ROOT)/msm/samsung/panel_common.conf
ccflags-y += -include $(DISPLAY_ROOT)/msm/samsung/panel_common_conf.h
ifeq ($(TARGET_BUILD_VARIANT), eng)
ccflags-y += -DCONFIG_OPCODE_PARSER=1
endif
msm_drm-$(CONFIG_DISPLAY_SAMSUNG) += samsung/ss_dsi_panel_sysfs.o \
samsung/ss_dsi_panel_debug.o \
samsung/ss_dsi_panel_common.o \
samsung/ss_dsi_mdnie_lite_common.o \
samsung/ss_dpui_common.o \
samsung/ss_copr_common.o \
samsung/ss_wrapper_common.o \
samsung/ss_panel_parse.o \
samsung/ss_panel_power.o \
samsung/ss_ddi_poc_common.o
ifeq ($(CONFIG_SEC_KUNIT), y)
ifeq ($(CONFIG_UML), y)
obj-y += samsung/kunit_test/ss_dsi_panel_common_test.o
endif
endif
# LEGO
subdir-ccflags-$(CONFIG_SEC_KUNIT) += \
-Wno-unused-variable \
-Wno-unused-function \
-Wno-missing-braces \
-Wno-format
msm_drm-$(CONFIG_DISPLAY_SAMSUNG) += samsung/SELF_DISPLAY/self_display.o
msm_drm-$(CONFIG_DISPLAY_SAMSUNG) += samsung/MAFPC/ss_dsi_mafpc.o

View File

@ -0,0 +1,38 @@
config SEC_DISPLAYPORT
bool "SEC DISPLAYPORT feature"
default n
help
Samsung specific displayport changes.
config SEC_DISPLAYPORT_MST
bool "SEC DISPLAYPORT MST feature"
depends on SEC_DISPLAYPORT
default n
help
Samsung specific displayport MST changes.
config SEC_DISPLAYPORT_BIGDATA
bool "DISPLAYPORT bigdata"
depends on !SEC_FACTORY
default n
help
Enable DISPLAYPORT bigdata.
config SEC_DISPLAYPORT_LOGGER
bool "SEC DISPLAYPORT LOGGER feature"
depends on SEC_DISPLAYPORT
help
Samsung specific displayport log changes.
config SEC_DISPLAYPORT_ENG
bool "SEC DISPLAYPORT ENG feature"
depends on SEC_DISPLAYPORT
help
Samsung specific eng test code for displayport.
config SEC_DISPLAYPORT_AUX_CONTROL
bool "SEC DISPLAYPORT Aux Control feature"
depends on SEC_DISPLAYPORT
default n
help
Support for AUX dongle control.

View File

@ -41,6 +41,7 @@ enum dp_altmode_pin_assignment {
DPAM_HPD_F,
};
#if !defined(CONFIG_SECDP)
static int dp_altmode_set_usb_dp_mode(struct dp_altmode_private *altmode)
{
int rc = 0;
@ -226,6 +227,7 @@ static void dp_altmode_register(void *priv)
else
DP_DEBUG("success\n");
}
#endif
static int dp_altmode_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
{
@ -271,7 +273,9 @@ static int dp_altmode_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb)
{
#if !defined(CONFIG_SECDP)
int rc = 0;
#endif
struct dp_altmode_private *altmode;
struct dp_altmode *dp_altmode;
@ -292,18 +296,22 @@ struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb)
dp_altmode->base.simulate_connect = dp_altmode_simulate_connect;
dp_altmode->base.simulate_attention = dp_altmode_simulate_attention;
#if !defined(CONFIG_SECDP)
rc = altmode_register_notifier(dev, dp_altmode_register, altmode);
if (rc < 0) {
DP_ERR("altmode probe notifier registration failed: %d\n", rc);
goto error;
}
#endif
DP_DEBUG("success\n");
return &dp_altmode->base;
#if !defined(CONFIG_SECDP)
error:
kfree(altmode);
return ERR_PTR(rc);
#endif
}
void dp_altmode_put(struct dp_hpd *dp_hpd)
@ -318,8 +326,10 @@ void dp_altmode_put(struct dp_hpd *dp_hpd)
altmode = container_of(dp_altmode, struct dp_altmode_private,
dp_altmode);
#if !defined(CONFIG_SECDP)
altmode_deregister_client(altmode->amclient);
altmode_deregister_notifier(altmode->dev, altmode);
#endif
kfree(altmode);
}

View File

@ -19,6 +19,20 @@
#include "dp_panel.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#if defined(CONFIG_SECDP_SWITCH)
#include <linux/switch.h>
static struct switch_dev switch_secdp_audio = {
.name = "ch_hdmi_audio",
};
#endif
#endif/*CONFIG_SECDP*/
struct dp_audio_private {
struct platform_device *ext_pdev;
struct platform_device *pdev;
@ -410,6 +424,11 @@ static int dp_audio_info_setup(struct platform_device *pdev,
return 0;
}
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_save_item(BD_AUD_CH, params->num_of_channels);
secdp_bigdata_save_item(BD_AUD_FREQ, params->sample_rate_hz);
#endif
mutex_lock(&audio->ops_lock);
audio->channels = params->num_of_channels;
@ -433,6 +452,38 @@ static int dp_audio_info_setup(struct platform_device *pdev,
return rc;
}
#if defined(CONFIG_SECDP)
static void secdp_audio_use_one_sampling_freq(u8 *adb, int size)
{
const int one_adb_size = 3;
int adb_count;
if (size <= 0)
return;
adb_count = size / one_adb_size;
while (adb_count > 0) {
if (adb[1] & BIT(2))
adb[1] = BIT(2); /* 48kHz */
else if (adb[1] & BIT(1))
adb[1] = BIT(1); /* 44.1kHz */
else if (adb[1] & BIT(0))
adb[1] = BIT(0); /* 32kHz */
else if (adb[1] & BIT(3))
adb[1] = BIT(3); /* 88kHz */
else if (adb[1] & BIT(4))
adb[1] = BIT(4); /* 96kHz */
else if (adb[1] & BIT(5))
adb[1] = BIT(5); /* 176kHz */
else if (adb[1] & BIT(6))
adb[1] = BIT(6); /* 192kHz */
adb += one_adb_size;
adb_count--;
}
}
#endif
static int dp_audio_get_edid_blk(struct platform_device *pdev,
struct msm_ext_disp_audio_edid_blk *blk)
{
@ -459,8 +510,19 @@ static int dp_audio_get_edid_blk(struct platform_device *pdev,
edid = audio->panel->edid_ctrl;
#if defined(CONFIG_SECDP)
if (secdp_adapter_is_legacy())
secdp_audio_use_one_sampling_freq(edid->audio_data_block, edid->adb_size);
#endif
blk->audio_data_blk = edid->audio_data_block;
blk->audio_data_blk_size = edid->adb_size;
#if defined(CONFIG_SECDP)
print_hex_dump(KERN_DEBUG, "AUDIO_BLK: ",
DUMP_PREFIX_NONE, 16, 1, blk->audio_data_blk,
blk->audio_data_blk_size, false);
secdp_logger_hex_dump(blk->audio_data_blk, "AUDIO_BLK:",
blk->audio_data_blk_size);
#endif
blk->spk_alloc_data_blk = edid->spkr_alloc_data_block;
blk->spk_alloc_data_blk_size = edid->sadb_size;
@ -677,6 +739,10 @@ end:
return rc;
}
#if defined(CONFIG_SECDP_SWITCH)
extern int secdp_get_audio_ch(void);
#endif
static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
{
int rc = 0;
@ -695,6 +761,17 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
if (rc)
goto end;
#if defined(CONFIG_SECDP_SWITCH)
{
if (!audio->dp_audio.has_mst) {
int audio_ch = state ? secdp_get_audio_ch() : -1;
switch_set_state(&switch_secdp_audio, audio_ch);
DP_INFO("secdp audio state:0x%x\n", audio_ch);
}
}
#endif
if (atomic_read(&audio->acked))
goto end;
@ -752,6 +829,13 @@ static int dp_audio_on(struct dp_audio *dp_audio)
return -EINVAL;
}
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status()) {
DP_INFO("cable is out\n");
return -EINVAL;
}
#endif
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
if (IS_ERR(audio)) {
DP_ERR("invalid input\n");
@ -798,6 +882,13 @@ static int dp_audio_off(struct dp_audio *dp_audio, bool skip_wait)
ext = &audio->ext_audio_data;
#if defined(CONFIG_SECDP)
if (!atomic_read(&audio->session_on)) {
DP_INFO("dp audio already off\n");
return rc;
}
#endif
work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work);
if (work_pending)
DP_DEBUG("pending notification work completed\n");
@ -830,6 +921,43 @@ static void dp_audio_notify_work_fn(struct work_struct *work)
dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
}
#if defined(CONFIG_SECDP_SWITCH)
int secdp_audio_register_switch(struct dp_audio *dp_audio)
{
int rc = 0;
if (dp_audio->has_mst) {
DP_INFO("skip switch register\n");
rc = -ENODEV;
goto end;
}
rc = switch_dev_register(&switch_secdp_audio);
if (rc) {
DP_INFO("Failed to register secdp_audio switch %d\n", rc);
rc = -ENODEV;
goto end;
}
DP_INFO("secdp_audio register success\n");
end:
return rc;
}
static void secdp_audio_unregister_switch(struct dp_audio_private *audio)
{
struct dp_audio *dp_audio = &audio->dp_audio;
if (dp_audio->has_mst) {
DP_DEBUG("skip switch unregister\n");
return;
}
switch_dev_unregister(&switch_secdp_audio);
DP_INFO("secdp_audio unregister success\n");
}
#endif
static int dp_audio_create_notify_workqueue(struct dp_audio_private *audio)
{
audio->notify_workqueue = create_workqueue("sdm_dp_audio_notify");
@ -847,6 +975,10 @@ static void dp_audio_destroy_notify_workqueue(struct dp_audio_private *audio)
{
if (audio->notify_workqueue)
destroy_workqueue(audio->notify_workqueue);
#if defined(CONFIG_SECDP_SWITCH)
secdp_audio_unregister_switch(audio);
#endif
}
struct dp_audio *dp_audio_get(struct platform_device *pdev,

View File

@ -22,6 +22,9 @@ struct dp_audio {
u32 lane_count;
u32 bw_code;
bool tui_active;
#if defined(CONFIG_SECDP_SWITCH)
bool has_mst;
#endif
/**
* on()
@ -73,4 +76,9 @@ struct dp_audio *dp_audio_get(struct platform_device *pdev,
* @dp_audio: an instance of dp_audio.
*/
void dp_audio_put(struct dp_audio *dp_audio);
#if defined(CONFIG_SECDP_SWITCH)
int secdp_audio_register_switch(struct dp_audio *dp_audio);
#endif
#endif /* _DP_AUDIO_H_ */

View File

@ -16,6 +16,12 @@
#include "dp_aux.h"
#include "dp_hpd.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#include "secdp.h"
#endif
#define DP_AUX_ENUM_STR(x) #x
#define DP_AUX_IPC_NUM_PAGES 10
@ -69,7 +75,11 @@ struct dp_aux_private {
struct dp_aux dp_aux;
struct dp_catalog_aux *catalog;
struct dp_aux_cfg *cfg;
#if !defined(CONFIG_SECDP)
struct device_node *aux_switch_node;
#else
struct secdp_misc *sec;
#endif
struct mutex mutex;
struct completion comp;
struct drm_dp_aux drm_aux;
@ -104,9 +114,11 @@ static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
int i, linelen, remaining = msg->size;
const int rowsize = 16;
u8 linebuf[64];
#if !defined(CONFIG_SECDP)
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
struct dp_aux *dp_aux = &aux->dp_aux;
#endif
snprintf(prefix, sizeof(prefix), "%s %s %4xh(%2zu): ",
(msg->request & DP_AUX_I2C_MOT) ? "I2C" : "NAT",
@ -120,10 +132,12 @@ static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
hex_dump_to_buffer(msg->buffer + i, linelen, rowsize, 1,
linebuf, sizeof(linebuf), false);
#if !defined(CONFIG_SECDP)
if (msg->size == 1 && msg->address == 0)
DP_DEBUG_V("%s%s\n", prefix, linebuf);
else
DP_AUX_DEBUG(dp_aux, "%s%s\n", prefix, linebuf);
#endif
}
}
@ -209,6 +223,21 @@ static u32 dp_aux_write(struct dp_aux_private *aux,
return len;
}
#if defined(CONFIG_SECDP)
#define DDC_SEGMENT_ADDR 0x30
static bool secdp_check_seg_addr(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
if (msg->address == DDC_SEGMENT_ADDR &&
!(msg->request & DP_AUX_I2C_READ) &&
msg->size == 1)
return true;
return false;
}
#endif
static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
@ -239,6 +268,15 @@ static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
ret = len;
} else {
#if defined(CONFIG_SECDP)
if (secdp_check_seg_addr(aux, msg)) {
DP_AUX_ERR(dp_aux, "ignore %s during [%s]\n",
dp_aux_get_error(aux->aux_error_num), prefix);
aux->aux_error_num = DP_AUX_ERR_NONE;
return msg->size;
}
#endif
DP_AUX_WARN_RATELIMITED(dp_aux, "aux err [%s] during [%s]\n",
dp_aux_get_error(aux->aux_error_num), prefix);
ret = -EINVAL;
@ -298,6 +336,13 @@ static void dp_aux_native_handler(struct dp_aux_private *aux)
aux->catalog->clear_hw_interrupts(aux->catalog);
}
#if defined(CONFIG_SECDP_BIGDATA)
if (aux->aux_error_num == DP_AUX_ERR_NONE)
secdp_bigdata_clr_error_cnt(ERR_AUX);
else
secdp_bigdata_inc_error_cnt(ERR_AUX);
#endif
complete(&aux->comp);
}
@ -327,6 +372,13 @@ static void dp_aux_i2c_handler(struct dp_aux_private *aux)
}
}
#if defined(CONFIG_SECDP_BIGDATA)
if (aux->aux_error_num == DP_AUX_ERR_NONE)
secdp_bigdata_clr_error_cnt(ERR_AUX);
else
secdp_bigdata_inc_error_cnt(ERR_AUX);
#endif
complete(&aux->comp);
}
@ -558,6 +610,20 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
ret = dp_aux_cmd_fifo_tx(aux, msg);
if ((ret < 0) && !atomic_read(&aux->aborted)) {
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status() || !secdp_get_hpd_status()) {
DP_INFO("hpd_low or cable_lost %d\n", ret);
/*
* don't need to repeat aux.
* exit loop in drm_dp_dpcd_access()
*/
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
ret = msg->size;
aux->retry_cnt = 0;
goto unlock_exit;
}
#endif
aux->retry_cnt++;
if (!(aux->retry_cnt % retry_count))
aux->catalog->update_aux_cfg(aux->catalog,
@ -876,10 +942,15 @@ end:
}
#endif
#if !defined(CONFIG_SECDP)
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
enum dp_aux_switch_type switch_type)
struct dp_aux_bridge *aux_bridge, void *ipc_log_context)
#else
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context, void *sec)
#endif
{
int rc = 0;
struct dp_aux_private *aux;
@ -904,7 +975,11 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
aux->dev = dev;
aux->catalog = catalog;
aux->cfg = parser->aux_cfg;
#if !defined(CONFIG_SECDP)
aux->aux_switch_node = aux_switch;
#else
aux->sec = (struct secdp_misc *)sec;
#endif
aux->aux_bridge = aux_bridge;
dp_aux = &aux->dp_aux;
aux->retry_cnt = 0;
@ -920,24 +995,6 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
dp_aux->set_sim_mode = dp_aux_set_sim_mode;
dp_aux->ipc_log_context = ipc_log_context;
/*Condition to avoid allocating function pointers for aux bypass mode*/
if (switch_type != DP_AUX_SWITCH_BYPASS) {
#if IS_ENABLED(CONFIG_QCOM_FSA4480_I2C)
if (switch_type == DP_AUX_SWITCH_FSA4480) {
dp_aux->switch_configure = dp_aux_configure_fsa_switch;
dp_aux->switch_register_notifier = fsa4480_reg_notifier;
dp_aux->switch_unregister_notifier = fsa4480_unreg_notifier;
}
#endif
#if IS_ENABLED(CONFIG_QCOM_WCD939X_I2C)
if (switch_type == DP_AUX_SWITCH_WCD939x) {
dp_aux->switch_configure = dp_aux_configure_wcd_switch;
dp_aux->switch_register_notifier = wcd_usbss_reg_notifier;
dp_aux->switch_unregister_notifier = wcd_usbss_unreg_notifier;
}
#endif
}
return dp_aux;
error:
return ERR_PTR(rc);

View File

@ -68,10 +68,15 @@ struct dp_aux {
int (*switch_unregister_notifier)(struct notifier_block *nb, struct device_node *node);
};
#if !defined(CONFIG_SECDP)
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
enum dp_aux_switch_type switch_type);
struct dp_aux_bridge *aux_bridge, void *ipc_log_context);
#else
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context, void *sec);
#endif
void dp_aux_put(struct dp_aux *aux);
#endif /*__DP_AUX_H_*/

View File

@ -12,6 +12,9 @@
#include "dp_reg.h"
#include "dp_debug.h"
#include "dp_link.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DP_GET_MSB(x) (x >> 8)
#define DP_GET_LSB(x) (x & 0xff)
@ -166,6 +169,11 @@ static u32 dp_read_hw(struct dp_catalog_private *catalog,
{
u32 data = 0;
#if defined(CONFIG_SECDP)
if (secdp_phy_reset_check())
return 0;
#endif
data = readl_relaxed(io_data->io.base + offset);
return data;
@ -174,6 +182,11 @@ static u32 dp_read_hw(struct dp_catalog_private *catalog,
static void dp_write_hw(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset, u32 data)
{
#if defined(CONFIG_SECDP)
if (secdp_phy_reset_check())
return;
#endif
writel_relaxed(data, io_data->io.base + offset);
}
@ -290,7 +303,16 @@ static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
if (read) {
data = dp_read(DP_AUX_TRANS_CTRL);
#if defined(CONFIG_SECDP)
/* Prevent_CXX Major defect - Invalid Assignment: The type size
* of both side variables are different:
* "data" is 4 ( unsigned int ) and "data & 0xfffffffffffffdffUL
* " is 8 ( unsigned long )
*/
data &= ((u32)~BIT(9));
#else
data &= ~BIT(9);
#endif
dp_write(DP_AUX_TRANS_CTRL, data);
} else {
dp_write(DP_AUX_TRANS_CTRL, 0);
@ -314,6 +336,10 @@ static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux)
io_data = catalog->io.dp_phy;
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS);
#if defined(CONFIG_SECDP)
if (data)
DP_DEBUG("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data);
#endif
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
@ -390,6 +416,13 @@ static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux,
return;
}
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status()) {
DP_INFO("cable is out\n");
return;
}
#endif
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_phy;
@ -1682,7 +1715,9 @@ static void dp_catalog_panel_dp_flush(struct dp_catalog_panel *panel,
static void dp_catalog_panel_pps_flush(struct dp_catalog_panel *panel)
{
dp_catalog_panel_dp_flush(panel, DP_PPS_FLUSH);
#if !defined(CONFIG_SECDP)
DP_DEBUG("pps flush for stream:%d\n", panel->stream_id);
#endif
}
static void dp_catalog_panel_dhdr_flush(struct dp_catalog_panel *panel)
@ -1956,6 +1991,22 @@ static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl,
value1 = vm_pre_emphasis[v_level][p_level];
}
#if defined(SECDP_SELF_TEST)
if (secdp_self_test_status(ST_VOLTAGE_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_VOLTAGE_TUN)[v_level*4 + p_level];
DP_INFO("[vx] value0: %02x => %02x\n", value0, val);
value0 = val;
}
if (secdp_self_test_status(ST_PREEM_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_PREEM_TUN)[v_level*4 + p_level];
DP_INFO("[px] value0: %02x => %02x\n", value1, val);
value1 = val;
}
#endif
/* program default setting first */
io_data = catalog->io.dp_ln_tx0;
@ -2956,6 +3007,10 @@ static int dp_catalog_init(struct device *dev, struct dp_catalog *dp_catalog,
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
#if defined(CONFIG_SECDP)
dp_catalog->parser = parser;
#endif
if (parser->hw_cfg.phy_version >= DP_PHY_VERSION_4_2_0)
dp_catalog->sub = dp_catalog_get_v420(dev, dp_catalog, &catalog->io);
else if (parser->hw_cfg.phy_version == DP_PHY_VERSION_2_0_0)

View File

@ -292,6 +292,10 @@ struct dp_catalog {
struct dp_catalog_sub *sub;
#if defined(CONFIG_SECDP)
struct dp_parser *parser;
#endif
void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
int (*get_reg_dump)(struct dp_catalog *dp_catalog,
char *mode, u8 **out_buf, u32 *out_buf_len);
@ -373,4 +377,28 @@ struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
u32 dp_catalog_get_dp_core_version(struct dp_catalog *dp_catalog);
u32 dp_catalog_get_dp_phy_version(struct dp_catalog *dp_catalog);
#if defined(CONFIG_SECDP_DBG)
enum secdp_hw_preshoot_t {
DP_HW_PRESHOOT_0,
DP_HW_PRESHOOT_1,
DP_HW_PRESHOOT_MAX,
};
static inline char *secdp_preshoot_to_string(int hw)
{
switch (hw) {
case DP_HW_PRESHOOT_0:
return DP_ENUM_STR(DP_HW_PRESHOOT_0);
case DP_HW_PRESHOOT_1:
return DP_ENUM_STR(DP_HW_PRESHOOT_1);
default:
return "unknown";
}
}
int secdp_catalog_preshoot_show(struct dp_catalog *catalog, char *buf);
void secdp_catalog_preshoot_store(struct dp_catalog *catalog, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif /* _DP_CATALOG_H_ */

View File

@ -12,6 +12,10 @@
#include <linux/rational.h>
#include <drm/drm_fixed.h>
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define dp_catalog_get_priv_v420(x) ({ \
struct dp_catalog *catalog; \
catalog = container_of(x, struct dp_catalog, x); \
@ -50,8 +54,95 @@ struct dp_catalog_private_v420 {
struct dp_catalog_sub sub;
struct dp_catalog_io *io;
struct dp_catalog *dpc;
#if defined(CONFIG_SECDP_DBG)
char preshoot[DP_HW_PRESHOOT_MAX];
#endif
};
#if defined(CONFIG_SECDP_DBG)
int secdp_catalog_preshoot_show(struct dp_catalog *catalog, char *buf)
{
struct dp_catalog_private_v420 *catalog_priv;
int rc = 0;
catalog_priv = container_of(catalog->sub,
struct dp_catalog_private_v420, sub);
rc += scnprintf(buf + rc, PAGE_SIZE - rc,
"%02x %02x\n",
catalog_priv->preshoot[DP_HW_PRESHOOT_0],
catalog_priv->preshoot[DP_HW_PRESHOOT_1]);
return rc;
}
void secdp_catalog_preshoot_store(struct dp_catalog *catalog, char *buf)
{
struct dp_catalog_private_v420 *catalog_priv;
char *tok;
u32 value;
int i, rc = 0;
catalog_priv = container_of(catalog->sub,
struct dp_catalog_private_v420, sub);
for (i = 0; i < DP_HW_PRESHOOT_MAX; i++) {
tok = strsep(&buf, ",");
if (!tok)
continue;
rc = kstrtouint(tok, 16, &value);
if (rc) {
DP_ERR("error: %s rc:%d\n", tok, rc);
goto end;
}
catalog_priv->preshoot[i] = value;
}
end:
return;
}
static void _secdp_catalog_preshoot_init(struct dp_catalog_private_v420 *catalog)
{
int i;
for (i = 0; i < DP_HW_PRESHOOT_MAX; i++)
catalog->preshoot[i] = 0xff;
}
static void _secdp_catalog_preshoot_adjust(
struct dp_catalog_private_v420 *catalog)
{
struct dp_io_data *io_data;
int i;
for (i = 0; i < DP_HW_PRESHOOT_MAX; i++) {
if (catalog->preshoot[i] != 0xff) {
if (i == DP_HW_PRESHOOT_0)
io_data = catalog->io->dp_ln_tx0;
else if (i == DP_HW_PRESHOOT_1)
io_data = catalog->io->dp_ln_tx1;
else
DP_ERR("cannot be here\n");
catalog->preshoot[i] |= BIT(5);
/*
* USB3_DP_PHY_DP_QSERDES_TX0_PRE_EMPH
* USB3_DP_PHY_DP_QSERDES_TX1_PRE_EMPH
*/
dp_write(0x108, catalog->preshoot[i]);
DP_INFO("%s 0x%02x write done!\n",
secdp_preshoot_to_string(i),
catalog->preshoot[i]);
}
}
}
#endif/*CONFIG_SECDP_DBG*/
static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg)
{
@ -248,6 +339,22 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
value1 = vm_pre_emphasis[v_level][p_level];
}
#if defined(SECDP_SELF_TEST)
if (secdp_self_test_status(ST_VOLTAGE_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_VOLTAGE_TUN)[v_level*4 + p_level];
DP_INFO("[vx] value0: 0x%02x => 0x%02x\n", value0, val);
value0 = val;
}
if (secdp_self_test_status(ST_PREEM_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_PREEM_TUN)[v_level*4 + p_level];
DP_INFO("[px] value0: 0x%02x => 0x%02x\n", value1, val);
value1 = val;
}
#endif
/* program default setting first */
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL_V420, 0x2A);
@ -277,6 +384,10 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
v_level, value0, p_level, value1);
}
#if defined(CONFIG_SECDP_DBG)
_secdp_catalog_preshoot_adjust(catalog);
#endif
}
static void dp_catalog_ctrl_lane_pnswap_v420(struct dp_catalog_ctrl *ctrl,
@ -335,6 +446,10 @@ struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
catalog_priv->sub.put = dp_catalog_put_v420;
#if defined(CONFIG_SECDP_DBG)
_secdp_catalog_preshoot_init(catalog_priv);
#endif
catalog->aux.setup = dp_catalog_aux_setup_v420;
catalog->aux.clear_hw_interrupts = dp_catalog_aux_clear_hw_int_v420;
catalog->panel.config_msa = dp_catalog_panel_config_msa_v420;

View File

@ -13,6 +13,12 @@
#include "dp_ctrl.h"
#include "dp_debug.h"
#include "sde_dbg.h"
#if defined(CONFIG_SECDP)
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#include "secdp.h"
#endif
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
@ -65,6 +71,10 @@ struct dp_ctrl_private {
struct dp_parser *parser;
struct dp_catalog_ctrl *catalog;
struct dp_pll *pll;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
bool link_train_status;
#endif
struct completion idle_comp;
struct completion video_comp;
@ -243,6 +253,11 @@ static void dp_ctrl_update_hw_vx_px(struct dp_ctrl_private *ctrl)
ctrl->link->link_params.bw_code == DP_LINK_BW_8_1)
high = true;
#if defined(CONFIG_SECDP)
secdp_redriver_linkinfo(ctrl->power, link->link_params.bw_code,
link->phy_params.v_level, link->phy_params.p_level);
#endif
ctrl->catalog->update_vx_px(ctrl->catalog,
link->phy_params.v_level, link->phy_params.p_level, high);
}
@ -457,6 +472,11 @@ static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
DP_DEBUG("new bw code=0x%x\n", ctrl->link->link_params.bw_code);
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_save_item(BD_CUR_LINK_RATE,
ctrl->link->link_params.bw_code);
#endif
return ret;
}
@ -561,9 +581,26 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
u8 const encoding = 0x1, downspread = 0x00;
struct drm_dp_link link_info = {0};
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status()) {
DP_INFO("cable is out\n");
return -EIO;
}
DP_ENTER("\n");
ctrl->link_train_status = false;
#endif
ctrl->link->phy_params.p_level = 0;
ctrl->link->phy_params.v_level = 0;
#if defined(CONFIG_SECDP)
if (secdp_check_hmd_dev(ctrl->sec, "PicoVR")) {
DP_INFO("pico REAL Plus!\n");
ctrl->link->phy_params.v_level = 2; /*800mV*/
}
#endif
link_info.num_lanes = ctrl->link->link_params.lane_count;
link_info.rate = drm_dp_bw_code_to_link_rate(
ctrl->link->link_params.bw_code);
@ -609,11 +646,22 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
DP_INFO("link training #2 successful\n");
end:
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status()) {
DP_INFO("cable is out <2>\n");
return -EIO;
}
#endif
dp_ctrl_state_ctrl(ctrl, 0);
/* Make sure to clear the current pattern before starting a new one */
wmb();
dp_ctrl_clear_training_pattern(ctrl);
#if defined(CONFIG_SECDP)
if (!ret)
ctrl->link_train_status = true;
#endif
return ret;
}
@ -636,7 +684,10 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
0x01);
ret = dp_ctrl_link_train(ctrl);
#if defined(CONFIG_SECDP_BIGDATA)
if (ret)
secdp_bigdata_inc_error_cnt(ERR_LINK_TRAIN);
#endif
end:
return ret;
}
@ -722,6 +773,13 @@ static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl,
else
pattern = DP_TRAINING_PATTERN_2;
#ifdef SECDP_MAX_HBR2
if (pattern == DP_TRAINING_PATTERN_4) {
DP_INFO("TPS4 to TPS3\n");
downgrade = true;
}
#endif
if (!downgrade)
goto end;
@ -754,6 +812,13 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
link_params->lane_count);
while (1) {
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status()) {
DP_INFO("cable is out\n");
rc = -EIO;
break;
}
#endif
DP_DEBUG("bw_code=%d, lane_count=%d\n",
link_params->bw_code, link_params->lane_count);
@ -793,6 +858,14 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
break;
}
#if defined(CONFIG_SECDP) && !defined(SECDP_AUDIO_CTS)
if ((ctrl->link->link_params.bw_code == DP_LINK_BW_1_62 && downgrade) ||
!secdp_get_cable_status()) {
rc = -EIO;
break;
}
#endif
if (!link_train_max_retries || atomic_read(&ctrl->aborted)) {
dp_ctrl_disable_link_clock(ctrl);
break;
@ -1384,6 +1457,16 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
ctrl->stream_count--;
}
#if defined(CONFIG_SECDP)
bool secdp_get_link_train_status(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
return ctrl->link_train_status;
}
#endif/*CONFIG_SECDP*/
static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
bool fec_mode, bool dsc_mode, bool shallow)
{
@ -1417,6 +1500,10 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
DP_DEBUG("using phy test link parameters\n");
} else {
#ifdef SECDP_OPTIMAL_LINK_RATE
if (!ctrl->panel->tbox)
rate = secdp_gen_link_clk(ctrl->panel);
#endif
ctrl->link->link_params.bw_code =
drm_dp_link_rate_to_bw_code(rate);
ctrl->link->link_params.lane_count =
@ -1575,6 +1662,9 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
ctrl->aux = in->aux;
ctrl->link = in->link;
ctrl->catalog = in->catalog;
#if defined(CONFIG_SECDP)
ctrl->sec = in->sec;
#endif
ctrl->pll = in->pll;
ctrl->dev = in->dev;
ctrl->mst_mode = false;

View File

@ -46,9 +46,16 @@ struct dp_ctrl_in {
struct dp_power *power;
struct dp_catalog_ctrl *catalog;
struct dp_pll *pll;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
#endif
};
struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
#if defined(CONFIG_SECDP)
bool secdp_get_link_train_status(struct dp_ctrl *dp_ctrl);
#endif
#endif /* _DP_CTRL_H_ */

View File

@ -25,6 +25,9 @@
#include "dp_hpd.h"
#include "dp_mst_sim.h"
#include "dp_mst_drm.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DEBUG_NAME "drm_dp"
@ -161,6 +164,16 @@ static ssize_t dp_debug_write_edid(struct file *file,
if (copy_from_user(buf, user_buff, size))
goto bail;
#if defined(CONFIG_SECDP)
if (!strncmp(buf, "reset", 5)) {
DP_DEBUG("disable SIM_MODE_EDID\n");
dp_debug_disable_sim_mode(debug, DP_SIM_MODE_EDID);
kfree(buf);
mutex_unlock(&debug->lock);
return rc;
}
#endif
edid_size = size / char_to_nib;
buf_t = buf;
size = edid_size;

View File

@ -45,6 +45,7 @@
DP_ERR_V(fmt, ##__VA_ARGS__); \
} while (0)
#if !defined(CONFIG_SECDP)
#define DP_DEBUG_V(fmt, ...) \
do { \
if (drm_debug_enabled(DRM_UT_KMS)) \
@ -80,6 +81,47 @@
#define DP_ERR_RATELIMITED_V(fmt, ...) \
pr_err_ratelimited("[drm:%s][msm-dp-err][%-4d]"fmt, __func__, \
current->pid, ##__VA_ARGS__)
#else
#define DP_DEBUG_V(fmt, ...) \
do { \
if (drm_debug_enabled(DRM_UT_KMS)) \
DRM_DEBUG("[msm-dp-debug][%-4d]"fmt, current->pid, \
##__VA_ARGS__); \
else \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
#define DP_INFO_V(fmt, ...) \
do { \
if (drm_debug_enabled(DRM_UT_KMS)) \
DRM_INFO("[msm-dp-info][%-4d]"fmt, current->pid, \
##__VA_ARGS__); \
else \
pr_info(fmt, ##__VA_ARGS__); \
} while (0)
#define DP_WARN_V(fmt, ...) pr_warn(fmt, ##__VA_ARGS__)
#define DP_WARN_RATELIMITED_V(fmt, ...) pr_warn(fmt, ##__VA_ARGS__)
#define DP_ERR_V(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
#define DP_ERR_RATELIMITED_V(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
#endif
#if defined(CONFIG_SECDP_DBG)
extern bool secdp_func_trace;
#define DP_ENTER(fmt, ...) \
do { \
if (secdp_func_trace) \
pr_debug("+++ " pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define DP_LEAVE(fmt, ...) \
do { \
if (secdp_func_trace) \
pr_debug("--- " pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#else
#define DP_ENTER(fmt, ...) do {} while (0)
#define DP_LEAVE(fmt, ...) do {} while (0)
#endif
#define DEFAULT_DISCONNECT_DELAY_MS 0
#define MAX_DISCONNECT_DELAY_MS 10000

File diff suppressed because it is too large Load Diff

View File

@ -141,4 +141,11 @@ static inline int dp_display_mmrm_callback(struct mmrm_client_notifier_data *not
return 0;
}
#endif /* CONFIG_DRM_MSM_DP */
#if defined(CONFIG_SECDP_DBG)
int secdp_debug_set_ssc(struct secdp_misc *sec, bool onoff);
bool secdp_debug_get_ssc(struct secdp_misc *sec);
int secdp_show_hmd_dev(struct secdp_misc *sec, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif /* _DP_DISPLAY_H_ */

View File

@ -14,6 +14,9 @@
#include "dp_drm.h"
#include "dp_mst_drm.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)

View File

@ -15,6 +15,9 @@
#include <linux/of_gpio.h>
#include "dp_gpio_hpd.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
struct dp_gpio_hpd_private {
struct device *dev;

View File

@ -22,6 +22,10 @@
#include "sde_hdcp_2x.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP_DBG)
#include <linux/secdp_logger.h>
#endif
#define DP_INTR_STATUS2 (0x00000024)
#define DP_INTR_STATUS3 (0x00000028)
#define dp_read(offset) readl_relaxed((offset))
@ -655,6 +659,10 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
}
cp_irq = buf & BIT(2);
#ifdef SECDP_TEST_HDCP2P2_REAUTH
cp_irq = true;
DP_DEBUG("[HDCP2P2_REAUTH_TEST]\n");
#endif
DP_DEBUG("cp_irq=0x%x\n", cp_irq);
buf = 0;
@ -667,6 +675,9 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
goto error;
}
*rx_status = buf;
#ifdef SECDP_TEST_HDCP2P2_REAUTH
*rx_status = 0x8;
#endif
DP_DEBUG("rx_status=0x%x\n", *rx_status);
}
@ -792,6 +803,15 @@ static bool dp_hdcp2p2_supported(void *input)
goto error;
}
#if defined(CONFIG_SECDP)
{
u32 i;
for (i = 0; i < DP_HDCP_RXCAPS_LENGTH; i++)
DP_DEBUG("rxcaps[%d] 0x%x\n", i, buf[i]);
}
#endif
DP_DEBUG("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1);
DP_DEBUG("VERSION=%d\n", buf[0]);

View File

@ -16,6 +16,9 @@
#include "dp_lphw_hpd.h"
#include "dp_debug.h"
#include "dp_bridge_hpd.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
static void dp_hpd_host_init(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog)

View File

@ -26,6 +26,12 @@
#include "dp_link.h"
#include "dp_panel.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#endif
enum dynamic_range {
DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
@ -902,6 +908,12 @@ static void dp_link_parse_sink_status_field(struct dp_link_private *link)
link->link_status);
if (len < DP_LINK_STATUS_SIZE)
DP_ERR("DP link status read failed\n");
#if defined(CONFIG_SECDP)
else
DP_INFO("[202h-207h] %02x-%02x-%02x-%02x-%02x-%02x\n",
link->link_status[0], link->link_status[1], link->link_status[2],
link->link_status[3], link->link_status[4], link->link_status[5]);
#endif
dp_link_parse_request(link);
}
@ -1267,6 +1279,106 @@ static void dp_link_reset_data(struct dp_link_private *link)
link->dp_link.test_response = 0;
}
#if defined(CONFIG_SECDP)
bool secdp_get_poor_connection_status(struct dp_link *dp_link)
{
return dp_link->poor_connection;
}
void secdp_clear_link_status_cnt(struct dp_link *dp_link)
{
dp_link->poor_connection = false;
dp_link->status_update_cnt = 0;
}
/** refer to dp_link_parse_sink_status_field() */
void secdp_read_link_status(struct dp_link *dp_link)
{
struct dp_link_private *link;
int len = 0;
if (!dp_link) {
DP_ERR("invalid input\n");
goto exit;
}
link = container_of(dp_link, struct dp_link_private, dp_link);
if (!link) {
DP_ERR("link is null\n");
goto exit;
}
DP_ENTER("\n");
len = drm_dp_dpcd_read_link_status(link->aux->drm_aux,
link->link_status);
if (len < DP_LINK_STATUS_SIZE) {
DP_ERR("DP link status read failed %d\n", len);
goto exit;
}
DP_INFO("[202h-207h] %02x-%02x-%02x-%02x-%02x-%02x\n",
link->link_status[0], link->link_status[1], link->link_status[2],
link->link_status[3], link->link_status[4], link->link_status[5]);
exit:
return;
}
/**
* @retval true if connection is stable
* @retval false if connection is unstable(poor)
*/
bool secdp_check_link_stable(struct dp_link *dp_link)
{
bool stable = false;
struct dp_link_private *link;
if (!dp_link) {
DP_ERR("invalid input\n");
goto exit;
}
link = container_of(dp_link, struct dp_link_private, dp_link);
if (!link) {
DP_ERR("link is null\n");
goto exit;
}
if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
DP_INTERLANE_ALIGN_DONE)) {
DP_ERR("[204h] interlane_align_done is zero!\n");
goto exit;
}
stable = true;
exit:
if (stable)
DP_DEBUG("DP connection is stable!\n");
#if defined(CONFIG_SECDP_BIGDATA)
if (!stable)
secdp_bigdata_inc_error_cnt(ERR_INF_IRQHPD);
#endif
return stable;
}
#if defined(CONFIG_SECDP_DBG)
int secdp_show_link_param(struct dp_link *dp_link, char *buf)
{
int rc = 0;
rc += scnprintf(buf + rc, PAGE_SIZE - rc,
"v_level: %u\np_level: %u\nlane_cnt: %u\nbw_code: 0x%x\n",
dp_link->phy_params.v_level,
dp_link->phy_params.p_level,
dp_link->link_params.lane_count,
dp_link->link_params.bw_code);
return rc;
}
#endif/*CONFIG_SECDP_DBG*/
#endif/*CONFIG_SECDP*/
/**
* dp_link_process_request() - handle HPD IRQ transition to HIGH
* @link: pointer to link module data
@ -1291,6 +1403,14 @@ static int dp_link_process_request(struct dp_link *dp_link)
dp_link_parse_sink_status_field(link);
#if defined(CONFIG_SECDP)
if (secdp_get_power_status() && !secdp_check_link_stable(dp_link)) {
dp_link->status_update_cnt++;
DP_INFO("[link_request] status_update_cnt %d\n",
dp_link->status_update_cnt);
secdp_link_backoff_start();
}
#endif
if (dp_link_is_test_edid_read(link)) {
dp_link->sink_request |= DP_TEST_LINK_EDID_READ;
goto exit;

View File

@ -148,6 +148,11 @@ struct dp_link {
u32 sink_request;
u32 test_response;
#if defined(CONFIG_SECDP)
bool poor_connection;
int status_update_cnt;
#endif
struct dp_link_sink_count sink_count;
struct dp_link_test_video test_video;
struct dp_link_test_audio test_audio;
@ -223,6 +228,9 @@ static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
break;
case DP_TEST_BIT_DEPTH_UNKNOWN:
default:
#if defined(CONFIG_SECDP)
pr_debug("%s: tbd(%d)\n", __func__, tbd);
#endif
bpp = 0;
}
@ -245,4 +253,15 @@ struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux, u32 dp_core_
*/
void dp_link_put(struct dp_link *dp_link);
#if defined(CONFIG_SECDP)
void secdp_clear_link_status_cnt(struct dp_link *dp_link);
void secdp_read_link_status(struct dp_link *dp_link);
bool secdp_check_link_stable(struct dp_link *dp_link);
bool secdp_get_poor_connection_status(struct dp_link *dp_link);
#if defined(CONFIG_SECDP_DBG)
int secdp_show_link_param(struct dp_link *dp_link, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif/*CONFIG_SECDP*/
#endif /* _DP_LINK_H_ */

View File

@ -52,6 +52,9 @@
#include "dp_drm.h"
#include "dp_debug.h"
#include "dp_parser.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
#define DP_MST_INFO(fmt, ...) DP_INFO(fmt, ##__VA_ARGS__)
@ -334,8 +337,10 @@ static int dp_mst_calc_pbn_mode(struct dp_display_mode *dp_mode)
pbn = drm_fixp2int(pbn_fp);
pinfo->pbn = pbn;
#if !defined(CONFIG_SECDP)
DP_DEBUG_V("pbn before overhead:%d pbn final:%d, bpp:%d\n", pinfo->pbn_no_overhead, pbn,
bpp);
#endif
return pbn;
}
@ -1225,6 +1230,9 @@ static int dp_mst_connector_get_modes(struct drm_connector *connector,
struct dp_display_mode *dp_mode = NULL;
int rc = 0;
struct edid *edid = NULL;
#if defined(CONFIG_SECDP)
u8 i;
#endif
DP_MST_DEBUG_V("enter:\n");
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, connector->base.id);
@ -1247,6 +1255,14 @@ static int dp_mst_connector_get_modes(struct drm_connector *connector,
mutex_lock(&mst->edid_lock);
c_conn->cached_edid = edid;
#if defined(CONFIG_SECDP)
for (i = 0; i <= edid->extensions; i++) {
print_hex_dump(KERN_DEBUG, "EDID: ", DUMP_PREFIX_NONE, 16, 1,
edid + i, EDID_LENGTH, false);
secdp_logger_hex_dump(edid + i, "EDID:", EDID_LENGTH);
}
#endif
duplicate_edid:
edid = drm_edid_duplicate(c_conn->cached_edid);
@ -1372,8 +1388,10 @@ int dp_mst_connector_get_mode_info(struct drm_connector *connector,
rc = dp_connector_get_mode_info(connector, drm_mode, NULL, mode_info,
display, avail_res);
#if !defined(CONFIG_SECDP)
DP_MST_DEBUG_V("mst connector:%d get mode info. rc:%d\n",
connector->base.id, rc);
#endif
DP_MST_DEBUG_V("exit:\n");
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, connector->base.id);

View File

@ -16,6 +16,13 @@
#include "sde_dsc_helper.h"
#include <drm/drm_edid.h>
#if defined(CONFIG_SECDP)
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#include "secdp.h"
#endif
#define DP_KHZ_TO_HZ 1000
#define DP_PANEL_DEFAULT_BPP 24
#define DP_MAX_DS_PORT_COUNT 1
@ -73,6 +80,9 @@ struct dp_panel_private {
struct dp_parser *parser;
struct dp_catalog_panel *catalog;
struct dp_panel *base;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
#endif
bool panel_on;
bool vsc_supported;
bool vscext_supported;
@ -84,12 +94,46 @@ struct dp_panel_private {
u8 minor;
};
#if defined(CONFIG_SECDP)
static struct dp_panel *g_dp_panel;
enum downstream_port_type {
DSP_TYPE_DP = 0x00,
DSP_TYPE_VGA,
DSP_TYPE_DVI_HDMI_DPPP,
DSP_TYPE_OTHER,
};
static inline char *mdss_dp_dsp_type_to_string(u32 dsp_type)
{
switch (dsp_type) {
case DSP_TYPE_DP:
return DP_ENUM_STR(DSP_TYPE_DP);
case DSP_TYPE_VGA:
return DP_ENUM_STR(DSP_TYPE_VGA);
case DSP_TYPE_DVI_HDMI_DPPP:
return DP_ENUM_STR(DSP_TYPE_DVI_HDMI_DPPP);
case DSP_TYPE_OTHER:
return DP_ENUM_STR(DSP_TYPE_OTHER);
default:
return "unknown";
}
}
/* OEM NAME */
static const u8 vendor_name[8] = {'S', 'E', 'C', '.', 'M', 'C', 'B', 0};
/* MODEL NAME */
static const u8 product_desc[16] = {'G', 'A', 'L', 'A', 'X', 'Y', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
#else
/* OEM NAME */
static const u8 vendor_name[8] = {81, 117, 97, 108, 99, 111, 109, 109};
/* MODEL NAME */
static const u8 product_desc[16] = {83, 110, 97, 112, 100, 114, 97, 103,
111, 110, 0, 0, 0, 0, 0, 0};
#endif
struct dp_dhdr_maxpkt_calc_input {
u32 mdp_clk;
@ -1071,6 +1115,7 @@ static void _dp_panel_calc_tu(struct dp_tu_calc_input *in,
tu_table->lower_boundary_count = tu.lower_boundary_count;
tu_table->tu_size_minus1 = tu.tu_size_minus1;
#if !defined(CONFIG_SECDP)
DP_DEBUG("TU: valid_boundary_link: %d\n", tu_table->valid_boundary_link);
DP_DEBUG("TU: delay_start_link: %d\n", tu_table->delay_start_link);
DP_DEBUG("TU: boundary_moderation_en: %d\n",
@ -1082,6 +1127,7 @@ static void _dp_panel_calc_tu(struct dp_tu_calc_input *in,
DP_DEBUG("TU: lower_boundary_count: %d\n",
tu_table->lower_boundary_count);
DP_DEBUG("TU: tu_size_minus1: %d\n", tu_table->tu_size_minus1);
#endif
}
static void dp_panel_calc_tu_parameters(struct dp_panel *dp_panel,
@ -1275,7 +1321,9 @@ static void _dp_panel_dsc_get_num_extra_pclk(struct msm_compression_info *comp_i
else
dsc->extra_width = 0;
#if !defined(CONFIG_SECDP)
DP_DEBUG_V("extra pclks required: %d\n", dsc->extra_width);
#endif
}
static void _dp_panel_dsc_bw_overhead_calc(struct dp_panel *dp_panel,
@ -1304,8 +1352,10 @@ static void _dp_panel_dsc_bw_overhead_calc(struct dp_panel *dp_panel,
dwidth_dsc_bytes = tot_num_hor_bytes + tot_num_eoc_symbols +
tot_num_dummy_bytes;
#if !defined(CONFIG_SECDP)
DP_DEBUG_V("dwidth_dsc_bytes:%d, tot_num_hor_bytes:%d\n",
dwidth_dsc_bytes, tot_num_hor_bytes);
#endif
dp_mode->dsc_overhead_fp = drm_fixp_from_fraction(dwidth_dsc_bytes,
tot_num_hor_bytes);
@ -1471,9 +1521,11 @@ static int dp_panel_dsc_prepare_basic_params(
(dsc_version_minor == 0x1 || dsc_version_minor == 0x2))
? true : false;
#if !defined(CONFIG_SECDP)
DP_DEBUG_V("DSC version: %d.%d, dpcd value: %x\n",
dsc_version_major, dsc_version_minor,
dp_panel->sink_dsc_caps.version);
#endif
if (!dsc_version_supported) {
dsc_version_major = 1;
@ -1496,8 +1548,12 @@ static int dp_panel_dsc_prepare_basic_params(
}
}
if (comp_info->dsc_info.slice_per_pkt == 0)
if (comp_info->dsc_info.slice_per_pkt == 0) {
#if defined(CONFIG_SECDP)
DP_ERR("slice_per_pkt is zero\n");
return -EINVAL;
#endif
}
ppr_max_index = dp_panel->dsc_dpcd[11] &= 0xf;
if (!ppr_max_index || ppr_max_index >= 15) {
@ -1528,8 +1584,20 @@ static int dp_panel_dsc_prepare_basic_params(
!dp_panel_check_slice_support(
comp_info->dsc_info.slice_per_pkt, slice_caps_1,
slice_caps_2)) {
if (i == ARRAY_SIZE(slice_per_line_tbl))
#if defined(CONFIG_SECDP)
DP_DEBUG("[%d] slice_width=%d, max_slice_width=%d, ppr_per_slice=%d, peak_throughput=%d, dp_panel_check_slice_support=%d\n",
i, slice_width, max_slice_width, ppr_per_slice, peak_throughput, dp_panel_check_slice_support(
comp_info->dsc_info.slice_per_pkt, slice_caps_1,
slice_caps_2));
DP_DEBUG("comp_info->dsc_info.slice_per_pkt=%d, slice_caps_1=%x, slice_caps_2=%x\n",
comp_info->dsc_info.slice_per_pkt, slice_caps_1, slice_caps_2);
#endif
if (i == ARRAY_SIZE(slice_per_line_tbl)) {
#if defined(CONFIG_SECDP)
DP_ERR("reached end of slice_per_line_tbl %d\n", i);
#endif
return -EINVAL;
}
rec = &slice_per_line_tbl[i];
comp_info->dsc_info.slice_per_pkt = rec->num_slices;
@ -1587,6 +1655,11 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
goto end;
}
#if defined(CONFIG_SECDP)
DP_ENTER("\n");
g_dp_panel = dp_panel;
#endif
dpcd = dp_panel->dpcd;
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
@ -1627,6 +1700,9 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
print_hex_dump_debug("[drm-dp] SINK DPCD: ",
DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false);
#if defined(CONFIG_SECDP)
secdp_logger_hex_dump(dp_panel->dpcd, "DPCD:", rlen);
#endif
rlen = drm_dp_dpcd_read(panel->aux->drm_aux,
DPRX_FEATURE_ENUMERATION_LIST, &rx_feature, 1);
@ -1653,6 +1729,12 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
/* override link params updated in dp_panel_init_panel_info */
link_info->rate = min_t(unsigned long, panel->parser->max_lclk_khz,
drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]));
#ifdef SECDP_MAX_HBR2
if (link_info->rate > 540000) { /*DP_LINK_BW_5_4*/
DP_DEBUG("set it to 540000!\n");
link_info->rate = 540000;
}
#endif
link_info->num_lanes = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
@ -1675,6 +1757,18 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
DP_DEBUG("version:%d.%d, rate:%d, lanes:%d\n", panel->major,
panel->minor, link_info->rate, link_info->num_lanes);
#ifdef SECDP_SELF_TEST
if (secdp_self_test_status(ST_LINK_RATE) >= 0) {
link_info->rate = secdp_self_test_get_arg(ST_LINK_RATE)[0];
DP_INFO("secdp self test: link_rate %d\n", link_info->rate);
}
if (secdp_self_test_status(ST_LANE_CNT) >= 0) {
link_info->num_lanes = secdp_self_test_get_arg(ST_LANE_CNT)[0];
DP_INFO("secdp self test: lane_cnt %d\n", link_info->num_lanes);
}
#endif
if (drm_dp_enhanced_frame_cap(dpcd))
link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
@ -1701,6 +1795,18 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
DP_DEBUG("DS port count %d greater that max (%d) supported\n",
dfp_count, DP_MAX_DS_PORT_COUNT);
#if defined(CONFIG_SECDP)
dp_panel->dsp_type = (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) >> 1;
DP_INFO("dsp_type: <%s>\n", mdss_dp_dsp_type_to_string(dp_panel->dsp_type));
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_save_item(BD_ADAPTER_TYPE, mdss_dp_dsp_type_to_string(dp_panel->dsp_type));
secdp_bigdata_save_item(BD_MAX_LANE_COUNT, link_info->num_lanes);
secdp_bigdata_save_item(BD_MAX_LINK_RATE, dp_panel->dpcd[DP_MAX_LINK_RATE]);
secdp_bigdata_save_item(BD_CUR_LANE_COUNT, link_info->num_lanes);
secdp_bigdata_save_item(BD_CUR_LINK_RATE, dp_panel->dpcd[DP_MAX_LINK_RATE]);
#endif
#endif
end:
return rc;
}
@ -1724,6 +1830,279 @@ static int dp_panel_set_default_link_params(struct dp_panel *dp_panel)
return 0;
}
#ifdef SECDP_OPTIMAL_LINK_RATE
/*
//IMPORT/Qualcomm/kernel/SM8350_R/msm-5.4/techpack/display/msm/dp/dp_panel.c#7
//KERNEL/LEGO/BSP/Combination/SM8350_R/msm-5.4/techpack/display/msm/dp/dp_panel.c#10
*/
static u32 secdp_panel_get_min_req_link_rate(struct dp_panel *dp_panel)
{
const u32 encoding_factx10 = 8;
u32 min_link_rate_khz = 0, lane_cnt;
struct dp_panel_info *pinfo;
if (!dp_panel) {
DP_ERR("invalid input\n");
goto end;
}
lane_cnt = dp_panel->link_info.num_lanes;
pinfo = &dp_panel->max_timing_info;
/* num_lanes * lane_count * 8 >= pclk * bpp * 10 */
min_link_rate_khz = pinfo->pixel_clk_khz /
(lane_cnt * encoding_factx10);
min_link_rate_khz *= pinfo->bpp;
DP_DEBUG("min lclk req=%d khz for pclk=%d khz, lanes=%d, bpp=%d\n",
min_link_rate_khz, pinfo->pixel_clk_khz, lane_cnt,
pinfo->bpp);
end:
return min_link_rate_khz;
}
#define RES_1920X1080 2073600
#define RES_2560X1440 3686400
static bool ps176_high_refresh_rate_check(struct dp_panel *dp_panel)
{
struct dp_panel_private *panel;
struct secdp_misc *sec;
struct dp_panel_info *max_timing;
int max_resolution;
bool ret = false;
DP_ENTER("\n");
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
sec = panel->sec;
if (!secdp_adapter_check_parade(sec))
goto end;
if (!secdp_adapter_check_ps176(sec))
goto end;
max_timing = &dp_panel->max_timing_info;
max_resolution = max_timing->h_active * max_timing->v_active;
if (max_resolution >= RES_1920X1080 &&
max_timing->refresh_rate > 110 &&
max_timing->pixel_clk_khz > 250000) {
ret = true;
} else if (max_resolution >= RES_2560X1440 &&
max_timing->refresh_rate > 75 &&
max_timing->pixel_clk_khz > 300000) {
ret = true;
}
DP_INFO("[ps176] max %ux%u@%uhz, pclk %uKhz, %d\n",
max_timing->h_active, max_timing->v_active,
max_timing->refresh_rate, max_timing->pixel_clk_khz, ret);
end:
DP_LEAVE("%d\n", ret);
return ret;
}
u32 secdp_gen_link_clk(struct dp_panel *dp_panel)
{
u32 calc_link_rate, min_link_rate;
DP_ENTER("\n");
#ifndef SECDP_MAX_HBR2
calc_link_rate = 810000;
#else
calc_link_rate = 540000;
#endif
if (!dp_panel || ps176_high_refresh_rate_check(dp_panel))
goto end;
min_link_rate = secdp_panel_get_min_req_link_rate(dp_panel);
if (!min_link_rate)
DP_INFO("timing not found\n");
if (min_link_rate <= 162000)
calc_link_rate = 162000;
else if (min_link_rate <= 270000)
calc_link_rate = 270000;
else if (min_link_rate <= 540000)
calc_link_rate = 540000;
#ifndef SECDP_MAX_HBR2
else if (min_link_rate <= 810000)
calc_link_rate = 810000;
#endif
else
DP_ERR("too big!, set default\n");
DP_INFO("min_link_rate <%u>, calc_link_rate <%u>\n",
min_link_rate, calc_link_rate);
end:
DP_LEAVE("\n");
return calc_link_rate;
}
#endif/*SECDP_OPTIMAL_LINK_RATE*/
#if defined(CONFIG_SECDP)
static int dp_panel_get_modes(struct dp_panel *dp_panel,
struct drm_connector *connector, struct dp_display_mode *mode);
static void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel,
const struct drm_display_mode *drm_mode,
struct dp_display_mode *dp_mode);
/**
* dp_panel_get_min_req_link_rate() needs two info :
* 1. pinfo->pixel_clk_khz
* 2. pinfo->bpp
* this function is made for future use of "SECDP_OPTIMAL_LINK_RATE"
*/
static void secdp_get_max_timing(struct dp_panel *dp_panel)
{
struct dp_link_params *link_params;
struct dp_panel_private *panel;
struct drm_device *dev;
struct drm_connector *conn;
struct drm_display_mode *mode, *temp;
struct dp_display_mode dp_mode;
struct dp_panel_info *pinfo, *timing;
int rc;
conn = dp_panel->connector;
dev = conn->dev;
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
mutex_lock(&dev->mode_config.mutex);
pinfo = &dp_panel->max_timing_info;
memset(pinfo, 0, sizeof(*pinfo));
memset(&dp_mode, 0, sizeof(dp_mode));
link_params = &panel->link->link_params;
link_params->bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate);
link_params->lane_count = dp_panel->link_info.num_lanes;
rc = dp_panel_get_modes(dp_panel, conn, &dp_mode);
if (!rc) {
DP_INFO("no valid mode\n");
goto end;
}
list_for_each_entry(mode, &conn->probed_modes, head) {
dp_panel_convert_to_dp_mode(dp_panel, mode, &dp_mode);
timing = &dp_mode.timing;
if (pinfo->pixel_clk_khz < timing->pixel_clk_khz) {
pinfo->h_active = timing->h_active;
pinfo->v_active = timing->v_active;
pinfo->refresh_rate = timing->refresh_rate;
pinfo->pixel_clk_khz = timing->pixel_clk_khz;
pinfo->bpp = timing->bpp;
DP_INFO("updated, %ux%u@%uhz, pclk:%u, bpp:%u\n",
pinfo->h_active, pinfo->v_active,
pinfo->refresh_rate, pinfo->pixel_clk_khz,
pinfo->bpp);
}
}
list_for_each_entry_safe(mode, temp, &conn->probed_modes, head) {
list_del(&mode->head);
drm_mode_destroy(conn->dev, mode);
}
end:
mutex_unlock(&dev->mode_config.mutex);
}
/* DP testbox list */
static char secdp_tbox[][MON_NAME_LEN] = {
"UNIGRAF TE",
"UFG DPR-120",
"UCD-400 DP",
"UCD-400 DP1",
"AGILENT ATR",
"UFG DP SINK",
};
#define SECDP_TBOX_MAX 32
/** check if connected sink is testbox or not
* return true if it's testbox
* return false otherwise (real sink)
*/
static bool secdp_check_tbox(struct dp_panel *panel)
{
unsigned long i, size = SECDP_TBOX_MAX;
size_t len = 0;
bool ret = false;
len = strlen(panel->monitor_name);
if (!len)
goto end;
size = min(ARRAY_SIZE(secdp_tbox), size);
for (i = 0; i < size; i++) {
int rc;
rc = strncmp(panel->monitor_name, secdp_tbox[i], len);
if (!rc) {
DP_INFO("<%s> detected!\n", panel->monitor_name);
ret = true;
goto end;
}
}
DP_INFO("real sink <%s>\n", panel->monitor_name);
end:
panel->tbox = ret;
return ret;
}
static void secdp_show_sink_caps(struct dp_panel *dp_panel)
{
DP_INFO("dpcd_rev:0x%02x, vendor:%s, monitor:%s\n",
dp_panel->dpcd[DP_DPCD_REV],
dp_panel->edid_ctrl->vendor_id,
dp_panel->monitor_name);
if (dp_panel->dsc_en) {
u32 dsc_version_major, dsc_version_minor;
bool dsc_version_supported = false;
dsc_version_major = dp_panel->sink_dsc_caps.version & 0xF;
dsc_version_minor = (dp_panel->sink_dsc_caps.version >> 4) & 0xF;
dsc_version_supported = (dsc_version_major == 0x1 &&
(dsc_version_minor == 0x1 || dsc_version_minor == 0x2))
? true : false;
DP_INFO("DSC version: %d.%d(support:%d), dpcd value: %x\n",
dsc_version_major, dsc_version_minor,
dsc_version_supported,
dp_panel->sink_dsc_caps.version);
}
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_save_item(BD_SINK_NAME, dp_panel->monitor_name);
secdp_bigdata_save_item(BD_EDID, (char *)(dp_panel->edid_ctrl->edid));
#endif
}
static bool secdp_fetch_monitor_name(struct edid *edid, char *name, int len)
{
struct edid *edid_dup;
bool ret = false;
if (!edid)
goto exit;
edid_dup = drm_edid_duplicate(edid);
drm_edid_get_monitor_name(edid_dup, name, len);
kfree(edid_dup);
ret = strlen(name) ? true : false;
exit:
return ret;
}
#endif
static int dp_panel_read_edid(struct dp_panel *dp_panel,
struct drm_connector *connector)
{
@ -1738,6 +2117,10 @@ static int dp_panel_read_edid(struct dp_panel *dp_panel,
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
#if defined(CONFIG_SECDP)
secdp_timing_init(panel->sec);
#endif
sde_get_edid(connector, &panel->aux->drm_aux->ddc,
(void **)&dp_panel->edid_ctrl);
if (!dp_panel->edid_ctrl->edid) {
@ -1745,6 +2128,10 @@ static int dp_panel_read_edid(struct dp_panel *dp_panel,
ret = -EINVAL;
goto end;
}
#if defined(CONFIG_SECDP)
secdp_get_max_timing(dp_panel);
#endif
end:
edid = dp_panel->edid_ctrl->edid;
dp_panel->audio_supported = drm_detect_monitor_audio(edid);
@ -1843,18 +2230,38 @@ static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
#if defined(CONFIG_SECDP)
usleep_range(10000, 11000);
#endif
rc = dp_panel_read_dpcd(dp_panel, multi_func);
if (rc || !is_link_rate_valid(drm_dp_link_rate_to_bw_code(
dp_panel->link_info.rate)) || !is_lane_count_valid(
dp_panel->link_info.num_lanes) ||
((drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate)) >
dp_panel->max_bw_code)) {
#if !defined(CONFIG_SECDP)
if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) {
DP_ERR("DPCD read failed, return early\n");
goto end;
}
#else
if (!secdp_get_hpd_status() || !secdp_get_cable_status()) {
DP_INFO("hpd_low or cable_lost\n");
rc = -ETIMEDOUT;
goto end;
}
#endif
DP_ERR("panel dpcd read failed/incorrect, set default params\n");
dp_panel_set_default_link_params(dp_panel);
#if defined(CONFIG_SECDP)
if (rc < 0) {
rc = -ETIMEDOUT;
goto end;
}
#endif
}
downstream_ports = dp_panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
@ -1881,6 +2288,9 @@ static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
rc = dp_panel_read_edid(dp_panel, connector);
if (rc) {
DP_ERR("panel edid read failed, set failsafe mode\n");
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_inc_error_cnt(ERR_EDID);
#endif
return rc;
}
@ -1902,6 +2312,13 @@ skip_edid:
DP_INFO("fec_en=%d, dsc_en=%d, widebus_en=%d\n", dp_panel->fec_en,
dp_panel->dsc_en, dp_panel->widebus_en);
#if defined(CONFIG_SECDP)
secdp_fetch_monitor_name(dp_panel->edid_ctrl->edid, dp_panel->monitor_name, 14);
secdp_check_tbox(dp_panel);
secdp_show_sink_caps(dp_panel);
#endif
end:
return rc;
}
@ -1924,7 +2341,15 @@ static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
if (dsc_en)
min_supported_bpp = 24;
#if !defined(CONFIG_SECDP)
bpp = min_t(u32, mode_edid_bpp, max_supported_bpp);
#else
/* 4Kp60hz + bpp30 does not output audio with DP2HDMI dongle connection because
* DP2HDMI dongle does not support HDR10 yet. It has bandwidth limitation
*/
bpp = min_t(u32, mode_edid_bpp,
((dp_panel->dsp_type == DSP_TYPE_DP) ? max_supported_bpp : max_supported_bpp - 6));
#endif
link_params = &panel->link->link_params;
@ -2042,6 +2467,9 @@ static int dp_panel_get_modes(struct dp_panel *dp_panel,
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
if (dp_panel->video_test) {
#if defined(CONFIG_SECDP)
DP_DEBUG("video_test:%d\n", dp_panel->video_test);
#endif
dp_panel_set_test_mode(panel, mode);
return 1;
} else if (dp_panel->edid_ctrl->edid) {
@ -2427,6 +2855,27 @@ static bool dp_panel_hdr_supported(struct dp_panel *dp_panel)
(panel->minor >= 4 || panel->vscext_supported);
}
#if defined(CONFIG_SECDP)
bool secdp_panel_hdr_supported(void)
{
struct dp_panel *dp_panel;
bool hdr;
dp_panel = g_dp_panel;
if (!dp_panel) {
DP_ERR("invalid input\n");
return false;
}
hdr = dp_panel_hdr_supported(dp_panel);
DP_INFO("dsp_type:%s, hdr:%d\n",
mdss_dp_dsp_type_to_string(dp_panel->dsp_type), hdr);
return ((dp_panel->dsp_type == DSP_TYPE_DP) && hdr);
}
#endif/*CONFIG_SECDP*/
static u32 dp_panel_calc_dhdr_pkt_limit(struct dp_panel *dp_panel,
struct dp_dhdr_maxpkt_calc_input *input)
{
@ -2829,6 +3278,20 @@ static void dp_panel_resolution_info(struct dp_panel_private *panel)
pinfo->refresh_rate, pinfo->bpp, pinfo->pixel_clk_khz,
panel->link->link_params.bw_code,
panel->link->link_params.lane_count);
#if defined(CONFIG_SECDP)
DP_INFO("SET NEW RESOLUTION: %dx%d@%dfps\n",
pinfo->h_active, pinfo->v_active, pinfo->refresh_rate);
#endif
#if defined(CONFIG_SECDP_BIGDATA)
{
char buf[20] = {0, };
scnprintf(buf, 20, "%dx%d@%d",
pinfo->h_active, pinfo->v_active, pinfo->refresh_rate);
secdp_bigdata_save_item(BD_RESOLUTION, buf);
}
#endif
}
static void dp_panel_config_sdp(struct dp_panel *dp_panel,
@ -2914,6 +3377,14 @@ static int dp_panel_update_edid(struct dp_panel *dp_panel, struct edid *edid)
dp_panel->edid_ctrl->edid = edid;
sde_parse_edid(dp_panel->edid_ctrl);
#if defined(CONFIG_SECDP)
if (!strlen(dp_panel->monitor_name)) {
secdp_fetch_monitor_name(edid, dp_panel->monitor_name, 14);
DP_INFO("[mst] vendor:%s, monitor:%s\n",
dp_panel->edid_ctrl->vendor_id, dp_panel->monitor_name);
}
#endif
rc = _sde_edid_update_modes(dp_panel->connector, dp_panel->edid_ctrl);
dp_panel->audio_supported = drm_detect_monitor_audio(edid);
@ -3156,9 +3627,16 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
panel->catalog = in->catalog;
panel->link = in->link;
panel->parser = in->parser;
#if defined(CONFIG_SECDP)
panel->sec = in->sec;
#endif
dp_panel = &panel->dp_panel;
#ifndef SECDP_MAX_HBR2
dp_panel->max_bw_code = DP_LINK_BW_8_1;
#else
dp_panel->max_bw_code = DP_LINK_BW_5_4;
#endif
dp_panel->spd_enabled = true;
dp_panel->link_bw_code = 0;
dp_panel->lane_count = 0;

View File

@ -93,6 +93,9 @@ struct dp_panel_in {
struct drm_connector *connector;
struct dp_panel *base_panel;
struct dp_parser *parser;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
#endif
};
struct dp_dsc_caps {
@ -130,6 +133,13 @@ struct dp_panel {
u32 link_bw_code;
u32 max_supported_bpp;
#if defined(CONFIG_SECDP)
bool tbox;
u8 monitor_name[14]; /* max 13 chars + null */
u32 dsp_type;
struct dp_panel_info max_timing_info;
#endif
/* By default, stream_id is assigned to DP_INVALID_STREAM.
* Client sets the stream id value using set_stream_id interface.
*/
@ -264,4 +274,10 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in);
void dp_panel_put(struct dp_panel *dp_panel);
void dp_panel_calc_tu_test(struct dp_tu_calc_input *in,
struct dp_vc_tu_mapping_table *tu_table);
#define SECDP_OPTIMAL_LINK_RATE /* use optimum link_rate, not max link_rate */
#ifdef SECDP_OPTIMAL_LINK_RATE
u32 secdp_gen_link_clk(struct dp_panel *dp_panel);
#endif
#endif /* _DP_PANEL_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -182,6 +182,42 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
}
}
#if defined(CONFIG_SECDP)
enum secdp_phy_pre_emphasis_type {
PHY_PRE_EMP0, /* 0 db */
PHY_PRE_EMP1, /* 3.5 db */
PHY_PRE_EMP2, /* 6.0 db */
PHY_PRE_EMP3, /* 9.5 db */
// MAX_PRE_EMP_LEVELS,
};
enum secdp_phy_voltage_type {
PHY_VOLTAGE_SWING0, /* 0.4 v */
PHY_VOLTAGE_SWING1, /* 0.6 v */
PHY_VOLTAGE_SWING2, /* 0.8 v */
PHY_VOLTAGE_SWING3, /* 1.2 v, optional */
MAX_VOLTAGE_LEVELS,
};
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
enum secdp_ps5169_pre_emphasis_type {
PHY_PS5169_EMP0, /* 0 db */
PHY_PS5169_EMP1, /* 3.5 db */
PHY_PS5169_EMP2, /* 6.0 db */
PHY_PS5169_EMP3, /* 9.5 db */
MAX_PS5169_EMP_LEVELS,
};
enum secdp_PS5169_voltage_type {
PHY_PS5169_SWING0, /* 0.4 v */
PHY_PS5169_SWING1, /* 0.6 v */
PHY_PS5169_SWING2, /* 0.8 v */
PHY_PS5169_SWING3, /* 1.2 v, optional */
MAX_PS5169_SWING_LEVELS,
};
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
#endif/*CONFIG_SECDP*/
/**
* struct dp_parser - DP parser's data exposed to clients
*
@ -246,6 +282,29 @@ struct dp_parser {
u8 *pre_emp_hbr_rbr;
bool valid_lt_params;
#if defined(CONFIG_SECDP)
struct regulator *aux_pullup_vreg;
bool cc_dir_inv; /* CC_DIR is inversed, e.g, T865 */
bool aux_sel_inv; /* inverse control of AUX_SEL e.g, D2Xq hwid 01,02 */
int use_redrv; /* ptn36502 needs NOT AUX switch SEL control */
int dex_dft_res; /* DeX default resolution, e.g, HG950 */
bool prefer_support; /* true if prefer resolution has high priority */
bool mrr_fps_nolimit; /* true if mirroring refresh rate has no limit */
bool rf_tx_backoff; /* true if it RF TX Backoff is supported, for SHELL-less type connector */
bool mst_support; /* true if MST is supported */
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
u8 ps5169_rbr_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_rbr_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr2_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr2_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr3_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr3_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
#endif/*CONFIG_SECDP*/
int (*parse)(struct dp_parser *parser);
struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);
void (*get_io_buf)(struct dp_parser *parser, char *name);
@ -286,4 +345,106 @@ struct dp_parser *dp_parser_get(struct platform_device *pdev);
* @parser: pointer to the parser's data.
*/
void dp_parser_put(struct dp_parser *parser);
#if defined(CONFIG_SECDP) && IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
enum secdp_ps5169_eq_t {
DP_PS5169_EQ0,
DP_PS5169_EQ1,
DP_PS5169_EQ_MAX,
};
enum secdp_ps5169_link_rate_t {
DP_PS5169_RATE_RBR,
DP_PS5169_RATE_HBR,
DP_PS5169_RATE_HBR2,
DP_PS5169_RATE_HBR3,
DP_PS5169_RATE_MAX,
};
#endif
#if defined(CONFIG_SECDP_DBG)
enum secdp_link_rate_t {
DP_LR_NONE = 0x0,
DP_LR_HBR_RBR = 0x1,
DP_LR_HBR2_3 = 0x2,
};
static inline char *secdp_link_rate_to_string(int lr)
{
switch (lr) {
case DP_LR_HBR_RBR:
return DP_ENUM_STR(DP_LR_HBR_RBR);
case DP_LR_HBR2_3:
return DP_ENUM_STR(DP_LR_HBR2_3);
default:
return "unknown";
}
}
enum secdp_phy_param_t {
DP_PARAM_NONE = 0x0,
DP_PARAM_VX = 0x1, /* voltage swing */
DP_PARAM_PX = 0x2, /* pre-emphasis */
};
static inline char *secdp_phy_type_to_string(int param)
{
switch (param) {
case DP_PARAM_VX:
return DP_ENUM_STR(DP_PARAM_VX);
case DP_PARAM_PX:
return DP_ENUM_STR(DP_PARAM_PX);
default:
return "unknown";
}
}
/* voltage swing, pre-emphasis */
int secdp_parse_vxpx_show(struct dp_parser *parser, enum secdp_link_rate_t lr,
enum secdp_phy_param_t vxpx, char *buf);
int secdp_parse_vxpx_store(struct dp_parser *parser, enum secdp_link_rate_t lr,
enum secdp_phy_param_t vxpx, char *buf);
int secdp_show_phy_param(struct dp_parser *parser, char *buf);
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
static inline char *secdp_ps5169_eq_to_string(int hw)
{
switch (hw) {
case DP_PS5169_EQ0:
return DP_ENUM_STR(DP_PS5169_EQ0);
case DP_PS5169_EQ1:
return DP_ENUM_STR(DP_PS5169_EQ1);
default:
return "unknown";
}
}
static inline char *secdp_ps5169_rate_to_string(int hw)
{
switch (hw) {
case DP_PS5169_RATE_RBR:
return DP_ENUM_STR(DP_PS5169_RATE_RBR);
case DP_PS5169_RATE_HBR:
return DP_ENUM_STR(DP_PS5169_RATE_HBR);
case DP_PS5169_RATE_HBR2:
return DP_ENUM_STR(DP_PS5169_RATE_HBR2);
case DP_PS5169_RATE_HBR3:
return DP_ENUM_STR(DP_PS5169_RATE_HBR3);
default:
return "unknown";
}
}
int secdp_parse_ps5169_show(struct dp_parser *parser, enum secdp_ps5169_eq_t eq,
enum secdp_ps5169_link_rate_t link_rate, char *buf);
int secdp_parse_ps5169_store(struct dp_parser *parser, enum secdp_ps5169_eq_t eq,
enum secdp_ps5169_link_rate_t link_rate, char *buf);
int secdp_show_ps5169_param(struct dp_parser *parser, char *buf);
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
/* AUX configuration */
int secdp_aux_cfg_show(struct dp_parser *parser, char *buf);
int secdp_aux_cfg_store(struct dp_parser *parser, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif

View File

@ -9,6 +9,10 @@
#include "dp_debug.h"
#include "dp_pll.h"
#if defined(CONFIG_SECDP)
#include <linux/secdp_logger.h>
#endif
static int dp_pll_fill_io(struct dp_pll *pll)
{
struct dp_parser *parser = pll->parser;

View File

@ -11,6 +11,28 @@
#include "dp_debug.h"
#include "dp_pll.h"
#if defined(CONFIG_SECDP)
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include "secdp.h"
#if IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)// && IS_ENABLED(CONFIG_IF_CB_MANAGER)
#include <linux/usb/typec/manager/if_cb_manager.h>
#endif
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502)
#include <linux/combo_redriver/ptn36502.h>
#elif IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
#include <linux/combo_redriver/ps5169.h>
#endif
#define DP_LINK_BW_RBR 0x06
#define DP_LINK_BW_HBR 0x0a
#define DP_LINK_BW_HBR2 0x14 /* 1.2 */
#define DP_LINK_BW_HBR3 0x1e /* 1.4 */
#endif/*CONFIG_SECDP*/
#define DP_CLIENT_NAME_SIZE 20
#define XO_CLK_KHZ 19200
@ -34,8 +56,32 @@ struct dp_power_private {
bool strm0_clks_parked;
bool strm1_clks_parked;
bool link_clks_parked;
#if defined(CONFIG_SECDP)
bool aux_pullup_on;
struct mutex dp_clk_lock;
#if IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
enum sbu_switch_status sbu_status;
#endif
void (*redrv_onoff)(struct dp_power_private *power,
bool enable, int lane);
void (*redrv_aux_ctrl)(struct dp_power_private *power, int cross);
void (*redrv_notify_linkinfo)(struct dp_power_private *power,
u32 bw_code, u8 v_level, u8 p_level);
#endif
};
#if defined(CONFIG_SECDP)
#define DP_ENUM_STR(x) #x
enum redriver_switch_t {
REDRIVER_SWITCH_UNKNOWN = -1,
REDRIVER_SWITCH_RESET = 0,
REDRIVER_SWITCH_CROSS,
REDRIVER_SWITCH_THROU,
};
#endif
static int dp_power_regulator_init(struct dp_power_private *power)
{
int rc = 0, i = 0, j = 0;
@ -84,6 +130,86 @@ static void dp_power_regulator_deinit(struct dp_power_private *power)
}
}
#if defined(CONFIG_SECDP)
/* factory use only
* ref: qusb_phy_enable_power()
*/
static int secdp_aux_pullup_vreg_enable(struct dp_power_private *power, bool on)
{
struct regulator *aux_pu_vreg;
int rc = 0;
if (!power || !power->parser) {
DP_ERR("error! power is null\n");
goto exit;
}
aux_pu_vreg = power->parser->aux_pullup_vreg;
if (!aux_pu_vreg) {
DP_ERR("error! vdda33 is null\n");
goto exit;
}
DP_ENTER("on:%d\n", on);
#define QUSB2PHY_3P3_VOL_MIN 3104000 /* uV */
#define QUSB2PHY_3P3_VOL_MAX 3105000 /* uV */
#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */
if (on) {
if (power->aux_pullup_on) {
DP_INFO("already on\n");
goto exit;
}
rc = regulator_set_load(aux_pu_vreg, QUSB2PHY_3P3_HPM_LOAD);
if (rc < 0) {
DP_ERR("Unable to set HPM of vdda33:%d\n", rc);
goto exit;
}
rc = regulator_set_voltage(aux_pu_vreg, QUSB2PHY_3P3_VOL_MIN,
QUSB2PHY_3P3_VOL_MAX);
if (rc) {
DP_ERR("Unable to set voltage for vdda33:%d\n", rc);
goto put_vdda33_lpm;
}
rc = regulator_enable(aux_pu_vreg);
if (rc) {
DP_ERR("Unable to enable vdda33:%d\n", rc);
goto unset_vdd33;
}
DP_INFO("[AUX_PU] on success\n");
power->aux_pullup_on = true;
} else {
rc = regulator_disable(aux_pu_vreg);
if (rc)
DP_ERR("Unable to disable vdda33:%d\n", rc);
unset_vdd33:
rc = regulator_set_voltage(aux_pu_vreg, 0,
QUSB2PHY_3P3_VOL_MAX);
if (rc)
DP_ERR("Unable to set 0 voltage for vdda33:%d\n", rc);
put_vdda33_lpm:
rc = regulator_set_load(aux_pu_vreg, 0);
if (!rc)
DP_INFO("[AUX_PU] off success\n");
else
DP_ERR("Unable to set 0 HPM of vdda33:%d\n", rc);
power->aux_pullup_on = false;
}
exit:
return rc;
}
#endif
static void dp_power_phy_gdsc(struct dp_power *dp_power, bool on)
{
int rc = 0;
@ -139,6 +265,11 @@ static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
goto error;
}
}
#if defined(CONFIG_SECDP)
secdp_aux_pullup_vreg_enable(power, enable);
#endif
error:
return rc;
}
@ -149,6 +280,10 @@ static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
struct pinctrl_state *pin_state;
struct dp_parser *parser;
#if IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
return 0;
#endif
parser = power->parser;
if (IS_ERR_OR_NULL(parser->pinctrl.pin))
@ -363,7 +498,11 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
{
int rc = 0;
struct dss_module_power *mp;
#if defined(CONFIG_SECDP)
static bool prev[DP_MAX_PM];
mutex_lock(&power->dp_clk_lock);
#endif
if (!power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
@ -372,6 +511,13 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
mp = &power->parser->mp[module];
#if defined(CONFIG_SECDP)
if (prev[module] == enable) {
DP_DEBUG("%d clk already %s\n", module, enable ? "enabled" : "disabled");
goto exit;
}
#endif
if (enable) {
rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
if (rc) {
@ -393,7 +539,14 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
dp_power_park_module(power, module);
}
#if defined(CONFIG_SECDP)
prev[module] = enable;
#endif
exit:
#if defined(CONFIG_SECDP)
mutex_unlock(&power->dp_clk_lock);
#endif
return rc;
}
@ -561,6 +714,7 @@ static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
return !!strnstr(gpio1, gpio2, strlen(gpio1));
}
#if !defined(CONFIG_SECDP)
static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
{
int i;
@ -586,10 +740,465 @@ static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
config++;
}
}
#else
int secdp_power_request_gpios(struct dp_power *dp_power)
{
int rc;
struct dp_power_private *power;
if (!dp_power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
goto exit;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
rc = dp_power_request_gpios(power);
exit:
return rc;
}
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502) || IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
static inline char *secdp_redriver_switch_to_string(int event)
{
switch (event) {
case REDRIVER_SWITCH_UNKNOWN:
return DP_ENUM_STR(REDRIVER_SWITCH_UNKNOWN);
case REDRIVER_SWITCH_RESET:
return DP_ENUM_STR(REDRIVER_SWITCH_RESET);
case REDRIVER_SWITCH_CROSS:
return DP_ENUM_STR(REDRIVER_SWITCH_CROSS);
case REDRIVER_SWITCH_THROU:
return DP_ENUM_STR(REDRIVER_SWITCH_THROU);
default:
return "unknown";
}
}
#endif
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502)
static void secdp_ptn36502_aux_ctrl(struct dp_power_private *power, int cross)
{
DP_DEBUG("cross:%s\n", secdp_redriver_switch_to_string(cross));
switch (cross) {
case REDRIVER_SWITCH_CROSS:
ptn36502_config(AUX_CROSS_MODE, 0);
break;
case REDRIVER_SWITCH_THROU:
ptn36502_config(AUX_THRU_MODE, 0);
break;
case REDRIVER_SWITCH_RESET:
ptn36502_config(SAFE_STATE, 0);
break;
default:
DP_INFO("unknown: %d\n", cross);
break;
}
}
static void secdp_ptn36502_onoff(struct dp_power_private *power, bool enable, int lane)
{
DP_DEBUG("en:%d, lane:%d\n", enable, lane);
if (enable) {
int val = -1;
if (lane == 2)
ptn36502_config(DP2_LANE_USB3_MODE, 1);
else if (lane == 4)
ptn36502_config(DP4_LANE_MODE, 1);
else {
DP_ERR("error! unknown lane: %d\n", lane);
goto exit;
}
val = ptn36502_i2c_read(Chip_ID);
DP_INFO("Chip_ID: 0x%x\n", val);
val = ptn36502_i2c_read(Chip_Rev);
DP_INFO("Chip_Rev: 0x%x\n", val);
} else {
ptn36502_config(SAFE_STATE, 0);
}
exit:
return;
}
static void secdp_ptn36502_notify_linkinfo(struct dp_power_private *power, u32 bw_code, u8 v_level, u8 p_level)
{
DP_ENTER("0x%x,%d,%d, do nothing!\n", bw_code, v_level, p_level);
//.TODO:
}
#elif IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
static void secdp_ps5169_aux_ctrl(struct dp_power_private *power, int cross)
{
/*
* ps5169 does not support AUX switching function.
* It needs to be done by AUX switch IC
*/
DP_ENTER("cross: %s, do nothing!\n",
secdp_redriver_switch_to_string(cross));
}
static void secdp_ps5169_onoff(struct dp_power_private *power, bool enable, int lane)
{
DP_DEBUG("en:%d, lane:%d\n", enable, lane);
if (enable) {
if (lane == 2)
ps5169_config(DP2_LANE_USB_MODE, 1);
else if (lane == 4)
ps5169_config(DP_ONLY_MODE, 1);
else {
DP_ERR("error! unknown lane: %d\n", lane);
goto exit;
}
DP_INFO("Chip_ID1: 0x%x, Chip_Rev1: 0x%x\n",
ps5169_i2c_read(Chip_ID1), ps5169_i2c_read(Chip_Rev1));
DP_INFO("Chip_ID2: 0x%x, Chip_Rev2: 0x%x\n",
ps5169_i2c_read(Chip_ID2), ps5169_i2c_read(Chip_Rev2));
} else {
ps5169_config(CLEAR_STATE, 0);
}
exit:
return;
}
static void secdp_ps5169_notify_linkinfo(struct dp_power_private *power,
u32 bw_code, u8 v_level, u8 p_level)
{
struct dp_parser *parser = power->parser;
u8 eq0, eq1;
switch (bw_code) {
case DP_LINK_BW_RBR:
eq0 = parser->ps5169_rbr_eq0[v_level][p_level];
eq1 = parser->ps5169_rbr_eq1[v_level][p_level];
break;
case DP_LINK_BW_HBR:
eq0 = parser->ps5169_hbr_eq0[v_level][p_level];
eq1 = parser->ps5169_hbr_eq1[v_level][p_level];
break;
case DP_LINK_BW_HBR2:
eq0 = parser->ps5169_hbr2_eq0[v_level][p_level];
eq1 = parser->ps5169_hbr2_eq1[v_level][p_level];
break;
case DP_LINK_BW_HBR3:
default:
eq0 = parser->ps5169_hbr3_eq0[v_level][p_level];
eq1 = parser->ps5169_hbr3_eq1[v_level][p_level];
break;
}
DP_DEBUG("bw:0x%x, v:%d, p:%d, eq0:0x%x, eq1:0x%x\n",
bw_code, v_level, p_level, eq0, eq1);
ps5169_notify_dplink(eq0, eq1);
}
#endif
void secdp_redriver_onoff(struct dp_power *dp_power,
bool enable, int lane)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
if (power && power->redrv_onoff)
power->redrv_onoff(power, enable, lane);
}
void secdp_redriver_linkinfo(struct dp_power *dp_power,
u32 rate, u8 v_level, u8 p_level)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
if (power && power->redrv_notify_linkinfo)
power->redrv_notify_linkinfo(power, rate, v_level, p_level);
}
static void secdp_redriver_register(struct dp_power_private *power)
{
int use_redrv;
if (!power || !power->parser) {
DP_ERR("invalid power!\n");
goto end;
}
use_redrv = power->parser->use_redrv;
DP_DEBUG("++ use_redrv(%d)\n", use_redrv);
if (!use_redrv) {
DP_INFO("nothing registered!\n");
goto end;
}
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502)
power->redrv_onoff = secdp_ptn36502_onoff;
power->redrv_aux_ctrl = secdp_ptn36502_aux_ctrl;
power->redrv_notify_linkinfo = secdp_ptn36502_notify_linkinfo;
DP_INFO("ptn36502 API registered!\n");
#elif IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
power->redrv_onoff = secdp_ps5169_onoff;
power->redrv_aux_ctrl = secdp_ps5169_aux_ctrl;
power->redrv_notify_linkinfo = secdp_ps5169_notify_linkinfo;
DP_INFO("ps5169 API registered!\n");
#endif
end:
return;
}
#if !IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
/* turn on EDP_AUX switch
* ===================================================
* | usbplug-cc(dir) | orientation | flip | aux-sel |
* ===================================================
* | 0 | CC1 | false | 0 |
* | 1 | CC2 | true | 1 |
* ===================================================
*/
static void _secdp_power_set_gpio(struct dp_power_private *power, bool flip)
{
int i, rc = 0;
/*int dir = (flip == false) ? 0 : 1;*/
struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
struct dss_gpio *config = mp->gpio_config;
struct dp_parser *parser = power->parser;
bool sel_val = false;
// DP_DEBUG("flip:%d, aux_inv:%d, redrv:%d\n",
// flip, parser->aux_sel_inv, parser->use_redrv);
if (parser->aux_sel_inv)
sel_val = true;
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-sel")) {
if (parser->use_redrv == SECDP_REDRV_PTN36502) {
rc = gpio_direction_output(config->gpio, 0);
} else {
/* SECDP_REDRV_PS5169 or SECDP_REDRV_NONE */
bool val = (bool)gpio_get_value(config->gpio);
if ((!flip && (val == sel_val)) ||
(flip && (val == !sel_val))) {
DP_DEBUG("%s: already %d %d, skip\n",
config->gpio_name, flip, val);
break;
}
rc = gpio_direction_output(config->gpio,
(!flip ? sel_val : !sel_val));
}
usleep_range(100, 120);
DP_INFO("[aux-sel] set %d (f:%d,i:%d,r:%d) %d\n",
gpio_get_value(config->gpio),
flip, parser->aux_sel_inv, parser->use_redrv, rc);
break;
}
}
config++;
}
usleep_range(100, 120);
config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-en")) {
if (!gpio_get_value(config->gpio)) {
DP_DEBUG("%s: already enabled, skip\n",
config->gpio_name);
break;
}
rc = gpio_direction_output(config->gpio, 0);
usleep_range(100, 120);
DP_INFO("[aux-en] set %d (f:%d,i:%d,r:%d) %d\n",
gpio_get_value(config->gpio),
flip, parser->aux_sel_inv, parser->use_redrv, rc);
break;
}
}
config++;
}
}
/* turn off EDP_AUX switch */
static void _secdp_power_unset_gpio(struct dp_power_private *power)
{
int i, rc = 0;
struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
struct dss_gpio *config = mp->gpio_config;
DP_ENTER("\n");
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-en")) {
if (gpio_get_value(config->gpio)) {
DP_DEBUG("%s: already disabled, skip\n",
config->gpio_name);
break;
}
rc = gpio_direction_output(config->gpio, 1);
usleep_range(100, 120);
DP_INFO("[aux-en] set %d, %d\n",
gpio_get_value(config->gpio), rc);
break;
}
}
config++;
}
config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-sel")) {
if (!gpio_get_value(config->gpio)) {
DP_DEBUG("%s: already 0, skip\n",
config->gpio_name);
break;
}
rc = gpio_direction_output(config->gpio, 0);
usleep_range(100, 120);
DP_INFO("[aux-sel] set %d, %d\n",
gpio_get_value(config->gpio), rc);
break;
}
}
config++;
}
}
#else/*CONFIG_SBU_SWITCH_CONTROL*/
static void _secdp_sbu_switch_on(struct dp_power_private *power, bool flip)
{
int cc_sbu = !flip ? CLOSE_SBU_CC1_ACTIVE : CLOSE_SBU_CC2_ACTIVE;
if (power->sbu_status == cc_sbu)
return;
usbpd_sbu_switch_control(cc_sbu);
power->sbu_status = cc_sbu;
}
static void _secdp_sbu_switch_off(struct dp_power_private *power)
{
if (power->sbu_status == OPEN_SBU)
return;
usbpd_sbu_switch_control(OPEN_SBU);
power->sbu_status = OPEN_SBU;
}
#endif
void secdp_power_set_gpio(struct dp_power *dp_power, bool flip)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
#if !IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
_secdp_power_set_gpio(power, flip);
#else
_secdp_sbu_switch_on(power, flip);
#endif
}
void secdp_power_unset_gpio(struct dp_power *dp_power)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
#if !IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
_secdp_power_unset_gpio(power);
#else
_secdp_sbu_switch_off(power);
#endif
}
#if defined(CONFIG_SECDP_FACTORY_DPSWITCH_TEST)
static void secdp_redriver_aux_ctrl(struct dp_power_private *power,
int cross)
{
if (power && power->redrv_aux_ctrl)
power->redrv_aux_ctrl(power, cross);
}
/*
* @aux_sel : 1 or 0
*/
void secdp_config_gpios_factory(struct dp_power *dp_power, int aux_sel, bool on)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
DP_DEBUG("sel:%d, on:%d\n", aux_sel, on);
if (on) {
secdp_aux_pullup_vreg_enable(power, true);
secdp_power_set_gpio(dp_power, aux_sel);
if (aux_sel == 1)
secdp_redriver_aux_ctrl(power, REDRIVER_SWITCH_CROSS);
else if (aux_sel == 0)
secdp_redriver_aux_ctrl(power, REDRIVER_SWITCH_THROU);
else
DP_ERR("unknown <%d>\n", aux_sel);
} else {
secdp_redriver_aux_ctrl(power, REDRIVER_SWITCH_RESET);
secdp_power_unset_gpio(dp_power);
secdp_aux_pullup_vreg_enable(power, false);
}
}
#endif
enum dp_hpd_plug_orientation secdp_get_plug_orientation(struct dp_power *dp_power)
{
struct dp_power_private *power;
struct dp_parser *parser;
struct dss_module_power *mp;
struct dss_gpio *config;
int i, dir;
power = container_of(dp_power, struct dp_power_private, dp_power);
parser = power->parser;
mp = &power->parser->mp[DP_CORE_PM];
config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name,
"usbplug-cc")) {
dir = gpio_get_value(config->gpio);
if (parser->cc_dir_inv)
dir = !dir;
DP_INFO("cc_dir_inv:%d, orientation:%s\n",
parser->cc_dir_inv, !dir ? "CC1" : "CC2");
if (dir == 0)
return ORIENTATION_CC1;
else /* if (dir == 1) */
return ORIENTATION_CC2;
}
}
config++;
}
/*cannot be here*/
return ORIENTATION_NONE;
}
#endif
static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
bool enable)
{
#if !defined(CONFIG_SECDP)
int rc = 0, i;
struct dss_module_power *mp;
struct dss_gpio *config;
@ -613,6 +1222,14 @@ static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
}
}
}
#else
struct dp_power *dp_power = &power->dp_power;
if (enable)
secdp_power_set_gpio(dp_power, flip);
else
secdp_power_unset_gpio(dp_power);
#endif
return 0;
}
@ -666,8 +1283,19 @@ static int dp_power_client_init(struct dp_power *dp_power,
dp_power->phandle = phandle;
dp_power->drm_dev = drm_dev;
#if defined(CONFIG_SECDP)
rc = dp_power_pinctrl_set(power, false);
if (rc) {
DP_ERR("failed to set pinctrl state\n");
goto error_client;
}
#endif
return 0;
#if defined(CONFIG_SECDP)
error_client:
dp_power_clk_init(power, false);
#endif
error_clk:
dp_power_regulator_deinit(power);
error_power:
@ -920,6 +1548,11 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll)
DP_DEBUG("Optional GDSC regulator is missing\n");
}
#if defined(CONFIG_SECDP)
secdp_redriver_register(power);
mutex_init(&power->dp_clk_lock);
#endif
return dp_power;
error:
return ERR_PTR(rc);

View File

@ -66,4 +66,20 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll);
* @power: pointer to the power module's data
*/
void dp_power_put(struct dp_power *power);
#if defined(CONFIG_SECDP)
enum dp_hpd_plug_orientation secdp_get_plug_orientation(struct dp_power *dp_power);
int secdp_power_request_gpios(struct dp_power *dp_power);
void secdp_power_set_gpio(struct dp_power *dp_power, bool flip);
void secdp_power_unset_gpio(struct dp_power *dp_power);
#if defined(CONFIG_SECDP_FACTORY_DPSWITCH_TEST)
void secdp_config_gpios_factory(struct dp_power *dp_power, int aux_sel, bool out_en);
#endif
void secdp_redriver_onoff(struct dp_power *dp_power, bool enable, int lane);
void secdp_redriver_linkinfo(struct dp_power *dp_power, u32 rate, u8 v_level, u8 p_level);
#endif/*CONFIG_SECDP*/
#endif /* _DP_POWER_H_ */

View File

@ -0,0 +1,432 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef __SECDP_H
#define __SECDP_H
#if IS_ENABLED(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
#include <linux/usb/typec/manager/usb_typec_manager_notifier.h>
#endif
#if IS_ENABLED(CONFIG_PDIC_NOTIFIER)
#include <linux/usb/typec/common/pdic_notifier.h>
#endif
#include <linux/secdp_logger.h>
#include <linux/pm_wakeup.h>
#include <linux/sched/clock.h>
/*#define MODEM_RF_INFO*/
#if defined(MODEM_RF_INFO) && IS_ENABLED(CONFIG_DEV_RIL_BRIDGE)
#include <linux/dev_ril_bridge.h>
struct rf_information {
u8 rat;
u32 band;
u32 arfcn;
} __packed;
#endif
#include "dp_hpd.h"
#include "dp_power.h"
#include "dp_panel.h"
#include "dp_catalog.h"
#include "dp_parser.h"
#include "dp_link.h"
#include "secdp_sysfs.h"
/*defined at kmodule/usb/typec/common/pdic_core.h*/
#define SAMSUNG_VENDOR_ID 0x04E8
#define DEXDOCK_PRODUCT_ID 0xA020 /* EE-MG950 DeX Station */
#define DEXPAD_PRODUCT_ID 0xA029 /* EE-M5100 DeX Pad */
#define DEXCABLE_PRODUCT_ID 0xA048 /* EE-I3100 DeX Cable */
#define HG950_PRODUCT_ID 0xA025 /* EE-HG950 HDMI Adapter */
#define MPA2_PRODUCT_ID 0xA027 /* EE-P5000 Multiport Adapter */
#define MPA3_PRODUCT_ID 0xA056 /* EE-P3200 Multiport Adapter */
#define MPA4_PRODUCT_ID 0xA066 /* EE-P5400 Multiport Adapter */
#define SECDP_ENUM_STR(x) #x
#define SECDP_USB_CONCURRENCY
#define SECDP_USE_WAKELOCK
#define SECDP_MAX_HBR2
/*#define SECDP_AUDIO_CTS*/
/*#define SECDP_HDCP_DISABLE*/
/*#define SECDP_TEST_HDCP2P2_REAUTH*/
/*#define NOT_SUPPORT_DEX_RES_CHANGE*/
#define REMOVE_YUV420_AT_PREFER
#define SYSFS_BW_CODE
#define DPCD_IEEE_OUI 0x500
#define DPCD_DEVID_STR 0x503
#define LEN_BRANCH_REV 3
#define DPCD_BRANCH_HW_REV 0x509
#define DPCD_BRANCH_SW_REV_MAJOR 0x50A
#define DPCD_BRANCH_SW_REV_MINOR 0x50B
#define MAX_CNT_LINK_STATUS_UPDATE 4
#define MAX_CNT_HDCP_RETRY 10
/* MST: max resolution, max refresh rate, max pclk */
#define MST_MAX_COLS 3840
#define MST_MAX_ROWS 2160
#define MST_MAX_FPS 30
#define MST_MAX_PCLK 300000
#define PDIC_DP_NOTI_REG_DELAY 1000
/* displayport self test */
#if defined(CONFIG_SECDP_DBG)
#define SECDP_SELF_TEST
#endif
#ifdef SECDP_SELF_TEST
#define ST_EDID_SIZE 256
#define ST_ARG_CNT 20
#define ST_TEST_EXIT 555
enum {
ST_CLEAR_CMD,
ST_LANE_CNT,
ST_LINK_RATE,
ST_CONNECTION_TEST,
ST_HDCP_TEST,
ST_PREEM_TUN,
ST_VOLTAGE_TUN,
ST_MAX,
};
struct secdp_sef_test_item {
char cmd_str[20];
int arg[ST_ARG_CNT];
int arg_cnt;
char arg_str[100];
bool enabled;
void (*clear)(void);
};
int secdp_self_test_status(int cmd);
void secdp_self_test_start_reconnect(struct secdp_sysfs *dp_sysfs, void (*func)(struct secdp_misc *sec));
void secdp_self_test_start_hdcp_test(struct secdp_sysfs *dp_sysfs, void (*func_on)(void),
void (*func_off)(void));
//void secdp_self_register_clear_func(int cmd, void (*func)(void));
int *secdp_self_test_get_arg(int cmd);
#endif/*SECDP_SELF_TEST*/
/* monitor aspect ratio */
enum mon_aspect_ratio_t {
MON_RATIO_NA = -1,
MON_RATIO_3_2,
MON_RATIO_4_3,
MON_RATIO_5_3,
MON_RATIO_5_4,
MON_RATIO_8_5,
MON_RATIO_10P5_9,
MON_RATIO_11_10,
MON_RATIO_16_9,
MON_RATIO_16_10,
MON_RATIO_21_9,
MON_RATIO_21_10,
MON_RATIO_32_9,
MON_RATIO_32_10,
};
static inline char *secdp_aspect_ratio_to_string(enum mon_aspect_ratio_t ratio)
{
switch (ratio) {
case MON_RATIO_3_2: return DP_ENUM_STR(MON_RATIO_3_2);
case MON_RATIO_4_3: return DP_ENUM_STR(MON_RATIO_4_3);
case MON_RATIO_5_3: return DP_ENUM_STR(MON_RATIO_5_3);
case MON_RATIO_5_4: return DP_ENUM_STR(MON_RATIO_5_4);
case MON_RATIO_8_5: return DP_ENUM_STR(MON_RATIO_8_5);
case MON_RATIO_10P5_9: return DP_ENUM_STR(MON_RATIO_10P5_9);
case MON_RATIO_11_10: return DP_ENUM_STR(MON_RATIO_11_10);
case MON_RATIO_16_9: return DP_ENUM_STR(MON_RATIO_16_9);
case MON_RATIO_16_10: return DP_ENUM_STR(MON_RATIO_16_10);
case MON_RATIO_21_9: return DP_ENUM_STR(MON_RATIO_21_9);
case MON_RATIO_21_10: return DP_ENUM_STR(MON_RATIO_21_10);
case MON_RATIO_32_9: return DP_ENUM_STR(MON_RATIO_32_9);
case MON_RATIO_32_10: return DP_ENUM_STR(MON_RATIO_32_10);
case MON_RATIO_NA: return DP_ENUM_STR(MON_RATIO_NA);
default: return "unknown";
}
}
/* adapter type : SST or MST */
enum secdp_adapter_t {
SECDP_ADT_UNKNOWN = -1,
SECDP_ADT_SST = 10,
SECDP_ADT_MST = 11,
};
/* dex supported resolutions */
enum dex_support_res_t {
DEX_RES_NOT_SUPPORT = 0,
DEX_RES_1600X900, /* HD+ */
DEX_RES_1920X1080, /* FHD */
DEX_RES_1920X1200, /* WUXGA */
DEX_RES_2560X1080, /* UW-UXGA */
DEX_RES_2560X1440, /* QHD */
DEX_RES_2560X1600, /* WQXGA */
DEX_RES_3440X1440, /* UW-QHD */
DEX_RES_END,
};
#define DEX_RES_DFT DEX_RES_1920X1080 /* DeX default timing */
#define DEX_DFT_COL 1920
#define DEX_DFT_ROW 1080
#define DEX_RES_MAX DEX_RES_3440X1440 /* DeX max timing */
#define DEX_MAX_COL 3440
#define DEX_MAX_ROW 1440
#define DEX_REFRESH_MIN 50
#define DEX_REFRESH_MAX 60
#define MIRROR_REFRESH_MIN 24
static inline char *secdp_dex_res_to_string(int res)
{
switch (res) {
case DEX_RES_NOT_SUPPORT:
return DP_ENUM_STR(DEX_RES_NOT_SUPPORT);
case DEX_RES_1600X900:
return DP_ENUM_STR(DEX_RES_1600X900);
case DEX_RES_1920X1080:
return DP_ENUM_STR(DEX_RES_1920X1080);
case DEX_RES_1920X1200:
return DP_ENUM_STR(DEX_RES_1920X1200);
case DEX_RES_2560X1080:
return DP_ENUM_STR(DEX_RES_2560X1080);
case DEX_RES_2560X1440:
return DP_ENUM_STR(DEX_RES_2560X1440);
case DEX_RES_2560X1600:
return DP_ENUM_STR(DEX_RES_2560X1600);
case DEX_RES_3440X1440:
return DP_ENUM_STR(DEX_RES_3440X1440);
default:
return "unknown";
}
}
enum DEX_STATUS {
DEX_DISABLED = 0,
DEX_ENABLED,
DEX_MODE_CHANGING,
};
/** redriver devices */
enum secdp_redrv_dev {
SECDP_REDRV_NONE = 0,
SECDP_REDRV_PTN36502, /* don't need AUX_SEL control */
SECDP_REDRV_PS5169, /* need AUX_SEL control */
};
static inline char *secdp_redrv_to_string(int res)
{
switch (res) {
case SECDP_REDRV_NONE:
return DP_ENUM_STR(SECDP_REDRV_NONE);
case SECDP_REDRV_PTN36502:
return DP_ENUM_STR(SECDP_REDRV_PTN36502);
case SECDP_REDRV_PS5169:
return DP_ENUM_STR(SECDP_REDRV_PS5169);
default:
return "unknown";
}
}
struct secdp_adapter {
uint ven_id;
uint prod_id;
char ieee_oui[4]; /* DPCD 500h ~ 502h */
char devid_str[7]; /* DPCD 503h ~ 508h */
char fw_ver[10]; /* firmware ver, 0:h/w, 1:s/w major, 2:s/w minor */
bool ss_genuine;
bool ss_legacy;
enum dex_support_res_t dex_type;
};
#define MON_NAME_LEN 14 /* monitor name length, max 13 chars + null */
#define MAX_NUM_HMD 32
#define DEX_TAG_HMD "HMD"
struct secdp_sink_dev {
uint ven_id; /* vendor id from PDIC */
uint prod_id; /* product id from PDIC */
char monitor_name[MON_NAME_LEN]; /* from EDID */
};
struct secdp_pdic_noti {
struct delayed_work reg_work;
struct notifier_block nb;
bool registered;
bool reset; /* true if PDIC or SSUSB get reset after DP connection */
};
struct secdp_prefer {
enum mon_aspect_ratio_t ratio;
bool exist; /* true if preferred resolution */
int hdisp; /* horizontal pixel of preferred resolution */
int vdisp; /* vertical pixel of preferred resolution */
int refresh; /* refresh rate of preferred resolution */
};
struct secdp_dex {
struct class *sysfs_class;
enum dex_support_res_t res; /* dex supported resolution */
enum DEX_STATUS prev; /* previously known as "dex_now" */
enum DEX_STATUS curr; /* previously known as "dex_en" */
int setting_ui; /* "dex_set", true if setting has Dex mode */
bool ignore_prefer_ratio; /* true if prefer ratio does not match to dex ratio */
bool adapter_check_skip;
/*
* 2 if resolution is changed during dex mode change.
* And once dex framework reads the dex_node_stauts using dex node,
* it's assigned to same value with curr.
*/
enum DEX_STATUS status; /* previously known as "dex_node_status" */
bool reconnecting; /* true if dex is under reconnecting */
};
struct secdp_display_timing {
u32 active_h;
u32 active_v;
u32 refresh_rate;
bool interlaced;
int clock; /* pixel clock, refer to "struct drm_display_mode" */
enum dex_support_res_t dex_res; /* dex supported resolution */
enum mon_aspect_ratio_t mon_ratio; /* monitor aspect ratio */
int supported; /* for unit test */
u64 total;
};
struct secdp_hmd {
struct secdp_sink_dev list[MAX_NUM_HMD]; /* supported HMD dev list */
struct mutex lock;
bool exist; /* true if connected sink is known HMD device */
};
struct secdp_hdcp {
struct delayed_work start_work;
int retry; /* count if dp link is unstable during hdcp */
};
struct secdp_hpd {
struct delayed_work noti_work;
bool noti_deferred;
atomic_t val; /* 1 if hpd high, 0 if hpd low" */
bool prev_evt;
};
struct secdp_debug {
bool prefer_check_skip;
};
struct secdp_misc {
struct delayed_work link_status_work;
struct delayed_work link_backoff_work;
bool backoff_start;
struct delayed_work poor_discon_work;
struct device *uevent_dev;
#ifdef MODEM_RF_INFO
struct rf_information rf_info;
struct notifier_block modem_rfinfo_nb;
#endif
bool extdisp_off;
bool cable_connected; /* previously known as "cable_connected_phy" */
bool link_conf; /* previously known as "sec_link_conf" */
struct secdp_hpd hpd;
int mode_cnt;
struct secdp_adapter adapter;
struct secdp_pdic_noti pdic_noti;
struct secdp_display_timing prf_timing; /* preferred timing */
struct secdp_display_timing mrr_timing; /* max "mirror" timing */
struct secdp_display_timing dex_timing; /* max "dex" timing */
struct secdp_prefer prefer;
struct secdp_hdcp hdcp;
struct secdp_debug debug;
struct secdp_sysfs *sysfs;
struct secdp_dex dex;
struct secdp_hmd hmd;
struct completion dp_off_comp;
struct completion dp_discon_comp;
bool dp_disconnecting;
bool lpm_booting;
struct mutex notify_lock;
struct mutex attention_lock;
struct mutex notifier_lock;
atomic_t noti_status;
struct notifier_block reboot_nb;
bool reboot; /* true if rebooted or shutdown */
#ifdef SECDP_USE_WAKELOCK
struct wakeup_source *ws;
#endif
#ifdef SECDP_SELF_TEST
struct delayed_work self_test_reconnect_work;
struct delayed_work self_test_hdcp_test_work;
void (*self_test_reconnect_cb)(struct secdp_misc *sec);
void (*self_test_hdcp_on_cb)(void);
void (*self_test_hdcp_off_cb)(void);
#endif
};
bool secdp_adapter_check_parade(struct secdp_misc *sec);
bool secdp_adapter_check_ps176(struct secdp_misc *sec);
bool secdp_adapter_check_ps176_legacy(struct secdp_misc *sec);
bool secdp_adapter_check_realtek(struct secdp_misc *sec);
bool secdp_get_lpm_mode(struct secdp_misc *sec);
int secdp_send_deferred_hpd_noti(struct secdp_misc *sec);
int secdp_pdic_noti_register_ex(struct secdp_misc *sec, bool retry);
bool secdp_phy_reset_check(void);
bool secdp_get_power_status(void);
bool secdp_get_cable_status(void);
bool secdp_get_hpd_irq_status(void);
int secdp_get_hpd_status(void);
struct drm_connector *secdp_get_connector(void);
bool secdp_get_reboot_status(void);
bool secdp_check_hmd_dev(struct secdp_misc *sec, const char *name_to_search);
int secdp_store_hmd_dev(struct secdp_misc *sec, char *buf, size_t len, int num);
void secdp_timing_init(struct secdp_misc *sec);
void secdp_extdisp_on(struct secdp_misc *sec);
void secdp_extdisp_off(struct secdp_misc *sec);
void secdp_reconnect(struct secdp_misc *sec);
bool secdp_check_reconnect(struct secdp_misc *sec);
void secdp_link_backoff_start(void);
void secdp_link_backoff_stop(void);
bool secdp_adapter_is_legacy(void);
bool secdp_panel_hdr_supported(void);
#endif/*__SECDP_H*/

View File

@ -0,0 +1,360 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2021 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* DP bigdata
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/secdp_bigdata.h>
#define EDID_BUF_SIZE 512
#define ERR_DATA_BUF_SIZE 1024
#define COL_NAME_SIZE 20
enum DP_ITEM_TYPE {
INT = 1,
HEX = 2,
STR = 4,
CHR = 8,
ERR = 16,
};
enum DP_STATUS {
STATUS_NO_CONNECTION,
STATUS_CONNECTION,
STATUS_ERROR_OCCURRED,
};
struct bd_item_info {
char name[COL_NAME_SIZE];
char type;
void *data;
int str_max_len;
};
struct bd_error_data {
int limit;
int count;
};
static char err_data_buf[ERR_DATA_BUF_SIZE];
static struct bd_item_info item_to_column[BD_ITEM_MAX];
static enum DP_STATUS dp_status;
static void secdp_bigdata_save_data(void);
static void secdp_bigdata_init_item(enum DP_BD_ITEM_LIST item, char *col_name, enum DP_ITEM_TYPE type, ...);
static void secdp_bigdata_init_error(enum DP_BD_ITEM_LIST item, char *col_name, int err_limit);
static void secdp_bigdata_save_item_int(enum DP_BD_ITEM_LIST item, int val);
static void secdp_bigdata_save_item_hex(enum DP_BD_ITEM_LIST item, int val);
static void secdp_bigdata_save_item_char(enum DP_BD_ITEM_LIST item, char val);
static void secdp_bigdata_save_item_str(enum DP_BD_ITEM_LIST item, char *val);
ssize_t _secdp_bigdata_show(struct class *class,
struct class_attribute *attr, char *buf)
{
if (dp_status == STATUS_NO_CONNECTION)
return 0;
return scnprintf(buf, ERR_DATA_BUF_SIZE, "%s", err_data_buf);
}
ssize_t _secdp_bigdata_store(struct class *dev,
struct class_attribute *attr, const char *buf, size_t size)
{
if ((buf[0] | 0x20) == 'c')
dp_status = STATUS_NO_CONNECTION;
return size;
}
void secdp_bigdata_init(struct class *dp_class)
{
secdp_bigdata_init_item(BD_LINK_CONFIGURE, "LINK_CFG", CHR);
secdp_bigdata_init_item(BD_ADAPTER_HWID, "ADT_HWID", HEX);
secdp_bigdata_init_item(BD_ADAPTER_FWVER, "ADT_FWVER", HEX);
secdp_bigdata_init_item(BD_ADAPTER_TYPE, "ADT_TYPE", STR, 20);
secdp_bigdata_init_item(BD_MAX_LANE_COUNT, "MLANE_CNT", INT);
secdp_bigdata_init_item(BD_MAX_LINK_RATE, "MLINK_RATE", INT);
secdp_bigdata_init_item(BD_CUR_LANE_COUNT, "CLANE_CNT", INT);
secdp_bigdata_init_item(BD_CUR_LINK_RATE, "CLINK_RATE", INT);
secdp_bigdata_init_item(BD_HDCP_VER, "HDCP_VER", STR, 10);
secdp_bigdata_init_item(BD_ORIENTATION, "ORIENTATION", STR, 10);
secdp_bigdata_init_item(BD_RESOLUTION, "RESOLUTION", STR, 20);
secdp_bigdata_init_item(BD_EDID, "EDID", STR, EDID_BUF_SIZE);
secdp_bigdata_init_item(BD_ADT_VID, "ADT_VID", HEX);
secdp_bigdata_init_item(BD_ADT_PID, "ADT_PID", HEX);
secdp_bigdata_init_item(BD_DP_MODE, "DP_MODE", STR, 10);
secdp_bigdata_init_item(BD_SINK_NAME, "SINK_NAME", STR, 14);
secdp_bigdata_init_item(BD_AUD_CH, "AUD_CH", INT);
secdp_bigdata_init_item(BD_AUD_FREQ, "AUD_FREQ", INT);
secdp_bigdata_init_item(BD_AUD_BIT, "AUD_BIT", INT);
secdp_bigdata_init_error(ERR_AUX, "ERR_AUX", 3);
secdp_bigdata_init_error(ERR_EDID, "ERR_EDID", 1);
secdp_bigdata_init_error(ERR_HDCP_AUTH, "ERR_HDCP", 5);
secdp_bigdata_init_error(ERR_LINK_TRAIN, "ERR_LT_TRAIN", 1);
secdp_bigdata_init_error(ERR_INF_IRQHPD, "ERR_INF_IRQHPD", 10);
}
static void secdp_bigdata_init_item_str(enum DP_BD_ITEM_LIST item, char *val, int max_len)
{
kfree(item_to_column[item].data);
item_to_column[item].data = kzalloc(max_len + 1, GFP_KERNEL);
if (!item_to_column[item].data)
return;
item_to_column[item].str_max_len = max_len;
strlcpy((char *)item_to_column[item].data, val, max_len + 1);
}
static void secdp_bigdata_init_item(enum DP_BD_ITEM_LIST item, char *col_name, enum DP_ITEM_TYPE type, ...)
{
va_list vl;
va_start(vl, type);
strlcpy(item_to_column[item].name, col_name, COL_NAME_SIZE);
item_to_column[item].type = type;
switch (type) {
case INT:
case HEX:
secdp_bigdata_save_item_int(item, -1);
break;
case STR:
secdp_bigdata_init_item_str(item, "X", (int)va_arg(vl, int));
break;
case CHR:
secdp_bigdata_save_item_char(item, 'X');
break;
default:
break;
}
va_end(vl);
}
static void secdp_bigdata_init_error(enum DP_BD_ITEM_LIST item, char *col_name, int err_limit)
{
struct bd_error_data *err = kzalloc(sizeof(struct bd_error_data), GFP_KERNEL);
if (err)
err->limit = err_limit;
strlcpy(item_to_column[item].name, col_name, COL_NAME_SIZE);
item_to_column[item].type = ERR;
item_to_column[item].data = err;
}
static void secdp_bigdata_save_item_int(enum DP_BD_ITEM_LIST item, int val)
{
if (!item_to_column[item].data) {
item_to_column[item].data = kzalloc(sizeof(int), GFP_KERNEL);
if (!item_to_column[item].data)
return;
}
*((int *)item_to_column[item].data) = val;
}
static void secdp_bigdata_save_item_hex(enum DP_BD_ITEM_LIST item, int val)
{
secdp_bigdata_save_item_int(item, val);
}
static void secdp_bigdata_save_item_char(enum DP_BD_ITEM_LIST item, char val)
{
if (!item_to_column[item].data) {
item_to_column[item].data = kzalloc(sizeof(char), GFP_KERNEL);
if (!item_to_column[item].data)
return;
}
*((char *)item_to_column[item].data) = val;
}
static void secdp_bigdata_save_item_str(enum DP_BD_ITEM_LIST item, char *val)
{
if (!item_to_column[item].data || !val)
return;
if (item == BD_EDID && val[0] != 'X') {
int ret = 0;
int i;
int ext_blk_cnt = val[0x7e] ? 1 : 0;
int edid_size = 128 * (ext_blk_cnt + 1);
for (i = 0; i < edid_size; i++) {
ret += scnprintf(((char *)item_to_column[item].data) + ret,
EDID_BUF_SIZE + 1 - ret, "%02x",
val[i]);
}
} else {
strlcpy((char *)item_to_column[item].data, val,
item_to_column[item].str_max_len + 1);
}
}
void secdp_bigdata_save_item(enum DP_BD_ITEM_LIST item, ...)
{
va_list vl;
if (item >= BD_ITEM_MAX || item < 0)
return;
va_start(vl, item);
switch (item_to_column[item].type) {
case INT:
secdp_bigdata_save_item_hex(item, (int)va_arg(vl, int));
break;
case HEX:
secdp_bigdata_save_item_int(item, (int)va_arg(vl, int));
break;
case STR:
secdp_bigdata_save_item_str(item, (char *)va_arg(vl, char *));
break;
case CHR:
secdp_bigdata_save_item_char(item, (char)va_arg(vl, int));
break;
default:
break;
}
va_end(vl);
}
void secdp_bigdata_inc_error_cnt(enum DP_BD_ITEM_LIST err)
{
if (err >= BD_ITEM_MAX || err < 0)
return;
if (item_to_column[err].data && item_to_column[err].type == ERR)
((struct bd_error_data *)item_to_column[err].data)->count++;
}
void secdp_bigdata_clr_error_cnt(enum DP_BD_ITEM_LIST err)
{
if (err >= BD_ITEM_MAX || err < 0)
return;
if (item_to_column[err].data && item_to_column[err].type == ERR)
((struct bd_error_data *)item_to_column[err].data)->count = 0;
}
static void secdp_bigdata_save_data(void)
{
int i;
int ret = 0;
for (i = 0; i < BD_ITEM_MAX; i++) {
switch (item_to_column[i].type) {
case INT:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%d\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
*((int *)item_to_column[i].data) : -1);
break;
case HEX:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"0x%x\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
*((int *)item_to_column[i].data) : -1);
break;
case STR:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%s\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
(char *)item_to_column[i].data : "X");
break;
case CHR:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%c\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
*((char *)item_to_column[i].data) : 'X');
break;
case ERR:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%d\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
((struct bd_error_data *)item_to_column[i].data)->count : 0);
break;
default:
break;
}
}
if (ret > 0)
err_data_buf[ret - 1] = '\n';
}
static int secdp_bigdata_check_err(void)
{
int i;
struct bd_error_data *e_data;
for (i = 0; i < BD_ITEM_MAX; i++) {
if (item_to_column[i].type == ERR) {
e_data = item_to_column[i].data;
if (e_data != NULL && e_data->count >= e_data->limit)
return 1;
}
}
return 0;
}
void secdp_bigdata_connection(void)
{
int i;
if (dp_status != STATUS_ERROR_OCCURRED)
dp_status = STATUS_CONNECTION;
for (i = 0; i < BD_ITEM_MAX; i++) {
switch (item_to_column[i].type) {
case INT:
case HEX:
secdp_bigdata_save_item_int(i, -1);
break;
case STR:
secdp_bigdata_save_item_str(i, "X");
break;
case CHR:
secdp_bigdata_save_item_char(i, 'X');
break;
case ERR:
secdp_bigdata_clr_error_cnt(i);
break;
default:
break;
}
}
}
void secdp_bigdata_disconnection(void)
{
if (secdp_bigdata_check_err()) {
dp_status = STATUS_ERROR_OCCURRED;
secdp_bigdata_save_data();
}
if (dp_status != STATUS_ERROR_OCCURRED)
secdp_bigdata_save_data();
}

View File

@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2022 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* SECDP logger
*
* 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/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/ktime.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
#include <linux/sched/clock.h>
#else
#include <linux/sched.h>
#endif
#include <linux/secdp_logger.h>
#include "secdp_unit_test.h"
#define BUF_SIZE SZ_64K
#define MAX_STR_LEN 160
#define PROC_FILE_NAME "dplog"
#define LOG_PREFIX "secdp"
static char log_buf[BUF_SIZE];
static unsigned int g_curpos;
static int is_secdp_logger_init;
static int is_buf_full;
static int log_max_count = -1;
static unsigned int max_mode_count;
static struct mutex dplog_lock;
static struct proc_dir_entry *g_entry;
static void dp_logger_print_date_time(void)
{
char tmp[64] = {0x0, };
struct tm tm;
struct timespec64 ts;
unsigned long sec;
ktime_get_real_ts64(&ts);
sec = ts.tv_sec - (sys_tz.tz_minuteswest * 60);
time64_to_tm(sec, 0, &tm);
snprintf(tmp, sizeof(tmp), "!@[%02d-%02d %02d:%02d:%02d.%03lu]",
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
ts.tv_nsec / 1000000);
secdp_logger_print("%s\n", tmp);
}
/* set max log count, if count is -1, no limit */
void secdp_logger_set_max_count(int count)
{
log_max_count = count;
dp_logger_print_date_time();
}
static void _secdp_logger_print(const char *fmt, va_list args)
{
int len;
char buf[MAX_STR_LEN] = {0, };
u64 time;
unsigned long nsec;
volatile unsigned int curpos;
mutex_lock(&dplog_lock);
time = local_clock();
nsec = do_div(time, 1000000000);
len = snprintf(buf, sizeof(buf), "[%5lu.%06ld] ",
(unsigned long)time, nsec / 1000);
len += vsnprintf(buf + len, MAX_STR_LEN - len, fmt, args);
if (len > MAX_STR_LEN)
len = MAX_STR_LEN;
curpos = g_curpos;
if (curpos + len >= BUF_SIZE) {
g_curpos = curpos = 0;
is_buf_full = 1;
}
memcpy(log_buf + curpos, buf, len);
g_curpos += len;
mutex_unlock(&dplog_lock);
}
void secdp_logger_print(const char *fmt, ...)
{
va_list args;
if (!is_secdp_logger_init)
return;
if (!log_max_count)
return;
if (log_max_count > 0)
log_max_count--;
va_start(args, fmt);
_secdp_logger_print(fmt, args);
va_end(args);
}
/* set max num of modes print */
void secdp_logger_set_mode_max_count(unsigned int num)
{
max_mode_count = num + 1;
}
void secdp_logger_dec_mode_count(void)
{
if (!is_secdp_logger_init)
return;
if (max_mode_count > 0)
max_mode_count--;
}
void secdp_logger_print_mode(const char *fmt, ...)
{
va_list args;
if (!is_secdp_logger_init)
return;
if (!max_mode_count)
return;
va_start(args, fmt);
_secdp_logger_print(fmt, args);
va_end(args);
}
void secdp_logger_hex_dump(void *buf, void *pref, size_t size)
{
uint8_t *ptr = buf;
size_t i;
char tmp[128] = {0x0, };
char *ptmp = tmp;
int len;
if (!is_secdp_logger_init)
return;
if (!log_max_count)
return;
if (log_max_count > 0)
log_max_count--;
for (i = 0; i < size; i++) {
len = snprintf(ptmp, 4, "%02x ", *ptr++);
ptmp = ptmp + len;
if (((i+1)%16) == 0) {
secdp_logger_print("%s%s\n", (char *)pref, tmp);
ptmp = tmp;
}
}
if (i % 16) {
len = ptmp - tmp;
tmp[len] = 0x0;
secdp_logger_print("%s%s\n", (char *)pref, tmp);
}
}
static ssize_t secdp_logger_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
loff_t pos = *offset;
ssize_t count;
size_t size;
volatile unsigned int curpos = g_curpos;
if (is_buf_full || BUF_SIZE <= curpos)
size = BUF_SIZE;
else
size = (size_t)curpos;
if (pos >= size)
return 0;
count = min(len, size);
if ((pos + count) > size)
count = size - pos;
if (copy_to_user(buf, log_buf + pos, count))
return -EFAULT;
*offset += count;
return count;
}
#if KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE
static const struct proc_ops secdp_logger_ops = {
.proc_read = secdp_logger_read,
.proc_lseek = default_llseek,
};
#else
static const struct file_operations secdp_logger_ops = {
.owner = THIS_MODULE,
.read = secdp_logger_read,
.llseek = default_llseek,
};
#endif
int secdp_logger_init(void)
{
struct proc_dir_entry *entry;
if (is_secdp_logger_init)
return 0;
entry = proc_create(PROC_FILE_NAME, 0444, NULL, &secdp_logger_ops);
if (!entry) {
pr_err("%s: failed to create proc entry\n", __func__);
return 0;
}
proc_set_size(entry, BUF_SIZE);
is_secdp_logger_init = 1;
g_entry = entry;
mutex_init(&dplog_lock);
secdp_logger_print("dp logger init ok\n");
return 0;
}
void secdp_logger_deinit(void)
{
if (!g_entry)
return;
proc_remove(g_entry);
is_secdp_logger_init = 0;
g_entry = NULL;
mutex_destroy(&dplog_lock);
secdp_logger_print("dp logger deinit ok\n");
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef __SECDP_SYSFS_H
#define __SECDP_SYSFS_H
#include "secdp.h"
struct secdp_sysfs_in {
struct device *dev;
struct dp_parser *parser;
struct dp_ctrl *ctrl;
struct dp_link *link;
struct dp_panel *panel;
struct dp_power *power;
struct dp_catalog *catalog;
struct secdp_misc *sec;
};
struct secdp_sysfs {
struct class dp_class;
};
/**
* secdp_sysfs_get() - get the functionalities of secdp sysfs module
*
*
* return: a pointer to dp_link struct
*/
struct secdp_sysfs *secdp_sysfs_get(struct secdp_sysfs_in *in);
/**
* secdp_sysfs_put() - releases the dp test module's resources
*
* @dp_link: an instance of dp_link module
*
*/
void secdp_sysfs_put(struct device *dev, struct secdp_sysfs *dp_sysfs);
#endif /*__SECDP_SYSFS_H*/

View File

@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2021, 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 "dp_debug.h"
#include "dp_display.h"
#include "sde_edid_parser.h"
#include "secdp.h"
static u8 g_test_edid[] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x09, 0xD1, 0x54, 0x7F, 0x45, 0x54, 0x00, 0x00,
0x14, 0x1B, 0x01, 0x03, 0x80, 0x46, 0x28, 0x78, 0x2E, 0xDF, 0x50, 0xA3, 0x54, 0x35, 0xB5, 0x26,
0x0F, 0x50, 0x54, 0xA5, 0x6B, 0x80, 0xD1, 0xC0, 0x81, 0xC0, 0x81, 0x00, 0x81, 0x80, 0xA9, 0xC0,
0xB3, 0x00, 0x01, 0x01, 0x01, 0x01, 0x51, 0xD0, 0x00, 0xA0, 0xF0, 0x70, 0x3E, 0x80, 0x30, 0x20,
0x35, 0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x53, 0x35, 0x48,
0x30, 0x31, 0x38, 0x39, 0x31, 0x53, 0x4C, 0x30, 0x0A, 0x20, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x18,
0x4C, 0x1E, 0x8C, 0x3C, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC,
0x00, 0x42, 0x65, 0x6E, 0x51, 0x20, 0x53, 0x57, 0x33, 0x32, 0x30, 0x0A, 0x20, 0x20, 0x01, 0x42,
0x02, 0x03, 0x45, 0xF1, 0x56, 0x61, 0x60, 0x5D, 0x5E, 0x5F, 0x10, 0x05, 0x04, 0x03, 0x02, 0x07,
0x06, 0x0F, 0x1F, 0x20, 0x21, 0x22, 0x14, 0x13, 0x12, 0x16, 0x01, 0x23, 0x09, 0x07, 0x07, 0xE6,
0x06, 0x05, 0x01, 0x60, 0x5A, 0x44, 0x6D, 0x03, 0x0C, 0x00, 0x10, 0x00, 0x38, 0x44, 0x20, 0x00,
0x60, 0x01, 0x02, 0x03, 0x67, 0xD8, 0x5D, 0xC4, 0x01, 0x78, 0x80, 0x01, 0xE4, 0x0F, 0x03, 0x00,
0x00, 0xE3, 0x05, 0xC3, 0x00, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45,
0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1E, 0x56, 0x5E, 0x00, 0xA0, 0xA0, 0xA0, 0x29, 0x50, 0x30,
0x20, 0x35, 0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1A, 0xF4, 0x51, 0x00, 0xA0, 0xF0, 0x70, 0x19,
0x80, 0x30, 0x20, 0x35, 0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0xAB,
};
static struct secdp_display_timing g_parsed_res[] = {
/* active_h, active_v, refresh_rate, interlaced */
{0, 640, 480, 60, false}, /* 640x480 60hz */
{0, 640, 480, 75, false}, /* 640x480 75hz */
{0, 720, 400, 70, false}, /* 720x400 70hz */
{0, 720, 480, 60, true}, /* 720x480i 60hz */
{0, 720, 480, 60, false}, /* 720x480 60hz */
{0, 720, 576, 50, true}, /* 720x576i 50hz */
{0, 720, 576, 50, false}, /* 720x576 50hz */
{0, 800, 600, 60, false}, /* 800x600 60hz */
{0, 800, 600, 75, false}, /* 800x600 75hz */
{0, 832, 624, 75, false}, /* 832x624 75hz */
{0, 1024, 768, 60, false}, /* 1024x768 60hz */
{0, 1024, 768, 75, false}, /* 1024x768 75hz */
{0, 1152, 864, 75, false}, /* 1152x864 75hz */
{0, 1280, 720, 50, false}, /* 1280x720 50hz */
{0, 1280, 720, 60, false}, /* 1280x720 60hz */
{0, 1280, 800, 60, false}, /* 1280x800 60hz */
{0, 1280, 1024, 60, false}, /* 1280x1024 60hz */
{0, 1280, 1024, 75, false}, /* 1280x1024 75hz */
{0, 1440, 480, 60, false}, /* 1440x480 60hz */
{0, 1600, 900, 60, false}, /* 1600x900 60hz */
{0, 1680, 1050, 60, false}, /* 1680x1050 60hz */
{0, 1920, 1080, 50, true}, /* 1920x1080i 50hz */
{0, 1920, 1080, 60, true}, /* 1920x1080i 60hz */
{0, 1920, 1080, 24, false}, /* 1920x1080 24hz */
{0, 1920, 1080, 25, false}, /* 1920x1080 25hz */
{0, 1920, 1080, 30, false}, /* 1920x1080 30hz */
{0, 1920, 1080, 50, false}, /* 1920x1080 50hz */
{0, 1920, 1080, 60, false}, /* 1920x1080 60hz */
{0, 2560, 1440, 60, false}, /* 2560x1440 60hz */
{0, 3840, 2160, 24, false}, /* 3840x2160 24hz */
{0, 3840, 2160, 25, false}, /* 3840x2160 25hz */
{0, 3840, 2160, 30, false}, /* 3840x2160 30hz */
{0, 3840, 2160, 50, false}, /* 3840x2160 50hz */
{0, 3840, 2160, 60, false}, /* 3840x2160 60hz */
};
static void drm_mode_remove(struct drm_connector *connector,
struct drm_display_mode *mode)
{
list_del(&mode->head);
drm_mode_destroy(connector->dev, mode);
}
bool secdp_unit_test_edid_parse(void)
{
int rc, i, parsed_res_cnt = 0, table_size;
bool ret = false;
struct sde_edid_ctrl *edid_ctrl = NULL;
struct drm_display_mode *mode, *t;
struct drm_connector *connector;
connector = secdp_get_connector();
if (!connector) {
DP_ERR("fail to get connector\n");
goto exit;
}
table_size = ARRAY_SIZE(g_parsed_res);
edid_ctrl = sde_edid_init();
if (!edid_ctrl) {
DP_ERR("edid_ctrl alloc failed\n");
goto exit;
}
mutex_lock(&connector->dev->mode_config.mutex);
edid_ctrl->edid = (struct edid *)g_test_edid;
rc = _sde_edid_update_modes(connector, edid_ctrl);
DP_INFO("_sde_edid_update_modes, rc: %d\n", rc);
/* init g_parsed_res */
for (i = 0; i < table_size; i++)
g_parsed_res[i].supported = false;
/* check resolutions */
list_for_each_entry(mode, &connector->probed_modes, head) {
DP_INFO("checking %s @ %d Hz..\n", mode->name, drm_mode_vrefresh(mode));
for (i = 0; i < table_size; i++) {
bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
if (g_parsed_res[i].active_h == mode->hdisplay &&
g_parsed_res[i].active_v == mode->vdisplay &&
g_parsed_res[i].refresh_rate == drm_mode_vrefresh(mode) &&
g_parsed_res[i].interlaced == interlaced) {
/*all conditions are met, mark it as supported*/
g_parsed_res[i].supported = true;
}
}
}
list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
drm_mode_remove(connector, mode);
mutex_unlock(&connector->dev->mode_config.mutex);
kfree(edid_ctrl);
/* count how many resolutions are marked as supported */
for (i = 0; i < table_size; i++) {
if (g_parsed_res[i].supported)
parsed_res_cnt++;
}
/* check if num of supported resolutions are found without errors */
if (parsed_res_cnt != table_size) {
DP_ERR("count is not matched! res_cnt: %d, table_size: %d\n",
parsed_res_cnt, table_size);
goto exit;
}
ret = true;
exit:
DP_INFO("returns %s\n", ret ? "true" : "false");
return ret;
}

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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.
*
*/
#ifndef __SECDP_UNIT_TEST_H
#define __SECDP_UNIT_TEST_H
bool secdp_unit_test_edid_parse(void);
#endif/*__SECDP_UNIT_TEST_H*/

View File

@ -267,6 +267,14 @@ static void dsi_catalog_phy_5_0_init(struct dsi_phy_hw *phy)
phy->ops.set_continuous_clk = dsi_phy_hw_v5_0_set_continuous_clk;
phy->ops.commit_phy_timing = dsi_phy_hw_v5_0_commit_phy_timing;
phy->ops.phy_idle_off = dsi_phy_hw_v5_0_phy_idle_off;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
phy->ops.store_str = dsi_phy_hw_v5_0_store_str;
phy->ops.show_str = dsi_phy_hw_v5_0_show_str;
phy->ops.store_vreg = dsi_phy_hw_v5_0_store_vreg;
phy->ops.show_vreg = dsi_phy_hw_v5_0_show_vreg;
phy->ops.store_emphasis = dsi_phy_hw_v5_0_store_emphasis;
#endif
}
/**

View File

@ -127,6 +127,14 @@ void dsi_phy_hw_v4_0_set_continuous_clk(struct dsi_phy_hw *phy, bool enable);
void dsi_phy_hw_v4_0_commit_phy_timing(struct dsi_phy_hw *phy,
struct dsi_phy_per_lane_cfgs *timing);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
void dsi_phy_hw_v5_0_store_str(struct dsi_phy_hw *phy, u32 *val);
u32 dsi_phy_hw_v5_0_show_str(struct dsi_phy_hw *phy);
void dsi_phy_hw_v5_0_store_vreg(struct dsi_phy_hw *phy, u32 *val);
u32 dsi_phy_hw_v5_0_show_vreg(struct dsi_phy_hw *phy);
void dsi_phy_hw_v5_0_store_emphasis(struct dsi_phy_hw *phy, u32 *val);
#endif
/* Definitions for 4nm PHY hardware driver */
void dsi_phy_hw_v5_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
void dsi_phy_hw_v5_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);

View File

@ -11,6 +11,12 @@
#include "dsi_clk.h"
#include "dsi_defs.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "sde_dbg.h"
#include "ss_dsi_panel_common.h"
#include "ss_panel_power.h"
#endif
struct dsi_core_clks {
struct dsi_core_clk_info clks;
};
@ -211,12 +217,18 @@ int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
rc = clk_set_parent(child->byte_clk, parent->byte_clk);
if (rc) {
DSI_ERR("failed to set byte clk parent\n");
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
SDE_DBG_DUMP(SDE_DBG_BUILT_IN_ALL, "panic");
#endif
goto error;
}
rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
if (rc) {
DSI_ERR("failed to set pixel clk parent\n");
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
SDE_DBG_DUMP(SDE_DBG_BUILT_IN_ALL, "panic");
#endif
goto error;
}
error:
@ -672,6 +684,11 @@ static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
}
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if ((l_type & DSI_LINK_LP_CLK) && (ctrl_count == 1))
ss_panel_power_on_middle_lp_hs_clk();
#endif
if (l_type & DSI_LINK_HS_CLK) {
if (!mngr->is_cont_splash_enabled) {
mngr->phy_config_cb(mngr->priv_data, true);
@ -698,6 +715,9 @@ static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
rc);
goto error_disable_master;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_power_on_middle_lp_hs_clk();
#endif
}
if (l_type & DSI_LINK_HS_CLK) {
@ -786,6 +806,9 @@ static int dsi_display_link_clk_disable(struct dsi_link_clks *clks,
continue;
if (l_type & DSI_LINK_LP_CLK) {
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_power_off_middle_lp_hs_clk();
#endif
rc = dsi_link_lp_clk_stop(&clk->lp_clks);
if (rc)
DSI_ERR("failed to turn off lp link clocks, rc=%d\n",
@ -1255,6 +1278,12 @@ int dsi_clk_req_state(void *client, enum dsi_clk_type clk,
}
mutex_unlock(&mngr->clk_mutex);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (clk & DSI_LINK_CLK)
ss_dct_update_ref(mngr->master_ndx, DCT_TAG_LINK_CLK, state);
#endif
return rc;
}

View File

@ -24,6 +24,11 @@
#include "sde_dbg.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#include "sde_trace.h"
#endif
#define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL"
#define DSI_CTRL_TX_TO_MS 1200
@ -392,6 +397,22 @@ static void dsi_ctrl_dma_cmd_wait_for_done(struct dsi_ctrl *dsi_ctrl)
DSI_CTRL_WARN(dsi_ctrl,
"dma_tx done but irq not triggered\n");
} else {
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = ss_get_vdd(dsi_ctrl->hw.display_index);
LCD_ERR(vdd, "dsi_ctrl_dma_cmd_wait_for_done Timeout!!\n");
/* check physical display connection */
if (gpio_is_valid(vdd->ub_con_det.gpio)) {
pr_err("[SDE] ub_con_det.gpio(%d) level=%d\n",
vdd->ub_con_det.gpio,
gpio_get_value(vdd->ub_con_det.gpio));
}
// case 03745287
//if (!dsi_ctrl->esd_check_underway && !vdd->panel_dead)
// SDE_DBG_DUMP(SDE_DBG_BUILT_IN_ALL, "panic");
#endif
SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_ERROR);
DSI_CTRL_ERR(dsi_ctrl,
"Command transfer failed\n");
@ -847,6 +868,10 @@ static int dsi_ctrl_clocks_init(struct platform_device *pdev,
xo->pixel_clk = xo->byte_clk;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_dct_update_clk(ctrl->hw.display_index, DCT_TAG_LINK_CLK, hs_link->byte_clk);
#endif
return 0;
fail:
dsi_ctrl_clocks_deinit(ctrl);
@ -1272,14 +1297,22 @@ int dsi_message_validate_tx_mode(struct dsi_ctrl *dsi_ctrl,
DSI_CTRL_ERR(dsi_ctrl, " Cannot transfer command,ops not defined\n");
return -ENOTSUPP;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if ((cmd_len + 4) > SZ_1M) {
#else
if ((cmd_len + 4) > SZ_4K) {
#endif
DSI_CTRL_ERR(dsi_ctrl, "Cannot transfer,size is greater than 4096\n");
return -ENOTSUPP;
}
}
if (*flags & DSI_CTRL_CMD_FETCH_MEMORY) {
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if ((dsi_ctrl->cmd_len + cmd_len + 4) > SZ_1M) {
#else
if ((dsi_ctrl->cmd_len + cmd_len + 4) > SZ_4K) {
#endif
DSI_CTRL_ERR(dsi_ctrl, "Cannot transfer,size is greater than 4096\n");
return -ENOTSUPP;
}
@ -1369,6 +1402,9 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl,
u32 hw_flags = 0;
struct dsi_ctrl_hw_ops dsi_hw_ops = dsi_ctrl->hw.ops;
struct dsi_split_link_config *split_link;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
u8 *tx_buf = (u8 *)msg->tx_buf;
#endif
split_link = &(dsi_ctrl->host_config.common_config.split_link);
@ -1416,10 +1452,19 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl,
cmd_mem,
hw_flags);
} else {
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (tx_buf[0] == 0x2a || tx_buf[0] == 0x2b)
SDE_ATRACE_BEGIN("dsi_message_tx_flush");
#endif
dsi_hw_ops.kickoff_command(
&dsi_ctrl->hw,
cmd_mem,
hw_flags);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (tx_buf[0] == 0x2a || tx_buf[0] == 0x2b)
SDE_ATRACE_END("dsi_message_tx_flush");
#endif
}
} else if (flags & DSI_CTRL_CMD_FIFO_STORE) {
dsi_hw_ops.kickoff_fifo_command(&dsi_ctrl->hw,
@ -1462,6 +1507,12 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl,
dsi_ctrl->cmd_trigger_frame);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
// TODO : this should be called in dsi_ctrl_dma_cmd_wait_for_done()..
// but there is no msg struct... fix this later... (CSP3)
if (tx_buf[0] == 0x2a || tx_buf[0] == 0x2b)
SDE_ATRACE_END("dsi_message_tx_wait");
#endif
dsi_hw_ops.reset_cmd_fifo(&dsi_ctrl->hw);
@ -1490,6 +1541,12 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd_de
u32 cnt = 0;
u8 *cmdbuf;
u32 *flags;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = ss_get_vdd(dsi_ctrl->hw.display_index);
ss_print_cmd_desc(cmd_desc, vdd);
ss_print_cmd_desc_evtlog(cmd_desc, vdd);
#endif
msg = &cmd_desc->msg;
flags = &cmd_desc->ctrl_flags;
@ -2072,6 +2129,10 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
enum dsi_ctrl_version version;
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO_CRITICAL(0, "+++\n");
#endif
id = of_match_node(msm_dsi_of_match, pdev->dev.of_node);
if (!id)
return -ENODEV;
@ -2145,6 +2206,10 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dsi_ctrl);
DSI_CTRL_INFO(dsi_ctrl, "Probe successful\n");
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO_CRITICAL(0, "---\n");
#endif
return 0;
fail_clks:
@ -2715,6 +2780,9 @@ static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
struct dsi_event_cb_info cb_info;
struct dsi_display *display;
bool skip_irq_enable = false;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) && IS_ENABLED(CONFIG_SEC_DEBUG)
struct samsung_display_driver_data *vdd = ss_get_vdd(dsi_ctrl->hw.display_index);
#endif
bool is_spurious_interrupt = false;
cb_info = dsi_ctrl->irq_info.irq_err_cb;
@ -2760,6 +2828,12 @@ static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
if (error & 0xF0000) {
u32 mask = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) && IS_ENABLED(CONFIG_SEC_DEBUG)
if (sec_debug_is_enabled() && ss_panel_attach_get(vdd)) {
pr_err("dsi FIFO OVERFLOW error: 0x%lx\n", error);
SDE_DBG_DUMP_WQ(SDE_DBG_BUILT_IN_ALL, "panic");
}
#endif
if (dsi_ctrl->hw.ops.get_error_mask)
mask = dsi_ctrl->hw.ops.get_error_mask(&dsi_ctrl->hw);
/* no need to report FIFO overflow if already masked */
@ -2777,6 +2851,12 @@ static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
/* DSI FIFO UNDERFLOW error */
if (error & 0xF00000) {
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) && IS_ENABLED(CONFIG_SEC_DEBUG)
if (sec_debug_is_enabled() && ss_panel_attach_get(vdd) && !vdd->panel_dead) { // check panel dead
pr_err("dsi FIFO UNDERFLOW error: 0x%lx\n", error);
SDE_DBG_DUMP_WQ(SDE_DBG_BUILT_IN_ALL, "panic");
}
#endif
if (cb_info.event_cb) {
cb_info.event_idx = DSI_FIFO_UNDERFLOW;
display = cb_info.event_usr_ptr;
@ -2812,6 +2892,11 @@ static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
/* enable back DSI interrupts */
if (dsi_ctrl->hw.ops.error_intr_ctrl && !skip_irq_enable)
dsi_ctrl->hw.ops.error_intr_ctrl(&dsi_ctrl->hw, true);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
inc_dpui_u32_field_nolock(DPUI_KEY_QCT_DSIE, 1);
ss_get_vdd(dsi_ctrl->hw.display_index)->dsi_errors = error;
#endif
}
/**
@ -2865,6 +2950,9 @@ static irqreturn_t dsi_ctrl_isr(int irq, void *ptr)
atomic_set(&dsi_ctrl->dma_irq_trig, 1);
dsi_ctrl_disable_status_interrupt(dsi_ctrl,
DSI_SINT_CMD_MODE_DMA_DONE);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO_IF(ss_get_vdd(dsi_ctrl->hw.display_index), "DMA_DONE\n");
#endif
complete_all(&dsi_ctrl->irq_info.cmd_dma_done);
}
@ -3734,6 +3822,44 @@ int dsi_ctrl_get_host_engine_init_state(struct dsi_ctrl *dsi_ctrl,
return 0;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/**
* dsi_ctrl_update_host_engine_state_for_cont_splash() -
* set engine state for dsi controller during continuous splash
* @dsi_ctrl: DSI controller handle.
* @state: Engine state.
*
* Set host engine state for DSI controller during continuous splash.
*
* Return: error code.
*/
int dsi_ctrl_update_host_engine_state_for_cont_splash(struct dsi_ctrl *dsi_ctrl,
enum dsi_engine_state state)
{
int rc = 0;
if (!dsi_ctrl || (state >= DSI_CTRL_ENGINE_MAX)) {
DSI_CTRL_ERR(dsi_ctrl, "Invalid params\n");
return -EINVAL;
}
mutex_lock(&dsi_ctrl->ctrl_lock);
rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_HOST_ENGINE, state);
if (rc) {
DSI_CTRL_ERR(dsi_ctrl, "Controller state check failed, rc=%d\n",
rc);
goto error;
}
DSI_CTRL_DEBUG(dsi_ctrl, "Set host engine state = %d\n", state);
dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_HOST_ENGINE, state);
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
}
#endif
/**
* dsi_ctrl_set_power_state() - set power state for dsi controller
* @dsi_ctrl: DSI controller handle.
@ -4272,6 +4398,9 @@ int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl)
*/
void dsi_ctrl_drv_register(void)
{
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO_CRITICAL(0, "+++\n");
#endif
platform_driver_register(&dsi_ctrl_driver);
}

View File

@ -61,6 +61,11 @@
/*Default tearcheck window size as programmed by MDP*/
#define TEARCHECK_WINDOW_SIZE 5
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* max size supported for dsi cmd transfer using DMA */
#define DSI_CTRL_MAX_CMD_FET_MEMORY_SIZE 200
#endif
/**
* enum dsi_power_state - defines power states for dsi controller.
* @DSI_CTRL_POWER_VREG_OFF: Digital and analog supplies for DSI controller
@ -651,6 +656,19 @@ void dsi_ctrl_transfer_unprepare(struct dsi_ctrl *dsi_ctrl, u32 flags);
*/
int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/**
* dsi_ctrl_update_host_engine_state_for_cont_splash() - update engine
* states for cont splash usecase
* @dsi_ctrl: DSI controller handle.
* @state: DSI engine state
*
* Return: error code.
*/
int dsi_ctrl_update_host_engine_state_for_cont_splash(struct dsi_ctrl *dsi_ctrl,
enum dsi_engine_state state);
#endif
/**
* dsi_ctrl_set_power_state() - set power state for dsi controller
* @dsi_ctrl: DSI controller handle.

View File

@ -985,6 +985,9 @@ struct dsi_ctrl_hw {
bool null_insertion_enabled;
bool widebus_support;
bool reset_trig_ctrl;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int display_index; /* primary display or secondary display */
#endif
};
#endif /* _DSI_CTRL_HW_H_ */

View File

@ -377,6 +377,10 @@ struct dsi_cmd_desc {
u32 ctrl;
u32 ctrl_flags;
ktime_t ts;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
u8 *ss_txbuf;
void *ss_cmd; /* struct ss_cmd_desc *ss_cmd */
#endif
};
/**
@ -393,6 +397,21 @@ struct dsi_panel_cmd_set {
u32 count;
u32 ctrl_idx;
struct dsi_cmd_desc *cmds;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#define SUPPORT_PANEL_REVISION 20
int ss_cmd_type;
char *name;
int exclusive_pass;
/* cmd_set_rev[panel_rev] is pointer to
* describe "struct dsi_panel_cmd_set *set" for each panel revision.
* If you want get cmd_set for panel revision A, get like below.
* struct dsi_panel_cmd_set *set = set->cmd_set_rev[panel_rev];
*/
void *cmd_set_rev[SUPPORT_PANEL_REVISION];
void *self_disp_cmd_set_rev;
#endif
};
/**
@ -450,6 +469,24 @@ struct dsi_mode_info {
struct msm_roi_caps roi_caps;
u32 qsync_min_fps;
u32 avr_step_fps;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* Identify VRR HS by drm_mode's name.
* drm_mode's name is defined by dsi_mode->timing.sot_hs_mode parsed
* from samsung,mdss-dsi-sot-hs-mode in panel dtsi file.
* ex) drm_mode->name is "1080x2316x60x193345cmdHS" for HS mode.
* drm_mode->name is "1080x2316x60x193345cmdNS" for NS mode.
* To use this feature, declare different porch between HS and NS modes,
* in panel dtsi file.
* Refer to ss_is_sot_hs_from_drm_mode().
*/
bool sot_hs_mode;
/* DDI Passive mode.
* ex) DDI 120hz + TE(AP) is 60hz.
*/
bool phs_mode;
#endif
};
/**

View File

@ -23,6 +23,10 @@
#include "dsi_pwr.h"
#include "sde_dbg.h"
#include "dsi_parser.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#include "ss_panel_power.h"
#endif
#define to_dsi_display(x) container_of(x, struct dsi_display, host)
#define INT_BASE_10 10
@ -40,12 +44,21 @@
#define SEC_PANEL_NAME_MAX_LEN 256
u8 dbgfs_tx_cmd_buf[SZ_4K];
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
char dsi_display_secondary[MAX_CMDLINE_PARAM_LEN];
struct dsi_display_boot_param boot_displays[MAX_DSI_ACTIVE_DISPLAY] = {
{.boot_param = dsi_display_primary},
{.boot_param = dsi_display_secondary},
};
#else
static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
static char dsi_display_secondary[MAX_CMDLINE_PARAM_LEN];
static struct dsi_display_boot_param boot_displays[MAX_DSI_ACTIVE_DISPLAY] = {
{.boot_param = dsi_display_primary},
{.boot_param = dsi_display_secondary},
};
#endif
static void dsi_display_panel_id_notification(struct dsi_display *display);
@ -361,6 +374,9 @@ static void dsi_display_aspace_cb_locked(void *cb_data, bool is_detach)
struct dsi_display *display;
struct dsi_display_ctrl *display_ctrl;
int rc, cnt;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!cb_data) {
DSI_ERR("aspace cb called with invalid cb_data\n");
@ -373,6 +389,10 @@ static void dsi_display_aspace_cb_locked(void *cb_data, bool is_detach)
* while detaching the non-secure context banks
*/
dsi_panel_acquire_panel_lock(display->panel);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = display->panel->panel_private;
mutex_lock(&vdd->cmd_lock);
#endif
if (is_detach) {
/* invalidate the stored iova */
@ -408,6 +428,9 @@ static void dsi_display_aspace_cb_locked(void *cb_data, bool is_detach)
}
end:
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mutex_unlock(&vdd->cmd_lock);
#endif
/* release panel_lock */
dsi_panel_release_panel_lock(display->panel);
}
@ -510,7 +533,11 @@ static int dsi_host_alloc_cmd_tx_buffer(struct dsi_display *display)
struct dsi_display_ctrl *display_ctrl;
display->tx_cmd_buf = msm_gem_new(display->drm_dev,
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
SZ_1M,
#else
SZ_4K,
#endif
MSM_BO_UNCACHED);
if ((display->tx_cmd_buf) == NULL) {
@ -519,8 +546,11 @@ static int dsi_host_alloc_cmd_tx_buffer(struct dsi_display *display)
goto error;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
display->cmd_buffer_size = SZ_1M;
#else
display->cmd_buffer_size = SZ_4K;
#endif
display->aspace = msm_gem_smmu_address_space_get(
display->drm_dev, MSM_SMMU_DOMAIN_UNSECURE);
@ -559,7 +589,11 @@ static int dsi_host_alloc_cmd_tx_buffer(struct dsi_display *display)
display_for_each_ctrl(cnt, display) {
display_ctrl = &display->ctrl[cnt];
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
display_ctrl->ctrl->cmd_buffer_size = SZ_1M;
#else
display_ctrl->ctrl->cmd_buffer_size = SZ_4K;
#endif
display_ctrl->ctrl->cmd_buffer_iova =
display->cmd_buffer_iova;
display_ctrl->ctrl->vaddr = display->vaddr;
@ -1007,6 +1041,11 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
} else if (status_mode == ESD_MODE_PANEL_TE) {
rc = dsi_display_status_check_te(dsi_display, te_rechecks);
te_check_override = false;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
} else if (status_mode == ESD_MODE_PANEL_IRQ) {
/* In SS ESD_MODE_PANEL_IRQ mode, always report panel_dead. */
rc = 0;
#endif
} else {
DSI_WARN("Unsupported check status mode: %d\n", status_mode);
panel->esd_config.esd_enabled = false;
@ -1350,12 +1389,23 @@ int dsi_display_set_power(struct drm_connector *connector,
{
struct dsi_display *display = disp;
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!display || !display->panel) {
DSI_ERR("invalid display/panel\n");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = display->panel->panel_private;
LCD_INFO(vdd, "%s ++\n", power_mode == SDE_MODE_DPMS_LP1 ? "LP1" :
power_mode == SDE_MODE_DPMS_LP2 ? "LP2" :
power_mode == SDE_MODE_DPMS_ON ? "ON" :
power_mode == SDE_MODE_DPMS_OFF ? "OFF" : "UNKNOWN_POWER_MODE");
#endif
switch (power_mode) {
case SDE_MODE_DPMS_LP1:
rc = dsi_panel_set_lp1(display->panel);
@ -1380,6 +1430,13 @@ int dsi_display_set_power(struct drm_connector *connector,
if (!rc)
display->panel->power_mode = power_mode;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO(vdd, "%s %s --\n", power_mode == SDE_MODE_DPMS_LP1 ? "LP1" :
power_mode == SDE_MODE_DPMS_LP2 ? "LP2" :
power_mode == SDE_MODE_DPMS_ON ? "ON" :
power_mode == SDE_MODE_DPMS_OFF ? "OFF" : "UNKNOWN_POWER_MODE",
rc ? "failed" : "successful");
#endif
return rc;
}
@ -2526,6 +2583,32 @@ void dsi_display_enable_event(struct drm_connector *connector,
}
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/**
* dsi_config_host_engine_state_for_cont_splash()- update host engine state
* during continuous splash.
* @display: Handle to dsi display
*
*/
static void dsi_config_host_engine_state_for_cont_splash
(struct dsi_display *display, bool enable)
{
int i;
struct dsi_display_ctrl *ctrl;
enum dsi_engine_state host_state = enable ? DSI_CTRL_ENGINE_ON : DSI_CTRL_ENGINE_OFF;
/* Sequence does not matter for split dsi usecases */
display_for_each_ctrl(i, display) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
continue;
dsi_ctrl_update_host_engine_state_for_cont_splash(ctrl->ctrl,
host_state);
}
}
#endif
static int dsi_display_ctrl_power_on(struct dsi_display *display)
{
int rc = 0;
@ -2606,6 +2689,11 @@ static void dsi_display_parse_cmdline_topology(struct dsi_display *display,
if (sw_te)
display->sw_te_using_wd = true;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* In case of no ":config" option in command line, it should set NO_OVERRIDE. */
display->cmdline_topology = NO_OVERRIDE;
#endif
str = strnstr(boot_str, ":config", strlen(boot_str));
if (str) {
if (sscanf(str, ":config%lu", &cmdline_topology) != 1) {
@ -2950,7 +3038,11 @@ error_host_deinit:
return rc;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int dsi_display_ctrl_init(struct dsi_display *display)
#else
static int dsi_display_ctrl_init(struct dsi_display *display)
#endif
{
int rc = 0;
int i;
@ -2996,7 +3088,11 @@ error_host_deinit:
return rc;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int dsi_display_ctrl_deinit(struct dsi_display *display)
#else
static int dsi_display_ctrl_deinit(struct dsi_display *display)
#endif
{
int rc = 0;
int i;
@ -3407,6 +3503,9 @@ int dsi_host_transfer_sub(struct mipi_dsi_host *host, struct dsi_cmd_desc *cmd)
struct dsi_display *display;
struct dsi_display_ctrl *ctrl;
int i, rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!host || !cmd) {
DSI_ERR("Invalid params\n");
@ -3477,8 +3576,21 @@ int dsi_host_transfer_sub(struct mipi_dsi_host *host, struct dsi_cmd_desc *cmd)
}
rc = dsi_ctrl_cmd_transfer(display->ctrl[idx].ctrl, cmd);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* TX: rc means error code, so rc=0 means no error.
* RX: rc means length of received data, so rc=0 means error.
*/
vdd = display->panel->panel_private;
if (((cmd->ctrl_flags & DSI_CTRL_CMD_READ) && rc <= 0) ||
(!(cmd->ctrl_flags & DSI_CTRL_CMD_READ) && rc)) {
LCD_ERR(vdd, "[%s] cmd transfer failed, rc=%d, cmd_flags=%x cmd = %x\n",
ss_get_cmd_name(vdd->cmd_type), rc, cmd->ctrl_flags, (u8 *)cmd->msg.tx_buf + 0);
rc = -EINVAL;
}
#else
if (rc)
DSI_ERR("[%s] cmd transfer failed, rc=%d\n", display->name, rc);
#endif
dsi_ctrl_transfer_unprepare(display->ctrl[idx].ctrl, cmd->ctrl_flags);
}
@ -3589,6 +3701,10 @@ static int dsi_display_clocks_init(struct dsi_display *display)
dsi_clock_name = "qcom,dsi-select-sec-clocks";
num_clk = dsi_display_get_clocks_count(display, dsi_clock_name);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (num_clk < 1)
DSI_ERR("^^^^^^^^^^ No dsi clock, Check panel_common.MODEL.dtsi!\n");
#endif
for (i = 0; i < num_clk; i++) {
dsi_display_get_clock_name(display, dsi_clock_name, i,
@ -4225,6 +4341,10 @@ error:
static bool dsi_display_validate_panel_resources(struct dsi_display *display)
{
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return true;
#endif
if (!is_sim_panel(display)) {
if (!display->panel->host_config.ext_bridge_mode &&
!gpio_is_valid(display->panel->reset_config.reset_gpio)) {
@ -4260,6 +4380,20 @@ static int dsi_display_res_init(struct dsi_display *display)
ctrl->phy = NULL;
goto error_ctrl_put;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!strcmp(display->display_type, "primary")) {
ctrl->phy->hw.display_index = PRIMARY_DISPLAY_NDX;
ctrl->ctrl->hw.display_index = PRIMARY_DISPLAY_NDX;
LCD_INFO_CRITICAL(0, "phy->hw.index[%d] & ctrl->hw.index[%d] are map to SS PRIMARY_DISPLAY_NDX\n",
ctrl->phy->hw.index, ctrl->ctrl->hw.index);
}
else if (!strcmp(display->display_type, "secondary")) {
ctrl->phy->hw.display_index = SECONDARY_DISPLAY_NDX;
ctrl->ctrl->hw.display_index = SECONDARY_DISPLAY_NDX;
LCD_INFO_CRITICAL(0, "phy->hw.index[%d] & ctrl->hw.index[%d] are map to SS SECONDARY_DISPLAY_NDX\n",
ctrl->phy->hw.index, ctrl->ctrl->hw.index);
}
#endif
}
display->panel = dsi_panel_get(&display->pdev->dev,
@ -5131,6 +5265,16 @@ static void dsi_display_validate_dms_fps(struct dsi_display_mode *cur_mode,
u32 cur_fps, to_fps;
u32 cur_h_active, to_h_active;
u32 cur_v_active, to_v_active;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
u32 cur_sot_hs, to_sot_hs;
u32 cur_phs, to_phs;
cur_sot_hs = cur_mode->timing.sot_hs_mode;
to_sot_hs = to_mode->timing.sot_hs_mode;
cur_phs = cur_mode->timing.phs_mode;
to_phs = to_mode->timing.phs_mode;
#endif
cur_fps = cur_mode->timing.refresh_rate;
to_fps = to_mode->timing.refresh_rate;
@ -5140,7 +5284,11 @@ static void dsi_display_validate_dms_fps(struct dsi_display_mode *cur_mode,
to_v_active = to_mode->timing.v_active;
if ((cur_h_active == to_h_active) && (cur_v_active == to_v_active) &&
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
((cur_phs != to_phs) || (cur_sot_hs != to_sot_hs) || (cur_fps != to_fps))) {
#else
(cur_fps != to_fps)) {
#endif
to_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS_FPS;
DSI_DEBUG("DMS Modeset with FPS change\n");
} else {
@ -5396,6 +5544,10 @@ int dsi_display_cont_splash_res_disable(void *dsi_display)
struct dsi_display *display = dsi_display;
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_power_pmic_vote(display->panel->panel_private, false);
#endif
/* Remove the panel vote that was added during dsi display probe */
rc = dsi_pwr_enable_regulator(&display->panel->power_info, false);
if (rc)
@ -5414,6 +5566,12 @@ int dsi_display_cont_splash_config(void *dsi_display)
{
struct dsi_display *display = dsi_display;
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
struct drm_encoder *drm_enc;
struct dsi_display_ctrl *ctrl;
int i;
#endif
/* Vote for gdsc required to read register address space */
if (!display) {
@ -5432,6 +5590,15 @@ int dsi_display_cont_splash_config(void *dsi_display)
display->is_cont_splash_enabled = true;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = display->panel->panel_private;
if (display->is_cont_splash_enabled) {
vdd->samsung_splash_enabled = true;
vdd->display_status_dsi.first_commit_disp_on = true;
LCD_INFO(vdd, "set samsung splash (%d)\n", vdd->samsung_splash_enabled);
}
#endif
/* Update splash status for clock manager */
dsi_display_clk_mngr_update_splash_status(display->clk_mngr,
display->is_cont_splash_enabled);
@ -5456,6 +5623,44 @@ int dsi_display_cont_splash_config(void *dsi_display)
/* Set the current brightness level */
dsi_panel_bl_handoff(display->panel);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (vdd->support_early_id_read) {
drm_enc = display->bridge->base.encoder;
/* disable autorefresh to prevent DSI FIFO underflow */
sde_encoder_prepare_commit(drm_enc);
/*
* To do dsi related work (rx/tx),
* need to set enable flags for dsi state (host_initialized, controller_state) first.
*/
mutex_lock(&display->display_lock);
dsi_config_host_engine_state_for_cont_splash(display, true);
dsi_display_ctrl_init(display);
display_for_each_ctrl(i, display) {
ctrl = &display->ctrl[i];
if (!ctrl || !ctrl->ctrl)
continue;
/* Copy dma_cmd_trigger of panel (parsed from dtsi) of dma_cmd_trigger of dsi_ctrl
* dma_cmd_trigger of dsi_ctrl will be copied at set_mode.
* to read opertaion before that, we have to copy the value by force.
*/
LCD_INFO(vdd, "assign ctrl : dma_cmd_trigger %d <- panel dma_cmd_trigger : %d\n",
ctrl->ctrl->host_config.common_config.dma_cmd_trigger, display->panel->host_config.dma_cmd_trigger);
ctrl->ctrl->host_config.common_config.dma_cmd_trigger = display->panel->host_config.dma_cmd_trigger;
}
ss_early_display_init(display->panel->panel_private);
dsi_display_ctrl_deinit(display);
dsi_config_host_engine_state_for_cont_splash(display, false);
mutex_unlock(&display->display_lock);
}
#endif
return rc;
clk_manager_update:
@ -5503,6 +5708,28 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display)
int rc = 0, i = 0;
struct dsi_display_ctrl *ctrl;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = display->panel->panel_private;
if (vdd->panel_func.samsung_dyn_mipi_pre)
vdd->panel_func.samsung_dyn_mipi_pre(vdd);
/* prevent below race condition which causes dsi interrupt storm.
* 1) TASK#1: start to transmit mipi cmd. (e.g.: brightness, mdnie, copr, and etc..
* 2) TASK#2: dynamic mipi clock: call dsi_display_link_clk_force_update_ctrl(),
* and stop link clock.
* 3) TASK#1: keep trying to send mipi cmd even link clock is off
* --> dsi interrupt storm occurs...
* MIPI DSI link clock has vote system, so link clock has refcount value more than 1.
* But its value never reach to 0 during display on due to QCT architecture,
* may be to prevent frequent display clock on/off overhead...
* So, QCT dynamic mipi clock ignores above vote system, and force to reset link clock...
*
* To prevent above race condition, add vdd->cmd_lock in this function.
*/
mutex_lock(&vdd->cmd_lock);
#endif
/*
* The force update dsi clock, is the only clock update function that toggles the state of
* DSI clocks without any ref count protection. With the addition of ASYNC command wait,
@ -5519,11 +5746,20 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display)
rc = dsi_display_link_clk_force_update_ctrl(display->dsi_clk_handle);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mutex_unlock(&vdd->cmd_lock);
#endif
if (!rc) {
DSI_DEBUG("dsi bit clk has been configured to %d\n",
display->cached_clk_rate);
atomic_set(&display->clkrate_change_pending, 0);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#if IS_ENABLED(CONFIG_DEV_RIL_BRIDGE)
ss_dyn_mipi_clk_tx_ffc(vdd);
#endif
#endif
} else {
DSI_ERR("Failed to configure dsi bit clock '%d'. rc = %d\n",
display->cached_clk_rate, rc);
@ -5959,6 +6195,12 @@ static const struct component_ops dsi_display_comp_ops = {
.unbind = dsi_display_unbind,
};
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
static const struct dev_pm_ops dsi_display_pm_ops = {
.suspend = ss_dsi_display_suspend,
};
#endif
static struct platform_driver dsi_display_driver = {
.probe = dsi_display_dev_probe,
.remove = dsi_display_dev_remove,
@ -5966,6 +6208,9 @@ static struct platform_driver dsi_display_driver = {
.name = "msm-dsi-display",
.of_match_table = dsi_display_dt_match,
.suppress_bind_attrs = true,
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
.pm = &dsi_display_pm_ops
#endif
},
};
@ -6058,6 +6303,10 @@ int dsi_display_dev_probe(struct platform_device *pdev)
goto end;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
DSI_INFO("dsi_display_dev_probe ++\n");
#endif
display->post_cmd_tx_workq = create_singlethread_workqueue(
"dsi_post_cmd_tx_workq");
if (!display->post_cmd_tx_workq) {
@ -6097,6 +6346,17 @@ int dsi_display_dev_probe(struct platform_device *pdev)
if (!panel_node)
DSI_WARN("%s panel_node %s not found\n", display->display_type,
boot_disp->name);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
pr_err("[SDE] %s boot_disp_en True, boot_disp->name=%s\n", display->display_type, boot_disp->name);
if (panel_node)
samsung_panel_initialize(boot_disp->name, index);
else {
pr_err("[SDE] Display Panel is not found. Failed to probe\n");
rc = -EPROBE_DEFER;
goto end;
}
#endif
} else {
panel_node = of_parse_phandle(node,
"qcom,dsi-default-panel", 0);
@ -6143,6 +6403,9 @@ int dsi_display_dev_probe(struct platform_device *pdev)
if (rc)
goto end;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
DSI_INFO("dsi_display_dev_probe --\n");
#endif
return 0;
end:
@ -7084,6 +7347,22 @@ int dsi_display_restore_bit_clk(struct dsi_display *display, struct dsi_display_
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
{
struct samsung_display_driver_data *vdd = display->panel->panel_private;
if (vdd->dyn_mipi_clk.is_support) {
clk_rate_hz = display->cached_clk_rate;
mode->timing.clk_rate_hz = clk_rate_hz;
mode->priv_info->clk_rate_hz = clk_rate_hz;
LCD_DEBUG(vdd, "restore byte clock [%d] \n", display->cached_clk_rate);
return 0;
}
}
#endif
/* avoid updating bit_clk for dyn clk feature disbaled usecase */
if (!display->panel->dyn_clk_caps.dyn_clk_support)
return 0;
@ -7578,6 +7857,10 @@ static bool dsi_display_match_timings(const struct dsi_display_mode *mode1,
mode1->timing.h_skew == mode2->timing.h_skew &&
mode1->timing.v_back_porch == mode2->timing.v_back_porch &&
mode1->timing.v_front_porch == mode2->timing.v_front_porch &&
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mode1->timing.sot_hs_mode == mode2->timing.sot_hs_mode &&
mode1->timing.phs_mode == mode2->timing.phs_mode &&
#endif
mode1->timing.v_sync_width == mode2->timing.v_sync_width;
end:
@ -7983,6 +8266,11 @@ static int dsi_display_pre_switch(struct dsi_display *display)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = display->panel->panel_private;
LCD_DEBUG(vdd, "DMS : update dsi ctrl for new mode\n");
#endif
rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
DSI_CORE_CLK, DSI_CLK_ON);
if (rc) {
@ -8169,6 +8457,57 @@ static void dsi_display_handle_lp_rx_timeout(struct work_struct *work)
u32 version = 0;
display = container_of(work, struct dsi_display, lp_rx_timeout_work);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (display && display->panel &&
(display->panel->panel_mode == DSI_OP_CMD_MODE) &&
!atomic_read(&display->panel->esd_recovery_pending)) {
struct samsung_display_driver_data *vdd;
struct sde_connector *conn;
vdd = display->panel->panel_private;
if (!vdd) {
LCD_ERR(vdd, "LP RX timeout: invalid vdd\n");
return;
}
if (!vdd->support_lp_rx_err_recovery) {
LCD_ERR(vdd, "LP RX timeout: do not support LP RX timeout recovery\n");
return;
}
conn = GET_SDE_CONNECTOR(vdd);
if (!conn) {
LCD_ERR(vdd, "LP RX timeout: invalid conn\n");
return;
}
/* Increase feild cnt only once because we retry 5 times for one mipi fail */
if (++vdd->lp_rx_fail_cnt == 1) {
if (vdd->ndx == PRIMARY_DISPLAY_NDX)
inc_dpui_u32_field(DPUI_KEY_QCT_MAIN_RX_FAIL_CNT, 1);
else
inc_dpui_u32_field(DPUI_KEY_QCT_SUB_RX_FAIL_CNT, 1);
}
if (display->enabled == false) { // dsi_bridge_enable, dsi_bridge_disable
LCD_ERR(vdd, "LP RX timeout: skip panel recovery, trial count = %d\n",
vdd->panel_recovery_cnt);
return;
}
LCD_ERR(vdd, "LP RX timeout: panel recovery for cmd panel, trial count = %d\n",
vdd->panel_recovery_cnt);
vdd->esd_recovery.esd_irq_enable(false, true, (void *)vdd, ESD_MASK_DEFAULT);
vdd->panel_lpm.esd_recovery = true;
vdd->panel_recovery_cnt++;
SS_XLOG(vdd->panel_recovery_cnt);
inc_dpui_u32_field(DPUI_KEY_QCT_RCV_CNT, 1);
schedule_work(&conn->status_work.work);
return;
}
#endif
if (!display || !display->panel ||
(display->panel->panel_mode != DSI_OP_VIDEO_MODE) ||
atomic_read(&display->panel->esd_recovery_pending)) {
@ -8321,6 +8660,9 @@ int dsi_display_prepare(struct dsi_display *display)
{
int rc = 0;
struct dsi_display_mode *mode;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!display) {
DSI_ERR("Invalid params\n");
@ -8332,6 +8674,12 @@ int dsi_display_prepare(struct dsi_display *display)
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = display->panel->panel_private;
if (!(display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS))
LCD_INFO(vdd, "++\n");
#endif
SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&display->display_lock);
@ -8456,6 +8804,17 @@ int dsi_display_prepare(struct dsi_display *display)
}
}
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
else if (ss_panel_attach_get(display->panel->panel_private)) {
/* In case of cont. splash on mode, it skips pinctrl setting
* included in dsi_panel_prepare(). Some display pins,
* which are not configured in bootloader, would be unpredictable state.
* Configure display pins here, in case of cont. splash on mode.
*/
LCD_INFO(vdd, "set display pinctrl in con_splash on mode\n");
rc = dsi_panel_set_pinctrl_state(display->panel, true);
}
#endif
goto error;
error_ctrl_link_off:
@ -8475,6 +8834,11 @@ error_panel_post_unprep:
error:
mutex_unlock(&display->display_lock);
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!(display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS))
LCD_INFO(vdd, "%s --\n", display->display_type);
#endif
return rc;
}
@ -8539,6 +8903,10 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable)
mutex_lock(&display->display_lock);
display->queue_cmd_waits = true;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
SDE_ATRACE_BEGIN(enable ? "qsync_on" : "qsync_off");
#endif
display_for_each_ctrl(i, display) {
if (enable) {
/* send the commands to enable qsync */
@ -8561,6 +8929,9 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable)
exit:
display->queue_cmd_waits = false;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
SDE_ATRACE_END(enable ? "qsync_on" : "qsync_off");
#endif
SDE_EVT32(enable, display->panel->qsync_caps.qsync_min_fps, rc);
mutex_unlock(&display->display_lock);
return rc;
@ -8630,13 +9001,28 @@ int dsi_display_pre_kickoff(struct drm_connector *connector,
struct dsi_display_mode *mode;
int rc = 0, ret = 0;
int i;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
mode = display->panel->cur_mode;
/* check and setup MISR */
if (display->misr_enable)
_dsi_display_setup_misr(display);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* SAMSUNG_FINGERPRINT */
vdd = display->panel->panel_private;
mutex_lock(&vdd->dyn_mipi_clk.dyn_mipi_lock);
/* configure dynamic clk rate */
if (vdd->dyn_mipi_clk.requested_clk_rate) {
if (display->panel->panel_mode == DSI_OP_CMD_MODE)
dsi_display_dynamic_clk_configure_cmd(display, vdd->dyn_mipi_clk.requested_clk_rate);
vdd->dyn_mipi_clk.requested_clk_rate = 0;
}
#endif
/* dynamic DSI clock setting */
if (atomic_read(&display->clkrate_change_pending)) {
mutex_lock(&display->display_lock);
@ -8689,6 +9075,9 @@ wait_failure:
mutex_unlock(&display->display_lock);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mutex_unlock(&vdd->dyn_mipi_clk.dyn_mipi_lock);
#endif
if (!ret)
rc = dsi_display_set_roi(display, params->rois);
@ -8771,6 +9160,9 @@ int dsi_display_enable(struct dsi_display *display)
{
int rc = 0;
struct dsi_display_mode *mode;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = display->panel->panel_private;
#endif
if (!display || !display->panel) {
DSI_ERR("Invalid params\n");
@ -8798,13 +9190,56 @@ int dsi_display_enable(struct dsi_display *display)
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* Initialize samsung display driver in continuous splash mode,
* like smart dimming, mdnie, and etc.
*/
LCD_INFO(vdd, "is_cont_splash_enabled, support_vrr_based_bl: %d, vrr_change: %d, multi_res_change: %d\n",
vdd->vrr.support_vrr_based_bl, vdd->vrr.is_vrr_changing,
vdd->vrr.is_multi_resolution_changing);
mutex_lock(&display->display_lock);
dsi_panel_enable(display->panel);
/* In case of that first commit includes VRR, it should handle VRR or multi resolution.
* dsi_mode_flags has no DSI_MODE_FLAG_DMS set which is set in
* dsi_display_validate_mode_change().
*/
/* If dsi_panel_switch() is called before dsi_panel_enable()
* which transmit qcom,mdss-dsi-on-command, it causes causes
* screen noise on HAB DDI.
*/
if (vdd->vrr.support_vrr_based_bl &&
(vdd->vrr.is_vrr_changing || vdd->vrr.is_multi_resolution_changing)) {
LCD_INFO(vdd, "DMS: VRR: trigger dms switch in splash on mode\n");
dsi_panel_switch(display->panel);
}
mode = display->panel->cur_mode;
if ((mode->priv_info->dsc_enabled) && (!vdd->mipi_header_modi)) {
mode->priv_info->dsc.config.pic_width *= display->ctrl_count;
dsi_panel_update_pps(display->panel);
}
mutex_unlock(&display->display_lock);
vdd->samsung_splash_enabled = false;
LCD_INFO(vdd, "%s : samsung splash disable!!\n", __func__);
#else
display->panel->panel_initialized = true;
DSI_DEBUG("cont splash enabled, display enable not required\n");
dsi_display_panel_id_notification(display);
#endif
return 0;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!(display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS))
LCD_INFO(vdd, "++\n");
#endif
mutex_lock(&display->display_lock);
mode = display->panel->cur_mode;
@ -8875,12 +9310,21 @@ error_disable_panel:
error:
mutex_unlock(&display->display_lock);
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!(display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS))
LCD_INFO(vdd, "--\n");
#endif
return rc;
}
int dsi_display_post_enable(struct dsi_display *display)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = display->panel->panel_private;
if (!(display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS))
LCD_INFO(vdd, "++\n");
#endif
if (!display) {
DSI_ERR("Invalid params\n");
@ -8902,18 +9346,36 @@ int dsi_display_post_enable(struct dsi_display *display)
display->name, rc);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!vdd->dtsi_data.samsung_tcon_clk_on_support) {
/* remove the clk vote for CMD mode panels */
if (display->config.panel_mode == DSI_OP_CMD_MODE) {
SDE_EVT32(SDE_EVTLOG_FUNC_CASE1); // case 04627046
dsi_display_clk_ctrl(display->dsi_clk_handle,
DSI_ALL_CLKS, DSI_CLK_OFF);
}
}
#else
/* remove the clk vote for CMD mode panels */
if (display->config.panel_mode == DSI_OP_CMD_MODE)
dsi_display_clk_ctrl(display->dsi_clk_handle,
DSI_ALL_CLKS, DSI_CLK_OFF);
#endif
mutex_unlock(&display->display_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!(display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS))
LCD_INFO(vdd, "-- \n");
#endif
return rc;
}
int dsi_display_pre_disable(struct dsi_display *display)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = display->panel->panel_private;
#endif
if (!display) {
DSI_ERR("Invalid params\n");
@ -8922,10 +9384,21 @@ int dsi_display_pre_disable(struct dsi_display *display)
mutex_lock(&display->display_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!vdd->dtsi_data.samsung_tcon_clk_on_support) {
/* enable the clk vote for CMD mode panels */
if (display->config.panel_mode == DSI_OP_CMD_MODE) {
SDE_EVT32(SDE_EVTLOG_FUNC_CASE1); // case 04627046
dsi_display_clk_ctrl(display->dsi_clk_handle,
DSI_ALL_CLKS, DSI_CLK_ON);
}
}
#else
/* enable the clk vote for CMD mode panels */
if (display->config.panel_mode == DSI_OP_CMD_MODE)
dsi_display_clk_ctrl(display->dsi_clk_handle,
DSI_ALL_CLKS, DSI_CLK_ON);
#endif
if (display->poms_pending) {
if (display->config.panel_mode == DSI_OP_CMD_MODE)
dsi_panel_switch_cmd_mode_out(display->panel);
@ -8976,12 +9449,18 @@ error:
int dsi_display_disable(struct dsi_display *display)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = display->panel->panel_private;
#endif
if (!display) {
DSI_ERR("Invalid params\n");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO(vdd, "++\n");
#endif
SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&display->display_lock);
@ -9036,6 +9515,9 @@ int dsi_display_disable(struct dsi_display *display)
}
mutex_unlock(&display->display_lock);
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO(vdd, "--\n");
#endif
return rc;
}
@ -9151,12 +9633,19 @@ end:
int dsi_display_unprepare(struct dsi_display *display)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!display) {
DSI_ERR("Invalid params\n");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = display->panel->panel_private;
LCD_INFO(vdd, "++\n");
#endif
SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&display->display_lock);
@ -9217,6 +9706,9 @@ int dsi_display_unprepare(struct dsi_display *display)
dsi_display_unregister_error_handler(display);
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO(vdd, "--\n");
#endif
return rc;
}

View File

@ -16,6 +16,9 @@
#include "sde_dbg.h"
#include "msm_drv.h"
#include "sde_encoder.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#endif
#define to_dsi_bridge(x) container_of((x), struct dsi_bridge, base)
#define to_dsi_state(x) container_of((x), struct dsi_connector_state, base)
@ -59,6 +62,11 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode,
!!(drm_mode->flags & DRM_MODE_FLAG_PHSYNC);
dsi_mode->timing.v_sync_polarity =
!!(drm_mode->flags & DRM_MODE_FLAG_PVSYNC);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
dsi_mode->timing.sot_hs_mode = ss_is_sot_hs_from_drm_mode(drm_mode);
dsi_mode->timing.phs_mode = ss_is_phs_from_drm_mode(drm_mode);
#endif
}
static void msm_parse_mode_priv_info(const struct msm_display_mode *msm_mode,
@ -134,10 +142,17 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
if (dsi_mode->timing.v_sync_polarity)
drm_mode->flags |= DRM_MODE_FLAG_PVSYNC;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
snprintf(drm_mode->name, DRM_DISPLAY_MODE_LEN, "%dx%dx%dx%s%s",
drm_mode->hdisplay, drm_mode->vdisplay,
drm_mode_vrefresh(drm_mode), panel_caps,
dsi_mode->timing.sot_hs_mode ? (dsi_mode->timing.phs_mode ? "PHS" : "HS") : "NS");
#else
/* set mode name */
snprintf(drm_mode->name, DRM_DISPLAY_MODE_LEN, "%dx%dx%d%s",
drm_mode->hdisplay, drm_mode->vdisplay,
drm_mode_vrefresh(drm_mode), panel_caps);
#endif
}
static void dsi_convert_to_msm_mode(const struct dsi_display_mode *dsi_mode,
@ -454,7 +469,25 @@ static bool _dsi_bridge_mode_validate_and_fixup(struct drm_bridge *bridge,
(!(adj_mode->dsi_mode_flags & DSI_MODE_FLAG_POMS_TO_CMD)) &&
(!crtc_state->active_changed ||
display->is_cont_splash_enabled)) {
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
rc = ss_vrr_apply_dsi_bridge_mode_fixup(display, cur_mode,
cur_dsi_mode, adj_mode);
SDE_EVT32(SDE_EVTLOG_FUNC_CASE2,
cur_dsi_mode.timing.refresh_rate,
cur_dsi_mode.timing.sot_hs_mode,
cur_dsi_mode.timing.phs_mode,
adj_mode->timing.refresh_rate,
adj_mode->timing.sot_hs_mode,
adj_mode->timing.phs_mode);
} else if (!dsi_display_mode_match(&cur_dsi_mode, adj_mode,
DSI_MODE_MATCH_FULL_TIMINGS)) {
rc = ss_vrr_save_dsi_bridge_mode_fixup(display, cur_mode,
cur_dsi_mode, adj_mode, crtc_state);
#else
adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS;
#endif
SDE_EVT32(SDE_EVTLOG_FUNC_CASE2,
adj_mode->timing.h_active,
@ -676,6 +709,10 @@ int dsi_conn_get_mode_info(struct drm_connector *connector,
dsi_display->panel->host_config.line_insertion_enable = 0;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mode_info->frame_rate_org = mode_info->frame_rate;
#endif
memcpy(&mode_info->topology, &dsi_mode->priv_info->topology,
sizeof(struct msm_display_topology));
@ -769,6 +806,9 @@ int dsi_conn_get_avr_step_fps(struct drm_connector_state *conn_state)
return -EINVAL;
priv_info = (struct dsi_display_mode_priv_info *)(msm_mode->private);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
DSI_DEBUG("avr_step_fps (%d)\n", priv_info->avr_step_fps);
#endif
return priv_info->avr_step_fps;
}
@ -1192,6 +1232,32 @@ int dsi_connector_get_modes(struct drm_connector *connector, void *data,
for (i = 0; i < count; i++) {
struct drm_display_mode *m;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = NULL;
if (display && display->panel)
vdd = display->panel->panel_private;
if (vdd) {
u32 fps = modes[i].timing.refresh_rate;
bool hs = modes[i].timing.sot_hs_mode;
bool phs = modes[i].timing.phs_mode;
int i;
for (i = 0; i < vdd->disable_vrr_modes_count; i++) {
if (vdd->disable_vrr_modes[i].fps == fps &&
vdd->disable_vrr_modes[i].hs == hs &&
vdd->disable_vrr_modes[i].phs == phs) {
DSI_INFO("disable vrr modes: %d%s\n",
fps, phs ? "PHS" : hs ? "HS" : "NS");
break;
}
}
if (i != vdd->disable_vrr_modes_count)
continue;
}
#endif
memset(&drm_mode, 0x0, sizeof(drm_mode));
dsi_convert_to_drm_mode(&modes[i], &drm_mode);
m = drm_mode_duplicate(connector->dev, &drm_mode);

View File

@ -18,6 +18,12 @@
#include "sde_dsc_helper.h"
#include "sde_vdc_helper.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#include "ss_panel_power.h"
#include "sde_trace.h"
#endif
/**
* topology is currently defined by a set of following 3 values:
* 1. num of layer mixers
@ -78,6 +84,11 @@ static int dsi_panel_vreg_get(struct dsi_panel *panel)
int i;
struct regulator *vreg = NULL;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return ss_panel_parse_powers(ss_get_vdd_from_panel_name(panel->name),
panel->panel_of_node, panel->parent);
#endif
for (i = 0; i < panel->power_info.count; i++) {
vreg = devm_regulator_get(panel->parent,
panel->power_info.vregs[i].vreg_name);
@ -115,6 +126,10 @@ static int dsi_panel_gpio_request(struct dsi_panel *panel)
int rc = 0;
struct dsi_panel_reset_config *r_config = &panel->reset_config;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return 0;
#endif
if (gpio_is_valid(r_config->reset_gpio)) {
rc = gpio_request(r_config->reset_gpio, "reset_gpio");
if (rc) {
@ -259,6 +274,10 @@ static int dsi_panel_reset(struct dsi_panel *panel)
struct dsi_panel_reset_config *r_config = &panel->reset_config;
int i;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return 0;
#endif
if (!gpio_is_valid(r_config->reset_gpio))
goto skip_reset_gpio;
@ -326,7 +345,11 @@ exit:
return rc;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int dsi_panel_set_pinctrl_state(struct dsi_panel *panel, bool enable)
#else
static int dsi_panel_set_pinctrl_state(struct dsi_panel *panel, bool enable)
#endif
{
int rc = 0;
struct pinctrl_state *state;
@ -355,6 +378,10 @@ static int dsi_panel_power_on(struct dsi_panel *panel)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return 0;
#endif
rc = dsi_pwr_enable_regulator(&panel->power_info, true);
if (rc) {
DSI_ERR("[%s] failed to enable vregs, rc=%d\n",
@ -396,6 +423,10 @@ static int dsi_panel_power_off(struct dsi_panel *panel)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return ss_panel_power_off_post_lp11(panel->panel_private);
#endif
if (gpio_is_valid(panel->reset_config.disp_en_gpio))
gpio_set_value(panel->reset_config.disp_en_gpio, 0);
@ -426,16 +457,51 @@ static int dsi_panel_power_off(struct dsi_panel *panel)
return rc;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int dsi_panel_tx_cmd_set(struct dsi_panel *panel,
int type)
#else
static int dsi_panel_tx_cmd_set(struct dsi_panel *panel,
enum dsi_cmd_set_type type)
#endif
{
int rc = 0, i = 0;
ssize_t len;
struct dsi_cmd_desc *cmds;
u32 count;
enum dsi_cmd_set_state state;
#if !IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct dsi_display_mode *mode;
#endif
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = panel->panel_private;
struct dsi_panel_cmd_set *set;
struct dsi_display *display = container_of(panel->host, struct dsi_display, host);
size_t tot_tx_len = 0;
int retry = 5;
if (ss_check_panel_connection(vdd)) {
LCD_INFO(vdd, "skip to send command(type: %d)\n", type);
dump_stack();
return 0;
}
/* ss_get_cmds() gets proper QCT cmds or SS cmds for panel revision. */
set = ss_get_cmds(vdd, type);
if (!set) {
LCD_INFO(vdd, "fail to get commands(%d)\n", type);
return 0;
}
cmds = set->cmds;
count = set->count;
state = set->state;
SDE_EVT32(type, state, count);
mutex_lock(&vdd->cmd_lock);
#else
if (!panel || !panel->cur_mode)
return -EINVAL;
@ -445,6 +511,23 @@ static int dsi_panel_tx_cmd_set(struct dsi_panel *panel,
count = mode->priv_info->cmd_sets[type].count;
state = mode->priv_info->cmd_sets[type].state;
SDE_EVT32(type, state, count);
#endif
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (cmds && (display->ctrl[0].ctrl->secure_mode)) {
for (i = 0 ; i < count ; i++) {
if (cmds->msg.tx_len > DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE) {
LCD_ERR(vdd, "Over DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE at secure_mode type = %d\n", type);
if (type != TX_MDNIE_TUNE)
WARN(1, "unexpected cmd type = %d\n", type);
goto error;
}
cmds++;
}
for (i = 0 ; i < count ; i++)
cmds--;
}
#endif
if (count == 0) {
DSI_DEBUG("[%s] No commands to be sent for state(%d)\n",
@ -455,24 +538,99 @@ static int dsi_panel_tx_cmd_set(struct dsi_panel *panel,
for (i = 0; i < count; i++) {
cmds->ctrl_flags = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (is_ss_cmd_op_skip(vdd, cmds->ss_cmd)) {
DSI_DEBUG("skip tx cmd\n");
cmds++;
continue;
}
/* set last_command if total size is over than MAX DSI FIFO SIZE */
cmds->msg.flags |= MIPI_DSI_MSG_BATCH_COMMAND;
if (tot_tx_len == 0)
tot_tx_len = ALIGN((cmds->msg.tx_len + 4), 4);
if (i < count - 1)
tot_tx_len += ALIGN(((cmds + 1)->msg.tx_len + 4), 4);
/* set last_command if next cmd is read cmd */
if (i < count - 1 && (cmds + 1)->msg.rx_len && (cmds + 1)->msg.rx_buf)
cmds->msg.flags &= ~(MIPI_DSI_MSG_BATCH_COMMAND);
if ((tot_tx_len > DSI_CTRL_MAX_CMD_FET_MEMORY_SIZE) || (i == count-1) || (cmds->post_wait_ms) ||
(cmds->last_command == true)) {
pr_debug("tot %zd is over than max || last cmd set, set last_command", tot_tx_len);
cmds->msg.flags &= ~(MIPI_DSI_MSG_BATCH_COMMAND);
tot_tx_len = 0;
}
if (vdd->not_support_single_tx) /* Some DDI does not support single tx */
cmds->msg.flags &= ~(MIPI_DSI_MSG_BATCH_COMMAND);
if (vdd->dtsi_data.samsung_cmds_unicast)
cmds->msg.flags |= MIPI_DSI_MSG_UNICAST_COMMAND;
/*
Single dsi display uses unicast by default.
Force Broadcast(dual dsi) dispaly use main only read operation,
even if samsung_cmds_unicast is not set.
*/
if (cmds->msg.rx_len && cmds->msg.rx_buf) {
cmds->msg.flags |= MIPI_DSI_MSG_UNICAST_COMMAND; /* Vendor QC uses this flag now */
cmds->ctrl_flags |= DSI_CTRL_CMD_READ;
}
#endif
if (state == DSI_CMD_SET_STATE_LP)
cmds->msg.flags |= MIPI_DSI_MSG_USE_LPM;
if (type == DSI_CMD_SET_VID_SWITCH_OUT)
cmds->msg.flags |= MIPI_DSI_MSG_ASYNC_OVERRIDE;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
while (retry-- >= 0) {
#endif
len = dsi_host_transfer_sub(panel->host, cmds);
if (len < 0) {
rc = len;
DSI_ERR("failed to set cmds(%d), rc=%d\n", type, rc);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
DSI_ERR("transfer retry!(%d)\n", retry);
continue;
#endif
goto error;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
else {
retry = 5;
break;
}
} /* while end */
if (retry < 0) {
if (!vdd->panel_dead)
SDE_DBG_DUMP(SDE_DBG_BUILT_IN_ALL, "panic");
else
DSI_ERR("Skip dump register & panic in ESD\n");
}
/* reset lp_rx_fail_cnt if mipi read is successful */
vdd->lp_rx_fail_cnt = 0;
#endif
if (cmds->post_wait_ms)
usleep_range(cmds->post_wait_ms*1000,
((cmds->post_wait_ms*1000)+10));
cmds++;
}
error:
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mutex_unlock(&vdd->cmd_lock);
#endif
return rc;
}
@ -554,6 +712,9 @@ static int dsi_panel_update_backlight(struct dsi_panel *panel,
int rc = 0;
unsigned long mode_flags = 0;
struct mipi_dsi_device *dsi = NULL;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!panel || (bl_lvl > 0xffff)) {
DSI_ERR("invalid params\n");
@ -569,9 +730,16 @@ static int dsi_panel_update_backlight(struct dsi_panel *panel,
if (panel->bl_config.bl_inverted_dbv)
bl_lvl = (((bl_lvl & 0xff) << 8) | (bl_lvl >> 8));
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = panel->panel_private;
rc = ss_brightness_dcs(panel->panel_private, bl_lvl, BACKLIGHT_NORMAL);
if (rc < 0)
LCD_ERR(vdd, "failed to update dcs backlight:%d\n", bl_lvl);
#else
rc = mipi_dsi_dcs_set_display_brightness(dsi, bl_lvl);
if (rc < 0)
DSI_ERR("failed to update dcs backlight:%d\n", bl_lvl);
#endif
if (unlikely(panel->bl_config.lp_mode))
dsi->mode_flags = mode_flags;
@ -935,6 +1103,11 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode,
mode->v_active, mode->v_front_porch, mode->v_back_porch,
mode->v_sync_width);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mode->sot_hs_mode = utils->read_bool(utils->data, "samsung,mdss-dsi-sot-hs-mode");
mode->phs_mode = utils->read_bool(utils->data, "samsung,mdss-dsi-phs-mode");
#endif
error:
return rc;
}
@ -1977,6 +2150,10 @@ int dsi_panel_create_cmd_packets(const char *data,
goto error_free_payloads;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
cmd[i].ss_txbuf = payload;
#endif
for (j = 0; j < cmd[i].msg.tx_len; j++)
payload[j] = data[7 + j];
@ -2044,7 +2221,9 @@ static int dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd,
DSI_DEBUG("type=%d, name=%s, length=%d\n", type, cmd_set_prop_map[type], length);
#if !IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) /* prevent log flood */
print_hex_dump_debug("", DUMP_PREFIX_NONE, 8, 1, data, length, false);
#endif
rc = dsi_panel_get_cmd_pkt_count(data, length, &packet_count);
if (rc) {
@ -2354,6 +2533,10 @@ static int dsi_panel_parse_power_cfg(struct dsi_panel *panel)
int rc = 0;
char *supply_name;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return 0;
#endif
if (panel->host_config.ext_bridge_mode)
return 0;
@ -2402,6 +2585,10 @@ static int dsi_panel_parse_gpios(struct dsi_panel *panel)
struct dsi_parser_utils *utils = &panel->utils;
char *reset_gpio_name, *mode_set_gpio_name;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
return 0;
#endif
if (!strcmp(panel->type, "primary")) {
reset_gpio_name = "qcom,platform-reset-gpio";
mode_set_gpio_name = "qcom,panel-mode-gpio";
@ -2576,6 +2763,18 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel)
panel->bl_config.brightness_max_level = val;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
rc = utils->read_u32(utils->data, "qcom,mdss-brightness-default-level",
&val);
if (rc) {
pr_debug("[%s] brigheness-default-level unspecified, defaulting to 255\n",
panel->name);
panel->bl_config.bl_level = 255;
} else {
panel->bl_config.bl_level = val;
}
#endif
panel->bl_config.bl_inverted_dbv = utils->read_bool(utils->data,
"qcom,mdss-dsi-bl-inverted-dbv");
@ -3561,6 +3760,11 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel)
esd_config->status_mode = ESD_MODE_SW_BTA;
} else if (!strcmp(string, "reg_read")) {
esd_config->status_mode = ESD_MODE_REG_READ;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
} else if (!strcmp(string, "irq_check")) {
esd_config->status_mode = ESD_MODE_PANEL_IRQ;
DSI_INFO("%s : irq_check!!\n", __func__);
#endif
} else if (!strcmp(string, "te_signal_check")) {
if (panel->panel_mode == DSI_OP_CMD_MODE) {
esd_config->status_mode = ESD_MODE_PANEL_TE;
@ -3590,6 +3794,10 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel)
esd_mode = "register_read";
} else if (panel->esd_config.status_mode == ESD_MODE_SW_BTA) {
esd_mode = "bta_trigger";
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
} else if (panel->esd_config.status_mode == ESD_MODE_PANEL_IRQ) {
esd_mode = "panel_irq";
#endif
} else if (panel->esd_config.status_mode == ESD_MODE_PANEL_TE) {
esd_mode = "te_check";
}
@ -3607,6 +3815,10 @@ static void dsi_panel_update_util(struct dsi_panel *panel,
struct device_node *parser_node)
{
struct dsi_parser_utils *utils = &panel->utils;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct dsi_parser_utils *self_disp_utils = &panel->self_display_utils;
struct dsi_parser_utils *mafpc_utils = &panel->mafpc_utils;
#endif
if (parser_node) {
*utils = *dsi_parser_get_parser_utils();
@ -3621,6 +3833,15 @@ static void dsi_panel_update_util(struct dsi_panel *panel,
utils->data = panel->panel_of_node;
end:
utils->node = panel->panel_of_node;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
*self_disp_utils = *dsi_parser_get_of_utils();
self_disp_utils->data = panel->self_display_of_node;
self_disp_utils->node = panel->self_display_of_node;
*mafpc_utils = *dsi_parser_get_of_utils();
mafpc_utils->data = panel->mafpc_of_node;
mafpc_utils->node = panel->mafpc_of_node;
#endif
}
static int dsi_panel_vm_stub(struct dsi_panel *panel)
@ -3664,6 +3885,10 @@ struct dsi_panel *dsi_panel_get(struct device *parent,
struct dsi_parser_utils *utils;
const char *panel_physical_type;
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct device_node *self_display_node = of_parse_phandle(of_node, "ss,self_display", 0);
struct device_node *mafpc_node = of_parse_phandle(of_node, "ss,mafpc", 0);
#endif
panel = kzalloc(sizeof(*panel), GFP_KERNEL);
if (!panel)
@ -3672,6 +3897,10 @@ struct dsi_panel *dsi_panel_get(struct device *parent,
dsi_panel_setup_vm_ops(panel, trusted_vm_env);
panel->panel_of_node = of_node;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
panel->self_display_of_node = self_display_node;
panel->mafpc_of_node = mafpc_node;
#endif
panel->parent = parent;
panel->type = type;
@ -3848,6 +4077,10 @@ int dsi_panel_drv_init(struct dsi_panel *panel,
goto error_gpio_release;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_init(panel);
#endif
goto exit;
error_gpio_release:
@ -4208,6 +4441,9 @@ int dsi_panel_get_mode(struct dsi_panel *panel,
int rc = 0, num_timings;
int traffic_mode;
void *utils_data = NULL;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!panel || !mode) {
DSI_ERR("invalid params\n");
@ -4321,6 +4557,17 @@ int dsi_panel_get_mode(struct dsi_panel *panel,
if (rc)
DSI_ERR("failed to partial update caps, rc=%d\n", rc);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = panel->panel_private;
vdd->num_of_intf = mode->priv_info->topology.num_intf;
LCD_INFO_ONCE(vdd, "vdd->num_of_intf = %d\n", vdd->num_of_intf);
if (mode->timing.qsync_min_fps) {
LCD_INFO(vdd, "index(%d) : mdp_transfer_time_us(%d), qsync fs(%d)\n",
index, mode->priv_info->mdp_transfer_time_us,
mode->timing.qsync_min_fps);
}
#endif
parse_fail:
utils->data = utils_data;
@ -4334,6 +4581,9 @@ int dsi_panel_get_host_cfg_for_mode(struct dsi_panel *panel,
{
int rc = 0;
struct dsi_dyn_clk_caps *dyn_clk_caps = &panel->dyn_clk_caps;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!panel || !mode || !config) {
DSI_ERR("invalid params\n");
@ -4370,6 +4620,12 @@ int dsi_panel_get_host_cfg_for_mode(struct dsi_panel *panel,
config->bit_clk_rate_hz_override = mode->priv_info->clk_rate_hz;
config->esc_clk_rate_hz = 19200000;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = panel->panel_private;
if (vdd->dtsi_data.samsung_esc_clk_128M)
config->esc_clk_rate_hz = 12800000;
#endif
mutex_unlock(&panel->panel_lock);
return rc;
}
@ -4406,6 +4662,11 @@ int dsi_panel_update_pps(struct dsi_panel *panel)
struct dsi_panel_cmd_set *set = NULL;
struct dsi_display_mode_priv_info *priv_info = NULL;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* Do not use QC PPS -> add PPS cmds in on_seq */
return 0;
#endif
if (!panel || !panel->cur_mode) {
DSI_ERR("invalid params\n");
return -EINVAL;
@ -4475,6 +4736,9 @@ int dsi_panel_set_lp1(struct dsi_panel *panel)
if (rc)
DSI_ERR("[%s] failed to send DSI_CMD_SET_LP1 cmd, rc=%d\n",
panel->name, rc);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_low_power_config(panel->panel_private, true);
#endif
exit:
mutex_unlock(&panel->panel_lock);
return rc;
@ -4497,6 +4761,9 @@ int dsi_panel_set_lp2(struct dsi_panel *panel)
if (rc)
DSI_ERR("[%s] failed to send DSI_CMD_SET_LP2 cmd, rc=%d\n",
panel->name, rc);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_low_power_config(panel->panel_private, true);
#endif
exit:
mutex_unlock(&panel->panel_lock);
return rc;
@ -4527,6 +4794,9 @@ int dsi_panel_set_nolp(struct dsi_panel *panel)
if (rc)
DSI_ERR("[%s] failed to send DSI_CMD_SET_NOLP cmd, rc=%d\n",
panel->name, rc);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_low_power_config(panel->panel_private, false);
#endif
exit:
mutex_unlock(&panel->panel_lock);
return rc;
@ -4543,6 +4813,10 @@ int dsi_panel_prepare(struct dsi_panel *panel)
mutex_lock(&panel->panel_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_power_on_post_lp11(panel->panel_private);
#endif
if (panel->lp11_init) {
rc = dsi_panel_power_on(panel);
if (rc) {
@ -4644,6 +4918,9 @@ int dsi_panel_send_qsync_on_dcs(struct dsi_panel *panel,
int ctrl_idx)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!panel) {
DSI_ERR("invalid params\n");
@ -4652,11 +4929,24 @@ int dsi_panel_send_qsync_on_dcs(struct dsi_panel *panel,
mutex_lock(&panel->panel_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
DSI_INFO("ctrl:%d qsync on\n", ctrl_idx);
vdd = panel->panel_private;
if (vdd) {
if (!SS_IS_CMDS_NULL(ss_get_cmds(vdd, TX_EARLY_TE))) {
vdd->early_te = true;
vdd->check_early_te = CHECK_EARLY_TE_COUNT;
if (vdd->panel_state != PANEL_PWR_LPM)
ss_send_cmd(vdd, TX_EARLY_TE);
}
}
#else
DSI_DEBUG("ctrl:%d qsync on\n", ctrl_idx);
rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_QSYNC_ON);
if (rc)
DSI_ERR("[%s] failed to send DSI_CMD_SET_QSYNC_ON cmds rc=%d\n",
panel->name, rc);
#endif
mutex_unlock(&panel->panel_lock);
return rc;
@ -4666,6 +4956,9 @@ int dsi_panel_send_qsync_off_dcs(struct dsi_panel *panel,
int ctrl_idx)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!panel) {
DSI_ERR("invalid params\n");
@ -4674,11 +4967,24 @@ int dsi_panel_send_qsync_off_dcs(struct dsi_panel *panel,
mutex_lock(&panel->panel_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
DSI_INFO("ctrl:%d qsync off\n", ctrl_idx);
vdd = panel->panel_private;
if (vdd) {
if (!SS_IS_CMDS_NULL(ss_get_cmds(vdd, TX_EARLY_TE))) {
vdd->early_te = false;
if (vdd->panel_state != PANEL_PWR_LPM)
ss_send_cmd(vdd, TX_EARLY_TE);
}
}
#else
DSI_DEBUG("ctrl:%d qsync off\n", ctrl_idx);
rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_QSYNC_OFF);
if (rc)
DSI_ERR("[%s] failed to send DSI_CMD_SET_QSYNC_OFF cmds rc=%d\n",
panel->name, rc);
#endif
mutex_unlock(&panel->panel_lock);
return rc;
@ -4813,6 +5119,35 @@ int dsi_panel_switch(struct dsi_panel *panel)
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (panel->panel_private) {
struct samsung_display_driver_data *vdd = panel->panel_private;
if (vdd->vrr.support_vrr_based_bl) {
/* Sometimes, GFX HAL sends DMS that inlcudes multi resolution and VRR,
* at one DMS commands. So, always handler DMS here.
*/
ss_panel_dms_switch(vdd);
return 0;
}
/* In QCT original VRR mode, below variables is meaningless..
* But, to keep latest information for debugging,
* update current vrr variables.
*/
vdd->vrr.cur_refresh_rate = vdd->vrr.adjusted_refresh_rate;
vdd->vrr.cur_sot_hs_mode = vdd->vrr.adjusted_sot_hs_mode;
vdd->vrr.cur_phs_mode = vdd->vrr.adjusted_phs_mode;
vdd->vrr.cur_h_active = vdd->vrr.adjusted_h_active;
vdd->vrr.cur_v_active = vdd->vrr.adjusted_v_active;
/* Do we have to change param only when the HS<->NORMAL be changed?
* No problem to notify VRR change in panel not supporting VRR?
*/
ss_set_vrr_switch(vdd, true);
}
#endif
mutex_lock(&panel->panel_lock);
rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_TIMING_SWITCH);
@ -4821,6 +5156,11 @@ int dsi_panel_switch(struct dsi_panel *panel)
panel->name, rc);
mutex_unlock(&panel->panel_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_send_cmd(panel->panel_private, TX_TIMING_SWITCH);
#endif
return rc;
}
@ -4880,6 +5220,9 @@ int dsi_panel_enable(struct dsi_panel *panel)
panel->panel_initialized = true;
error:
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_on(panel->panel_private);
#endif
mutex_unlock(&panel->panel_lock);
return rc;
}
@ -4920,6 +5263,10 @@ int dsi_panel_pre_disable(struct dsi_panel *panel)
if (gpio_is_valid(panel->bl_config.en_gpio))
gpio_set_value(panel->bl_config.en_gpio, 0);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_off_pre(panel->panel_private);
#endif
rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_PRE_OFF);
if (rc) {
DSI_ERR("[%s] failed to send DSI_CMD_SET_PRE_OFF cmds, rc=%d\n",
@ -4935,14 +5282,28 @@ error:
int dsi_panel_disable(struct dsi_panel *panel)
{
int rc = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
#endif
if (!panel) {
DSI_ERR("invalid params\n");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
vdd = panel->panel_private;
LCD_INFO(vdd, "++\n");
#endif
mutex_lock(&panel->panel_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!ss_panel_attach_get(panel->panel_private)) {
LCD_INFO(vdd, "PBA booting, skip to disable panel\n");
goto skip_cmd_tx;
}
#endif
/* Avoid sending panel off commands when ESD recovery is underway */
if (!atomic_read(&panel->esd_recovery_pending)) {
/*
@ -4967,10 +5328,18 @@ int dsi_panel_disable(struct dsi_panel *panel)
rc = 0;
}
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
skip_cmd_tx:
ss_panel_off_post(panel->panel_private);
#endif
panel->panel_initialized = false;
panel->power_mode = SDE_MODE_DPMS_OFF;
mutex_unlock(&panel->panel_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
LCD_INFO(vdd, "--\n");
#endif
return rc;
}
@ -4993,6 +5362,11 @@ int dsi_panel_unprepare(struct dsi_panel *panel)
}
error:
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ss_panel_power_off_pre_lp11(panel->panel_private);
#endif
mutex_unlock(&panel->panel_lock);
return rc;
}

View File

@ -177,6 +177,9 @@ enum esd_check_status_mode {
ESD_MODE_PANEL_TE,
ESD_MODE_SW_SIM_SUCCESS,
ESD_MODE_SW_SIM_FAILURE,
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ESD_MODE_PANEL_IRQ,
#endif
ESD_MODE_MAX
};
@ -275,6 +278,14 @@ struct dsi_panel {
enum dsi_panel_physical_type panel_type;
struct dsi_panel_ops panel_ops;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
void *panel_private;
struct device_node *self_display_of_node;
struct dsi_parser_utils self_display_utils;
struct device_node *mafpc_of_node;
struct dsi_parser_utils mafpc_utils;
#endif
};
static inline bool dsi_panel_ulps_feature_enabled(struct dsi_panel *panel)
@ -411,4 +422,12 @@ int dsi_panel_create_cmd_packets(const char *data, u32 length, u32 count,
void dsi_panel_destroy_cmd_packets(struct dsi_panel_cmd_set *set);
void dsi_panel_dealloc_cmd_packets(struct dsi_panel_cmd_set *set);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#define SS_CMD_PROP_STR_LEN (100)
int dsi_panel_set_pinctrl_state(struct dsi_panel *panel, bool enable);
int dsi_panel_tx_cmd_set(struct dsi_panel *panel, int type);
#endif
#endif /* _DSI_PANEL_H_ */

View File

@ -1444,6 +1444,42 @@ void dsi_phy_set_continuous_clk(struct msm_dsi_phy *phy, bool enable)
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
void dsi_phy_store_str(struct msm_dsi_phy *phy, u32 *val)
{
if (phy->hw.ops.store_str)
phy->hw.ops.store_str(&phy->hw, val);
}
u32 dsi_phy_show_str(struct msm_dsi_phy *phy)
{
u32 ret = 0;
if (phy->hw.ops.show_str)
ret = phy->hw.ops.show_str(&phy->hw);
return ret;
}
void dsi_phy_store_vreg(struct msm_dsi_phy *phy, u32 *val)
{
if (phy->hw.ops.store_vreg)
phy->hw.ops.store_vreg(&phy->hw, val);
}
u32 dsi_phy_show_vreg(struct msm_dsi_phy *phy)
{
u32 ret = 0;
if (phy->hw.ops.show_vreg)
ret = phy->hw.ops.show_vreg(&phy->hw);
return ret;
}
void dsi_phy_store_emphasis(struct msm_dsi_phy *phy, u32 *val)
{
if (phy->hw.ops.store_emphasis)
phy->hw.ops.store_emphasis(&phy->hw, val);
}
#endif
/**
* dsi_phy_pll_parse_dfps_data() - parse dfps data for PLL
* @phy: DSI PHY handle

View File

@ -419,4 +419,13 @@ int dsi_phy_dynclk_configure(struct msm_dsi_phy *phy);
* @phy: DSI PHY handle
*/
void dsi_phy_pll_parse_dfps_data(struct msm_dsi_phy *phy);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
void dsi_phy_store_str(struct msm_dsi_phy *phy, u32 *val);
u32 dsi_phy_show_str(struct msm_dsi_phy *phy);
void dsi_phy_store_vreg(struct msm_dsi_phy *phy, u32 *val);
u32 dsi_phy_show_vreg(struct msm_dsi_phy *phy);
void dsi_phy_store_emphasis(struct msm_dsi_phy *phy, u32 *val);
#endif
#endif /* _DSI_PHY_H_ */

View File

@ -385,6 +385,13 @@ struct dsi_phy_hw_ops {
*/
int (*pll_toggle)(void *pll, bool prepare);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
void (*store_str)(struct dsi_phy_hw *phy, u32 *val);
u32 (*show_str)(struct dsi_phy_hw *phy);
void (*store_vreg)(struct dsi_phy_hw *phy, u32 *val);
u32 (*show_vreg)(struct dsi_phy_hw *phy);
void (*store_emphasis)(struct dsi_phy_hw *phy, u32 *val);
#endif
};
/**
@ -413,6 +420,10 @@ struct dsi_phy_hw {
DECLARE_BITMAP(feature_map, DSI_PHY_MAX_FEATURES);
struct dsi_phy_hw_ops ops;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int display_index; /* primary display or secondary display */
#endif
};
/**

View File

@ -12,6 +12,10 @@
#include "dsi_phy_hw.h"
#include "dsi_catalog.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#endif
#define DSIPHY_CMN_REVISION_ID0 0x000
#define DSIPHY_CMN_REVISION_ID1 0x004
#define DSIPHY_CMN_REVISION_ID2 0x008
@ -229,6 +233,118 @@ void dsi_phy_hw_v5_0_commit_phy_timing(struct dsi_phy_hw *phy,
DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_13, timing->lane_v4[13]);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* To store driving streng for Motto tool */
void dsi_phy_hw_v5_0_store_str(struct dsi_phy_hw *phy, u32 *val)
{
u32 hstx_str = 0;
u32 cal_sel = 0;
/* The register setting range is from 'b0000 (weakest) to 'b1111 (strongest). */
DSI_PHY_INFO(phy, "base : 0x%X, val : 0x%X\n", phy->base, *val);
DSI_W32(phy, DSIPHY_CMN_GLBL_HSTX_STR_CTRL_0, *val);
hstx_str = DSI_R32(phy, DSIPHY_CMN_GLBL_HSTX_STR_CTRL_0);
/* DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL[0] needs to be set to bit1
* to select strength override value from DSIPHY_CMN_GLBL_HSTX_STR_CTRL_0.
*/
cal_sel = DSI_R32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL);
cal_sel |= BIT(0);
DSI_W32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL, cal_sel);
DSI_PHY_INFO(phy, "applied hstx : 0x%X, cal_sel : 0x%X\n", hstx_str, cal_sel);
}
u32 dsi_phy_hw_v5_0_show_str(struct dsi_phy_hw *phy)
{
u32 hstx_str = 0;
hstx_str = DSI_R32(phy, DSIPHY_CMN_GLBL_HSTX_STR_CTRL_0);
DSI_PHY_INFO(phy, "cur base : 0x%X, hstx_str : 0x%X (0x00 ~ 0xFF)\n", phy->base, hstx_str);
return hstx_str;
}
/* CMN_VREG_CTRL_0[1:0]: For D-PHY, it adjusts the Tx DC level of Vhigh and Vlow -> cotrol whole */
void dsi_phy_hw_v5_0_store_vreg(struct dsi_phy_hw *phy, u32 *val)
{
u32 vreg_ctrl_0_rd, vreg_ctrl_0_str;
vreg_ctrl_0_rd = DSI_R32(phy, DSIPHY_CMN_VREG_CTRL_0); // | BIT(2) | BIT(5);
vreg_ctrl_0_str = *val;
DSI_PHY_INFO(phy, "base : 0x%X, val:0x%X, vreg_ctrl_0:0x%X->0x%X\n",
phy->base, *val, vreg_ctrl_0_rd, vreg_ctrl_0_str);
DSI_W32(phy, DSIPHY_CMN_VREG_CTRL_0, vreg_ctrl_0_str);
}
/* CMN_VREG_CTRL_0[1:0]: For D-PHY, it adjusts the Tx DC level of Vhigh and Vlow -> whole control */
u32 dsi_phy_hw_v5_0_show_vreg(struct dsi_phy_hw *phy)
{
u32 val = 0;
val = DSI_R32(phy, DSIPHY_CMN_VREG_CTRL_0);
DSI_PHY_INFO(phy, "cur base : 0x%X, vreg_ctrl_0 : 0x%02X \n", phy->base, val);
return val;
}
/* To store de-emphasis adjusted for Motto tool */
void dsi_phy_hw_v5_0_store_emphasis(struct dsi_phy_hw *phy, u32 *val)
{
u32 cal_sel = 0;
u32 cmn_ctrl_2 = 0;
u32 read[2];
struct samsung_display_driver_data *vdd = ss_get_vdd(phy->index);
DSI_PHY_INFO(phy, "val:0x%x (ndx:%x)\n", *val, phy->index);
if ((!vdd->motto_info.motto_emphasis) && (!vdd->motto_info.init_backup)) {
/* backup default data */
vdd->motto_info.cal_sel_init =
DSI_R32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL);
vdd->motto_info.cmn_ctrl2_init = DSI_R32(phy, DSIPHY_CMN_CTRL_2);
DSI_PHY_INFO(phy, "backup sel:%x(0), cmn:%x(40)\n",
vdd->motto_info.cal_sel_init, vdd->motto_info.cmn_ctrl2_init);
vdd->motto_info.init_backup = true;
}
/* Common for both DSI_PHY_VERSION_4_0~4_3 */
cal_sel = DSI_R32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL);
if (*val == 0x01) {
/* Use DSIPHY_CMN_CTRL_2 to enable de-emphasis, by asserting bit[2] and bit[5].
* To use precalibrated values,adjust DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL bit[2]
* to change between low (bit0) and high (bit1) EQ.
*/
/* cmn_ctrl_2 : assert [2],[5] */
cmn_ctrl_2 = vdd->motto_info.cmn_ctrl2_init | BIT(2) | BIT(5);
/* cal_sel : assert [2] */
cal_sel |= BIT(2);
} else if (*val == 0) { /* restore init(set 0) value */
if (!vdd->motto_info.init_backup) {
DSI_PHY_ERR(phy, "no init backed up.\n");
} else {
cmn_ctrl_2 = vdd->motto_info.cmn_ctrl2_init;
cal_sel &= ~(BIT(2) | BIT(5));
}
vdd->motto_info.init_backup = false;
DSI_PHY_INFO(phy, "restore cmn_ctrl2:%x, cal_sel:%x\n", cmn_ctrl_2, cal_sel);
} else
DSI_PHY_ERR(phy, "invalid val:%x\n", *val);
DSI_W32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL, cal_sel);
DSI_W32(phy, DSIPHY_CMN_CTRL_2, cmn_ctrl_2);
read[0] = DSI_R32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL);
read[1] = DSI_R32(phy, DSIPHY_CMN_CTRL_2);
DSI_PHY_INFO(phy, "applied sel:%x, cmn:%x\n", read[0], read[1]);
/* store curr to use enable fc if modified */
vdd->motto_info.cal_sel_curr = read[0];
vdd->motto_info.cmn_ctrl2_curr = read[1];
}
#endif
/**
* calc_cmn_lane_ctrl0() - Calculate the value to be set for
* DSIPHY_CMN_LANE_CTRL0 register.
@ -293,6 +409,64 @@ static void dsi_phy_hw_cphy_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *c
DSI_W32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL, 0x00);
DSI_W32(phy, DSIPHY_CMN_GLBL_LPTX_STR_CTRL, 0x55);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (phy->version == DSI_PHY_VERSION_4_1) {
struct samsung_display_driver_data *vdd;
if (phy->display_index == PRIMARY_DISPLAY_NDX)
vdd = ss_get_vdd(PRIMARY_DISPLAY_NDX);
else
vdd = ss_get_vdd(SECONDARY_DISPLAY_NDX);
if (test_bit(SS_PHY_CMN_VREG_CTRL_0, vdd->ss_phy_ctrl_bit)) {
DSI_W32(phy, DSIPHY_CMN_VREG_CTRL_0,
vdd->ss_phy_ctrl_data[SS_PHY_CMN_VREG_CTRL_0]);
LCD_DEBUG(vdd, "DSIPHY_CMN_VREG_CTRL_0 : 0x%x\n", DSI_R32(phy, DSIPHY_CMN_VREG_CTRL_0));
}
if (test_bit(SS_PHY_CMN_CTRL_2, vdd->ss_phy_ctrl_bit)) {
DSI_W32(phy, DSIPHY_CMN_CTRL_2,
vdd->ss_phy_ctrl_data[SS_PHY_CMN_CTRL_2]);
LCD_DEBUG(vdd, "DSIPHY_CMN_CTRL_2 : 0x%x\n", DSI_R32(phy, DSIPHY_CMN_CTRL_2));
}
if (test_bit(SS_PHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL, vdd->ss_phy_ctrl_bit)) {
DSI_W32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL,
vdd->ss_phy_ctrl_data[SS_PHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL]);
LCD_DEBUG(vdd, "DSIPHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL : 0x%x\n",
DSI_R32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL));
}
if (test_bit(SS_PHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL, vdd->ss_phy_ctrl_bit)) {
DSI_W32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL,
vdd->ss_phy_ctrl_data[SS_PHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL]);
LCD_DEBUG(vdd, "DSIPHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL : 0x%x\n",
DSI_R32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL));
}
if (test_bit(SS_PHY_CMN_GLBL_RESCODE_OFFSET_MID_CTRL, vdd->ss_phy_ctrl_bit)) {
DSI_W32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_MID_CTRL,
vdd->ss_phy_ctrl_data[SS_PHY_CMN_GLBL_RESCODE_OFFSET_MID_CTRL]);
LCD_DEBUG(vdd, "DSIPHY_CMN_GLBL_RESCODE_OFFSET_MID_CTRL : 0x%x\n",
DSI_R32(phy, DSIPHY_CMN_GLBL_RESCODE_OFFSET_MID_CTRL));
}
if (test_bit(SS_PHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL, vdd->ss_phy_ctrl_bit)) {
DSI_W32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL,
vdd->ss_phy_ctrl_data[SS_PHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL]);
LCD_DEBUG(vdd, "DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL : 0x%x\n",
DSI_R32(phy, DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL));
}
}
//TODO Add 4_3 version (5)
#endif
/* Remove power down from all blocks */
DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f);
@ -348,6 +522,16 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *c
bool split_link_enabled;
u32 lanes_per_sublink;
u32 cmn_lane_ctrl0 = 0;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd = ss_get_vdd(phy->display_index);
if (!vdd) {
LCD_ERR(vdd, "inavlid vdd idx %d\n", phy->display_index);
return ;
}
LCD_INFO_ONCE(vdd, "PHY_%d (%X), display_index:%d ver : %d, bit_clk : %d\n",
phy->index, phy->base, phy->display_index , phy->version, cfg->bit_clk_rate_hz);
#endif
/* Alter PHY configurations if data rate less than 1.5GHZ*/
if (cfg->bit_clk_rate_hz <= 1500000000)
@ -359,6 +543,28 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *c
glbl_str_swi_cal_sel_ctrl = 0x00;
glbl_hstx_str_ctrl_0 = 0x88;
/* SM8550 TODO:Compare with 8650 phy tune guide */
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* Set if Motto values had set */
if (vdd->motto_info.motto_swing) {
glbl_hstx_str_ctrl_0 = vdd->motto_info.motto_swing;
/* DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL[0] needs to be set to 'b1
* to select strength override value from DSIPHY_CMN_GLBL_HSTX_STR_CTRL_0.
*/
glbl_str_swi_cal_sel_ctrl |= 0x01;
LCD_INFO_ONCE(vdd, "motto_swing : 0x%X, cal_sel : %X\n",
vdd->motto_info.motto_swing, glbl_str_swi_cal_sel_ctrl);
}
if (vdd->motto_info.motto_emphasis) {
/* To use precalibrated values,adjust DSIPHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL bit[2]
* to change between low (bit0) and high (bit1) EQ.
*/
glbl_str_swi_cal_sel_ctrl |= BIT(2);
LCD_INFO_ONCE(vdd, "motto_emphasis cmn_ctrl2 : 0x%X cal_sel : %X\n",
vdd->motto_info.cmn_ctrl2_curr, glbl_str_swi_cal_sel_ctrl);
}
#endif
split_link_enabled = cfg->split_link.enabled;
lanes_per_sublink = cfg->split_link.lanes_per_sublink;
@ -383,6 +589,15 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *c
/* Configure PHY lane swap */
dsi_phy_hw_v5_0_lane_swap_config(phy, &cfg->lane_map);
/* SM8550 TODO:Compare with 8650 phy tune guide */
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* CMN_VREG_CTRL_0[1:0]: For D-PHY */
if (vdd->motto_info.vreg_ctrl_0) {
LCD_INFO_ONCE(vdd, "vreg_ctrl_0 : 0x%02X -> 0x%02X\n", vreg_ctrl_0, vdd->motto_info.vreg_ctrl_0);
vreg_ctrl_0 = vdd->motto_info.vreg_ctrl_0;
}
#endif
/* Enable LDO */
DSI_W32(phy, DSIPHY_CMN_VREG_CTRL_0, vreg_ctrl_0);
DSI_W32(phy, DSIPHY_CMN_VREG_CTRL_1, 0x19);
@ -416,8 +631,18 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *c
DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, cmn_lane_ctrl0);
}
/* SM8550 TODO:Compare with 8650 phy tune guide */
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* Set if Motto values had set */
if (vdd->motto_info.motto_emphasis) {
DSI_W32(phy, DSIPHY_CMN_CTRL_2, vdd->motto_info.cmn_ctrl2_curr);
} else
/* Select full-rate mode */
DSI_W32(phy, DSIPHY_CMN_CTRL_2, 0x40);
#else
/* Select full-rate mode */
DSI_W32(phy, DSIPHY_CMN_CTRL_2, 0x40);
#endif
switch (cfg->pll_source) {
case DSI_PLL_SOURCE_STANDALONE:

View File

@ -206,6 +206,10 @@ static void dsi_pll_config_slave(struct dsi_pll_resource *rsc)
rsc->slave ? "configured" : "absent");
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
extern int vdd_pll_ssc_disabled;
#endif
static void dsi_pll_setup_config(struct dsi_pll_4nm *pll, struct dsi_pll_resource *rsc)
{
struct dsi_pll_config *config = &pll->pll_configuration;
@ -233,6 +237,13 @@ static void dsi_pll_setup_config(struct dsi_pll_4nm *pll, struct dsi_pll_resourc
if (rsc->ssc_ppm)
config->ssc_offset = rsc->ssc_ppm;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (vdd_pll_ssc_disabled) {
pr_err_once("[5nm] disable pll ssc %d\n", vdd_pll_ssc_disabled);
config->enable_ssc = false;
}
#endif
}
static void dsi_pll_calc_dec_frac(struct dsi_pll_4nm *pll, struct dsi_pll_resource *rsc)

View File

@ -29,6 +29,10 @@
#include <linux/dma-fence-chain.h>
#endif
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#endif
#define MULTIPLE_CONN_DETECTED(x) (x > 1)
struct msm_commit {
@ -251,6 +255,23 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
else
funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
{
/*
notify registered clients about suspend event
This noti is triggered before panel power off & DSI_CMD_SET_OFF.
*/
int blank = FB_BLANK_POWERDOWN;
if (encoder->encoder_type == DRM_MODE_ENCODER_DSI &&
(connector->state->crtc &&
connector->state->crtc->state->active_changed))
__msm_drm_notifier_call_chain(FB_EVENT_BLANK, &blank);
else
pr_debug("%s %d\n", __func__, encoder->encoder_type);
}
#endif
drm_bridge_chain_post_disable(bridge);
}
@ -494,6 +515,24 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
funcs->enable(encoder);
else
funcs->commit(encoder);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
{
/*
notify registered clients about resume event.
This noti is triggered after panel power on & DSI_CMD_SET_ON.
*/
int blank = FB_BLANK_UNBLANK;
/* notify only in case state is changed (off -> on) */
if (encoder->encoder_type == DRM_MODE_ENCODER_DSI &&
(connector->state->crtc &&
connector->state->crtc->state->active_changed))
__msm_drm_notifier_call_chain(FB_EVENT_BLANK, &blank);
else
pr_debug("%s %d\n", __func__, encoder->encoder_type);
}
#endif
}
if (kms && kms->funcs && kms->funcs->commit) {
@ -613,6 +652,10 @@ int msm_atomic_prepare_fb(struct drm_plane *plane,
return msm_framebuffer_prepare(new_state->fb, kms->aspace);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int ss_get_vdd_ndx_from_state(struct drm_atomic_state *old_state);
#endif
/* The (potentially) asynchronous part of the commit. At this point
* nothing can fail short of armageddon.
*/
@ -622,6 +665,10 @@ static void complete_commit(struct msm_commit *c)
struct drm_device *dev = state->dev;
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int ndx;
struct samsung_display_driver_data *vdd;
#endif
drm_atomic_helper_wait_for_fences(dev, state, false);
@ -649,6 +696,13 @@ static void complete_commit(struct msm_commit *c)
msm_atomic_wait_for_commit_done(dev, state);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ndx = ss_get_vdd_ndx_from_state(state);
vdd = ss_get_vdd(ndx);
if (vdd)
ss_event_frame_update_post(vdd);
#endif
drm_atomic_helper_cleanup_planes(dev, state);
kms->funcs->complete_commit(kms, state);
@ -778,12 +832,23 @@ int msm_atomic_commit(struct drm_device *dev,
struct drm_plane *plane;
struct drm_plane_state *old_plane_state, *new_plane_state;
int i, ret;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct samsung_display_driver_data *vdd;
int ndx;
#endif
if (!priv || priv->shutdown_in_progress) {
DRM_ERROR("priv is null or shutdwon is in-progress\n");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
ndx = ss_get_vdd_ndx_from_state(state);
vdd = ss_get_vdd(ndx);
if (vdd)
ss_event_frame_update_pre(vdd);
#endif
SDE_ATRACE_BEGIN("atomic_commit");
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret) {

View File

@ -92,6 +92,27 @@
} while (0)
static DEFINE_MUTEX(msm_release_lock);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
static BLOCKING_NOTIFIER_HEAD(msm_drm_notifier_list);
int msm_drm_register_notifier_client(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&msm_drm_notifier_list, nb);
}
EXPORT_SYMBOL(msm_drm_register_notifier_client);
int msm_drm_unregister_notifier_client(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&msm_drm_notifier_list, nb);
}
EXPORT_SYMBOL(msm_drm_unregister_notifier_client);
int __msm_drm_notifier_call_chain(unsigned long event, void *data)
{
return blocking_notifier_call_chain(&msm_drm_notifier_list,
event, data);
}
#endif
static void msm_fb_output_poll_changed(struct drm_device *dev)
{
@ -1064,6 +1085,10 @@ void msm_atomic_flush_display_threads(struct msm_drm_private *priv)
/*
* DRM operations:
*/
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
struct msm_file_private *msm_ioctl_power_ctrl_ctx;
DEFINE_MUTEX(msm_ioctl_power_ctrl_ctx_lock);
#endif
static int context_init(struct drm_device *dev, struct drm_file *file)
{
@ -1096,6 +1121,13 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
static void context_close(struct msm_file_private *ctx)
{
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mutex_lock(&msm_ioctl_power_ctrl_ctx_lock);
if (msm_ioctl_power_ctrl_ctx == ctx)
msm_ioctl_power_ctrl_ctx = NULL;
mutex_unlock(&msm_ioctl_power_ctrl_ctx_lock);
#endif
kfree(ctx);
}
@ -1696,6 +1728,12 @@ int msm_ioctl_power_ctrl(struct drm_device *dev, void *data,
priv = dev->dev_private;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
mutex_lock(&msm_ioctl_power_ctrl_ctx_lock);
msm_ioctl_power_ctrl_ctx = ctx;
mutex_unlock(&msm_ioctl_power_ctrl_ctx_lock);
#endif
mutex_lock(&ctx->power_lock);
old_cnt = ctx->enable_refcnt;
@ -2040,6 +2078,12 @@ static int add_display_components(struct device *dev,
node = of_parse_phandle(np, "connectors", i);
if (!node)
break;
#ifndef CONFIG_SECDP
if (!strncmp(node->name, "qcom,dp_display", 15)) {
pr_info("[drm-dp] disabled displayport!\n");
continue;
}
#endif
component_match_add(dev, matchptr, compare_of, node);
}
@ -2388,6 +2432,18 @@ static void __exit msm_drm_unregister(void)
module_init(msm_drm_register);
module_exit(msm_drm_unregister);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#if IS_ENABLED(CONFIG_REGULATOR_S2DOS05)
MODULE_SOFTDEP("pre: s2dos05-regulator");
#endif
#if IS_ENABLED(CONFIG_REGULATOR_S2DOS07)
MODULE_SOFTDEP("pre: s2dos07");
#endif
#if IS_ENABLED(CONFIG_REGULATOR_S2MPB03)
MODULE_SOFTDEP("pre: s2mpb03");
#endif
#endif
MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
MODULE_DESCRIPTION("MSM DRM Driver");
MODULE_LICENSE("GPL");

View File

@ -20,6 +20,10 @@
#ifndef __MSM_DRV_H__
#define __MSM_DRV_H__
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) && IS_ENABLED(CONFIG_UML)
#include "samsung/kunit_test/ss_kunit_test_garbage_macro.h"
#endif
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
@ -257,6 +261,10 @@ enum msm_mdp_conn_property {
CONNECTOR_PROP_WB_ROT_TYPE,
CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK,
CONNECTOR_PROP_BPP_MODE,
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
/* SAMSUNG_FINGERPRINT */
CONNECTOR_PROP_FINGERPRINT_MASK,
#endif
/* total # of properties */
CONNECTOR_PROP_COUNT
@ -849,6 +857,9 @@ struct msm_display_wd_jitter_config {
*/
struct msm_mode_info {
uint32_t frame_rate;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
uint32_t frame_rate_org;
#endif
uint32_t vtotal;
uint32_t prefill_lines;
uint32_t jitter_numer;
@ -1373,6 +1384,10 @@ int msm_framebuffer_get_cache_hint(struct drm_framebuffer *fb,
struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
void msm_fbdev_free(struct drm_device *dev);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int __msm_drm_notifier_call_chain(unsigned long event, void *data);
#endif
struct hdmi;
#if IS_ENABLED(CONFIG_DRM_MSM_HDMI)
int msm_hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev,

View File

@ -32,6 +32,10 @@
#include "msm_mmu.h"
#include "sde_dbg.h"
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
#include "ss_dsi_panel_common.h"
#endif
struct msm_smmu_client {
struct device *dev;
const char *compat;
@ -255,6 +259,9 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
unsigned long attrs = 0x0;
int ret;
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
int retry_cnt;
#endif
if (!sgt || !client) {
DRM_ERROR("sg table is invalid\n");
@ -268,6 +275,23 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
if (!(flags & MSM_BO_EXTBUF)) {
ret = dma_map_sg_attrs(client->dev, sgt->sgl, sgt->nents, dir,
attrs);
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
if (!in_interrupt()) {
if (!ret) {
for (retry_cnt = 0; retry_cnt < 62 ; retry_cnt++) {
/* To wait free page by memory reclaim*/
usleep_range(16000, 16000);
pr_err("dma map sg failed : retry (%d)\n", retry_cnt);
ret = dma_map_sg_attrs(client->dev, sgt->sgl, sgt->nents, dir,
attrs);
if (!ret)
break;
}
}
}
#endif
if (!ret) {
DRM_ERROR("dma map sg failed\n");
return -ENOMEM;
@ -282,6 +306,11 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
dir, attrs, client->secure, flags);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) && IS_ENABLED(CONFIG_SEC_DEBUG)
if (sec_debug_is_enabled() && sgt && sgt->sgl)
ss_smmu_debug_map(SMMU_RT_DISPLAY_DEBUG, sgt);
#endif
return 0;
}
@ -305,6 +334,11 @@ static void msm_smmu_unmap_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
dir, client->secure, flags);
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG) && IS_ENABLED(CONFIG_SEC_DEBUG)
if (sec_debug_is_enabled() && sgt && sgt->sgl)
ss_smmu_debug_unmap(SMMU_RT_DISPLAY_DEBUG, sgt);
#endif
if (!(flags & MSM_BO_EXTBUF))
dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, dir);
}
@ -465,6 +499,11 @@ static int msm_smmu_fault_handler(struct iommu_domain *domain,
DRM_ERROR("trigger dump, iova=0x%08lx, flags=0x%x\n", iova, flags);
DRM_ERROR("SMMU device:%s", client->dev ? client->dev->kobj.name : "");
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
//ss_smmu_debug_log();
SDE_DBG_DUMP(SDE_DBG_BUILT_IN_ALL, "panic"); // case 03250922
#endif
/*
* return -ENOSYS to allow smmu driver to dump out useful
* debug info.
@ -528,6 +567,9 @@ static int msm_smmu_probe(struct platform_device *pdev)
return -EINVAL;
}
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
pr_err("msm_smmu_probe ++ \n");
#endif
DRM_INFO("probing device %s\n", match->compatible);
client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
@ -566,6 +608,10 @@ static int msm_smmu_probe(struct platform_device *pdev)
if (ret)
pr_err("component add failed\n");
#if IS_ENABLED(CONFIG_DISPLAY_SAMSUNG)
pr_err("msm_smmu_probe -- \n");
#endif
return ret;
}

View File

@ -0,0 +1,8 @@
XXD := /usr/bin/xxd
SED := /bin/sed
#Translate .dat file to .h to cover the case which can not use request_firmware(Recovery Mode)
CLEAR_TMP := $(shell rm -f E3_S6E3HAE_AMB681AZ01_PDF_DATA)
COPY_TO_HERE := $(shell cp -vf $(DISPLAY_BLD_DIR)/msm/samsung/panel_data_file/E3_S6E3HAE_AMB681AZ01.dat E3_S6E3HAE_AMB681AZ01_PDF_DATA)
DATA_TO_HEX := $(shell $(XXD) -i E3_S6E3HAE_AMB681AZ01_PDF_DATA > $(DISPLAY_BLD_DIR)/msm/samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01_PDF.h)
ADD_NULL_CHR := $(shell $(SED) -i -e 's/\([0-9a-f]\)$$/\0, 0x00/' $(DISPLAY_BLD_DIR)/msm/samsung/E3_S6E3HAE_AMB681AZ01/E3_S6E3HAE_AMB681AZ01_PDF.h)

View File

@ -0,0 +1 @@
export CONFIG_PANEL_E3_S6E3HAE_AMB681AZ01_WQHD=y

View File

@ -0,0 +1 @@
#define CONFIG_PANEL_E3_S6E3HAE_AMB681AZ01_WQHD 1

View File

@ -0,0 +1,33 @@
/*
* =================================================================
*
*
* Description: samsung display panel file
* Company: Samsung Electronics
*
* ================================================================
*/
/*
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2023, Samsung Electronics. 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.
*
*
*/
#ifndef E3_S6E3HAE_AMB681AZ01_HW_PARAM_H
#define E3_S6E3HAE_AMB681AZ01_HW_PARAM_H
static int GLUT_OFFSET_night_dim_V_revA[GAMMA_OFFSET_SIZE * GLUT_SIZE] = { };
static int GLUT_OFFSET_48_96HS_V_revA[GAMMA_OFFSET_SIZE * GLUT_SIZE] = { };
#endif

View File

@ -0,0 +1,8 @@
XXD := /usr/bin/xxd
SED := /bin/sed
#Translate .dat file to .h to cover the case which can not use request_firmware(Recovery Mode)
CLEAR_TMP := $(shell rm -f E3_S6E3HAF_AMB679FN01_PDF_DATA)
COPY_TO_HERE := $(shell cp -vf $(DISPLAY_BLD_DIR)/msm/samsung/panel_data_file/E3_S6E3HAF_AMB679FN01.dat E3_S6E3HAF_AMB679FN01_PDF_DATA)
DATA_TO_HEX := $(shell $(XXD) -i E3_S6E3HAF_AMB679FN01_PDF_DATA > $(DISPLAY_BLD_DIR)/msm/samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01_PDF.h)
ADD_NULL_CHR := $(shell $(SED) -i -e 's/\([0-9a-f]\)$$/\0, 0x00/' $(DISPLAY_BLD_DIR)/msm/samsung/E3_S6E3HAF_AMB679FN01/E3_S6E3HAF_AMB679FN01_PDF.h)

View File

@ -0,0 +1 @@
export CONFIG_PANEL_E3_S6E3HAF_AMB679FN01_WQHD=y

View File

@ -0,0 +1 @@
#define CONFIG_PANEL_E3_S6E3HAF_AMB679FN01_WQHD 1

View File

@ -0,0 +1,576 @@
/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* DDI ABC operation
* Author: Samsung Display Driver Team <cj1225.jang@samsung.com>
*
* 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 "ss_dsi_panel_common.h"
#include "ss_dsi_mafpc.h"
static int ss_mafpc_make_img_mass_cmds(struct samsung_display_driver_data *vdd,
char *data, u32 data_size, struct ss_cmd_desc *ss_cmd)
{
int ret = 0;
SDE_ATRACE_BEGIN("mafpc_mass_cmd_generation");
mutex_lock(&vdd->mafpc.vdd_mafpc_lock);
ret = ss_convert_img_to_mass_cmds(vdd, data, data_size, ss_cmd);
mutex_unlock(&vdd->mafpc.vdd_mafpc_lock);
SDE_ATRACE_END("mafpc_mass_cmd_generation");
LCD_INFO(vdd, "tx_len: %d\n", ss_cmd->tx_len);
return ret;
}
#define WAIT_FRAME (1)
static int ss_mafpc_img_write(struct samsung_display_driver_data *vdd, bool is_instant)
{
if (!vdd->mafpc.is_support) {
LCD_ERR(vdd, "mafpc is not supported..(%d) \n", vdd->mafpc.is_support);
return -EACCES;
}
LCD_INFO(vdd, "++(%d)\n", is_instant);
mutex_lock(&vdd->self_disp.vdd_self_display_ioctl_lock);
mutex_lock(&vdd->mafpc.vdd_mafpc_lock);
atomic_inc(&vdd->block_commit_cnt);
ss_wait_for_kickoff_done(vdd);
ss_send_cmd(vdd, TX_MAFPC_SETTING);
ss_release_commit(vdd);
mutex_unlock(&vdd->mafpc.vdd_mafpc_lock);
mutex_unlock(&vdd->self_disp.vdd_self_display_ioctl_lock);
LCD_INFO(vdd, "--(%d)\n", is_instant);
return 0;
}
static int ss_mafpc_enable(struct samsung_display_driver_data *vdd, int enable)
{
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->mafpc.is_support) {
LCD_ERR(vdd, "mafpc is not supported..(%d) \n", vdd->mafpc.is_support);
return -EACCES;
}
mutex_lock(&vdd->mafpc.vdd_mafpc_lock);
if (enable) {
ss_send_cmd(vdd, TX_MAFPC_ON);
/* To update mAFPC brightness scale factor */
ss_brightness_dcs(vdd, USE_CURRENT_BL_LEVEL, BACKLIGHT_NORMAL);
} else
ss_send_cmd(vdd, TX_MAFPC_OFF);
mutex_unlock(&vdd->mafpc.vdd_mafpc_lock);
LCD_INFO(vdd, "%s\n", enable ? "Enable" : "Disable");
return 0;
}
static int ss_mafpc_crc_check(struct samsung_display_driver_data *vdd)
{
int rx_len;
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->mafpc.is_support) {
LCD_ERR(vdd, "mafpc is not supported..(%d) \n", vdd->mafpc.is_support);
return -ENODEV;
}
if (!vdd->mafpc.crc_size) {
LCD_ERR(vdd, "mAFPC crc size is zero..\n\n");
return -EINVAL;
}
LCD_INFO(vdd, "++\n");
mutex_lock(&vdd->mafpc.vdd_mafpc_crc_check_lock);
/* prevent sw reset to trigger esd recovery */
LCD_INFO(vdd, "disable esd interrupt\n");
if (vdd->esd_recovery.esd_irq_enable)
vdd->esd_recovery.esd_irq_enable(false, true, (void *)vdd, ESD_MASK_MAFPC_CRC);
ss_block_commit(vdd);
rx_len = ss_send_cmd_get_rx(vdd, TX_MAFPC_CRC_CHECK, vdd->mafpc.crc_read_data);
ss_release_commit(vdd);
if (rx_len <= 0) {
LCD_ERR(vdd, "fail to read ddi id(%d)\n", rx_len);
return -EINVAL;
}
if (rx_len != vdd->mafpc.crc_size) {
LCD_ERR(vdd, "rx_len(%d) != crc_size(%d)\n", rx_len, vdd->mafpc.crc_size);
return -EINVAL;
}
if (memcmp(vdd->mafpc.crc_read_data, vdd->mafpc.crc_pass_data, vdd->mafpc.crc_size)) {
LCD_ERR(vdd, "mAFPC CRC check fail !!\n");
ret = -EFAULT;
}
/* enable esd interrupt */
LCD_INFO(vdd, "enable esd interrupt\n");
if (vdd->esd_recovery.esd_irq_enable)
vdd->esd_recovery.esd_irq_enable(true, true, (void *)vdd, ESD_MASK_MAFPC_CRC);
mutex_unlock(&vdd->mafpc.vdd_mafpc_crc_check_lock);
LCD_INFO(vdd, "-- \n");
return ret;
}
static int ss_mafpc_debug(struct samsung_display_driver_data *vdd)
{
return 0;
}
static int update_mafpc_scale(struct samsung_display_driver_data *vdd,
char *val, struct ss_cmd_desc *cmd)
{
int idx;
int i = -1, j;
int ret;
cmd->skip_by_cond = false;
if (!vdd->mafpc.en) {
LCD_DEBUG(vdd, "mAFPC is not enabled\n");
ret = -EPERM;
goto err_skip;
}
if (!vdd->mafpc.is_br_table_updated) {
LCD_ERR(vdd, "Brightness Table for mAFPC is not updated yet\n");
ret = -EPERM;
goto err_skip;
}
while (!cmd->pos_0xXX[++i] && i < cmd->tx_len);
if (i + vdd->mafpc_scale_table.col_size > cmd->tx_len) {
LCD_ERR(vdd, "fail to find proper 0xXX position(%d, %d)\n",
i, cmd->tx_len);
ret = -EINVAL;
goto err_skip;
}
idx = vdd->mafpc.scale_idx;
if (idx < 0 || idx >= vdd->mafpc_scale_table.row_size) {
LCD_ERR(vdd, "Invalid index for mAFPC scale table: %d/%d\n",
idx, vdd->mafpc_scale_table.row_size);
ret = -EINVAL;
goto err_skip;
}
for (j = 0; j < vdd->mafpc_scale_table.col_size; j++)
cmd->txbuf[i + j] = vdd->mafpc_scale_table.cmds[idx][j];
LCD_DEBUG(vdd, "idx [%d] : %x %x %x\n", idx,
cmd->txbuf[i], cmd->txbuf[i + 1], cmd->txbuf[i + 2]);
return 0;
err_skip:
cmd->skip_by_cond = true;
return ret;
}
static int update_abc_data(struct samsung_display_driver_data *vdd,
char *val, struct ss_cmd_desc *cmd)
{
struct cmd_ref_state *state = &vdd->cmd_ref_state;
bool is_factory_mode = state->is_factory_mode;
struct ss_cmd_desc *src_ss_cmd;
struct dsi_cmd_desc *tmp_qc_cmd;
if (is_factory_mode)
src_ss_cmd = &vdd->mafpc_crc_img_cmd;
else
src_ss_cmd = &vdd->mafpc_img_cmd;
/* update target ss cmd except qc_cmd pointer */
tmp_qc_cmd = cmd->qc_cmd;
cmd = src_ss_cmd;
cmd->qc_cmd = tmp_qc_cmd;
/* update target qc cmd except ss_cmd pointer */
cmd->qc_cmd = src_ss_cmd->qc_cmd;
cmd->qc_cmd->ss_cmd = cmd;
/* data pass of ss_bridge_qc_cmd_update */
if (is_factory_mode) {
cmd->qc_cmd->msg.tx_buf = vdd->mafpc_crc_img_cmd.txbuf;
cmd->qc_cmd->msg.tx_len = vdd->mafpc_crc_img_cmd.tx_len;
} else {
cmd->qc_cmd->msg.tx_buf = vdd->mafpc_img_cmd.txbuf;
cmd->qc_cmd->msg.tx_len = vdd->mafpc_img_cmd.tx_len;
}
LCD_INFO(vdd, "cmd->tx_len:%d, tx_buf:[0x%x, 0x%x, 0x%x..\n",
cmd->qc_cmd->msg.tx_len, vdd->mafpc_img_cmd.txbuf[0],
vdd->mafpc_img_cmd.txbuf[1], vdd->mafpc_img_cmd.txbuf[2]);
return 0;
}
static int update_abc_ctrl_data(struct samsung_display_driver_data *vdd,
char *val, struct ss_cmd_desc *cmd)
{
u32 ctrl_cmd_size = vdd->mafpc.enable_cmd_size;
char *ctrl_cmd_buf = vdd->mafpc.enable_cmd_buf;
int i = -1, j, pos = 0;
char show_buf[200] = {0, };
if (!ctrl_cmd_buf) {
LCD_ERR(vdd, "ctrl_cmd_buf is NULL\n");
goto err_skip;
}
while (!cmd->pos_0xXX[++i] && i < cmd->tx_len);
if (i + 1 >= cmd->tx_len) {
LCD_ERR(vdd, "fail to find proper 0xXX position\n");
goto err_skip;
}
memcpy(&cmd->txbuf[i], ctrl_cmd_buf, ctrl_cmd_size);
for (j = 0; j < ctrl_cmd_size; j++)
pos += scnprintf(show_buf + pos, sizeof(show_buf) - pos, "%02x ", ctrl_cmd_buf[j]);
LCD_INFO(vdd, "update ABC enable cmd [%d] = %s\n", ctrl_cmd_size, show_buf);
return 0;
err_skip:
cmd->skip_by_cond = true;
return -EINVAL;
}
/*
* ss_mafpc_ioctl() : get ioctl from mdnie framework.
*/
static long ss_mafpc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if ((_IOC_TYPE(cmd) != MAFPC_IOCTL_MAGIC) ||
(_IOC_NR(cmd) >= IOCTL_MAFPC_MAX)) {
LCD_ERR(vdd, "TYPE(%u) NR(%u) is wrong..\n",
_IOC_TYPE(cmd), _IOC_NR(cmd));
return -EINVAL;
}
LCD_INFO(vdd, "cmd = %s\n", cmd == IOCTL_MAFPC_ON ? "IOCTL_MAFPC_ON" :
cmd == IOCTL_MAFPC_OFF ? "IOCTL_MAFPC_OFF" :
cmd == IOCTL_MAFPC_ON_INSTANT ? "IOCTL_MAFPC_ON_INSTANT" :
cmd == IOCTL_MAFPC_OFF_INSTANT ? "IOCTL_MAFPC_OFF_INSTANT" : "IOCTL_ERR");
switch (cmd) {
case IOCTL_MAFPC_ON:
vdd->mafpc.en = true;
break;
case IOCTL_MAFPC_ON_INSTANT:
vdd->mafpc.en = true;
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_INFO(vdd, "Panel is not ready(%d), will apply next display on\n",
vdd->panel_state);
return -ENODEV;
}
ss_mafpc_img_write(vdd, true);
ss_mafpc_enable(vdd, true);
break;
case IOCTL_MAFPC_OFF:
vdd->mafpc.en = false;
break;
case IOCTL_MAFPC_OFF_INSTANT:
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_ERR(vdd, "Panel is not ready. Panel State(%d)\n", vdd->panel_state);
return -ENODEV;
}
vdd->mafpc.en = false;
ss_mafpc_enable(vdd, false);
break;
default:
LCD_ERR(vdd, "invalid cmd : %u \n", cmd);
break;
}
return ret;
}
/*
* ss_mafpc_write_from_user() : get mfapc image data from mdnie framework.
* prepare for dsi_panel_cmds.
*/
static ssize_t ss_mafpc_write_from_user(struct file *file, const char __user *user_buf,
size_t total_count, loff_t *ppos)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int i, j, k = 0;
int ret = 0;
u32 enable_cmd_size = vdd->mafpc.enable_cmd_size;
char *enable_cmd_buf = vdd->mafpc.enable_cmd_buf;
u32 img_size = vdd->mafpc.img_size;
char *img_buf = vdd->mafpc.img_buf;
u32 br_table_size = vdd->mafpc.scale_table_size;
char *scale_tbl_buf = vdd->mafpc.scale_table_buf;
u32 hdr_size = vdd->mafpc.header_cmd_size;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "no vdd");
return -ENODEV;
}
if (unlikely(!enable_cmd_buf)) {
LCD_ERR(vdd, "No mafpc Enable cmd Buffer\n");
return -ENODEV;
}
if (unlikely(!img_buf)) {
LCD_ERR(vdd, "No mafpc Image Buffer\n");
return -ENODEV;
}
if (unlikely(!scale_tbl_buf)) {
LCD_ERR(vdd, "No mafpc scale tbl Buffer\n");
return -ENODEV;
}
if (unlikely(!user_buf)) {
LCD_ERR(vdd, "invalid user buffer\n");
return -EINVAL;
}
if (total_count != (enable_cmd_size + hdr_size + img_size + br_table_size)) {
LCD_ERR(vdd, "Invalid size %zu, should be %u\n",
total_count, (enable_cmd_size + hdr_size + img_size + br_table_size));
return -EINVAL;
}
LCD_INFO(vdd, "Total_Count(%zu), hdr_size (%u) cmd_size(%u), img_size(%u), br_table_size(%u)\n",
total_count, hdr_size, enable_cmd_size, img_size, br_table_size);
/*
* Get 67bytes Enable Command to match with mafpc image data
(1Byte(Header) + 66Byte(Control Data))
*/
ret = copy_from_user(enable_cmd_buf, user_buf + hdr_size, enable_cmd_size);
if (unlikely(ret < 0)) {
LCD_ERR(vdd, "failed to copy_from_user (Enable Command)\n");
return -EINVAL;
}
/* Get ABC Compensation Image Data from user space (mDNIE Service) */
ret = copy_from_user(img_buf, user_buf + enable_cmd_size + hdr_size, img_size);
if (unlikely(ret < 0)) {
LCD_ERR(vdd, "failed to copy_from_user (Image Data)\n");
return -EINVAL;
}
/* Get 225(75 x 3)Bytes for brightness scale cmd table from user space (mDNIE Service) */
ret = copy_from_user(scale_tbl_buf, user_buf + enable_cmd_size + hdr_size + img_size, br_table_size);
if (unlikely(ret < 0)) {
LCD_ERR(vdd, "failed to copy_from_user (Brightness Scale Table)\n");
return -EINVAL;
}
for (i = 0; i < vdd->mafpc_scale_table.row_size; i++) {
for (j = 0; j < vdd->mafpc_scale_table.col_size; j++)
vdd->mafpc_scale_table.cmds[i][j] = scale_tbl_buf[k++];
LCD_INFO(vdd, "[%02d] %X %X %X\n", i,
vdd->mafpc_scale_table.cmds[i][0],
vdd->mafpc_scale_table.cmds[i][1],
vdd->mafpc_scale_table.cmds[i][2]);
}
vdd->mafpc.is_br_table_updated = true;
ss_mafpc_make_img_mass_cmds(vdd, vdd->mafpc.img_buf, vdd->mafpc.img_size, &vdd->mafpc_img_cmd);
return total_count;
}
static int ss_mafpc_open(struct inode *inode, struct file *file)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
vdd->mafpc.file_open = true;
LCD_DEBUG(vdd, "[OPEN]\n");
return 0;
}
static int ss_mafpc_release(struct inode *inode, struct file *file)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
vdd->mafpc.file_open = false;
LCD_DEBUG(vdd, "[RELEASE]\n");
return 0;
}
static const struct file_operations mafpc_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ss_mafpc_ioctl,
.open = ss_mafpc_open,
.release = ss_mafpc_release,
.write = ss_mafpc_write_from_user,
};
#define DEV_NAME_SIZE 24
int ss_mafpc_init(struct samsung_display_driver_data *vdd)
{
int ret = 0;
static char devname[DEV_NAME_SIZE] = {'\0', };
struct dsi_panel_cmd_set *pcmds;
struct dsi_panel *panel = NULL;
struct mipi_dsi_host *host = NULL;
struct dsi_display *display = NULL;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->mafpc.is_support) {
LCD_ERR(vdd, "mAFPC is not supported\n");
return -EINVAL;
}
panel = (struct dsi_panel *)vdd->msm_private;
host = panel->mipi_device.host;
display = container_of(host, struct dsi_display, host);
mutex_init(&vdd->mafpc.vdd_mafpc_lock);
mutex_init(&vdd->mafpc.vdd_mafpc_crc_check_lock);
if (vdd->ndx == PRIMARY_DISPLAY_NDX)
snprintf(devname, DEV_NAME_SIZE, "mafpc");
else
snprintf(devname, DEV_NAME_SIZE, "mafpc%d", vdd->ndx);
vdd->mafpc.dev.minor = MISC_DYNAMIC_MINOR;
vdd->mafpc.dev.name = devname;
vdd->mafpc.dev.fops = &mafpc_fops;
vdd->mafpc.dev.parent = &display->pdev->dev;;
vdd->mafpc.enable = ss_mafpc_enable;
vdd->mafpc.crc_check = ss_mafpc_crc_check;
vdd->mafpc.make_img_mass_cmds = ss_mafpc_make_img_mass_cmds;
vdd->mafpc.img_write = ss_mafpc_img_write;
vdd->mafpc.debug = ss_mafpc_debug;
/* Alloc the memory for ABC scale table */
vdd->mafpc.scale_table_size = vdd->mafpc_scale_table.row_size * vdd->mafpc_scale_table.col_size;
if (vdd->mafpc.scale_table_size) {
vdd->mafpc.scale_table_buf = kzalloc(vdd->mafpc.scale_table_size, GFP_KERNEL);
if (IS_ERR_OR_NULL(vdd->mafpc.scale_table_buf))
LCD_ERR(vdd, "fail to allocate scale_table_buf(%d)\n", vdd->mafpc.scale_table_size);
}
LCD_INFO(vdd, "scale_table_size [%d] = (%d) x (%d)\n", vdd->mafpc.scale_table_size,
vdd->mafpc_scale_table.row_size, vdd->mafpc_scale_table.col_size);
/* Alloc the memory for ABC enable cmd */
if (vdd->mafpc.enable_cmd_size) {
vdd->mafpc.enable_cmd_buf = kzalloc(vdd->mafpc.enable_cmd_size, GFP_KERNEL);
if (IS_ERR_OR_NULL(vdd->mafpc.enable_cmd_buf)) {
LCD_ERR(vdd, "Failed to alloc mafpc enable cmd buffer\n");
} else {
/* initialize mafpc enable cmd to default control data */
pcmds = ss_get_cmds(vdd, TX_MAFPC_CTRL_DATA);
if (pcmds->count <= 0) {
LCD_ERR(vdd, "no mafpc_default_enable_cmd cmds\n");
} else {
memcpy(vdd->mafpc.enable_cmd_buf, &pcmds->cmds[0].ss_txbuf[1],
vdd->mafpc.enable_cmd_size);
LCD_INFO(vdd, "initialize mafpc enable cmd\n");
}
}
}
LCD_INFO(vdd, "enable_cmd_size [%d]\n", vdd->mafpc.enable_cmd_size);
ret = ss_wrapper_misc_register(vdd, &vdd->mafpc.dev);
if (ret) {
LCD_ERR(vdd, "failed to register driver : %d\n", ret);
vdd->mafpc.is_support = false;
return -ENODEV;
}
register_op_sym_cb(vdd, "MAFPC", update_mafpc_scale, true);
register_op_sym_cb(vdd, "ABC_DATA", update_abc_data, true);
register_op_sym_cb(vdd, "UPDATE_ABC_CTRL_DATA", update_abc_ctrl_data, true);
LCD_INFO(vdd, "Success to register mafpc device..(%d)\n", ret);
return ret;
}
MODULE_DESCRIPTION("mAFPC driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* DDI ABC operation
* Author: Samsung Display Driver Team <cj1225.jang@samsung.com>
*
* 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.
*/
#ifndef __SS_DSI_MAFPC_H__
#define __SS_DSI_MAFPC_H__
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/file.h>
/* Align & Paylod Size for Embedded mode Transfer */
#define MAFPC_CMD_ALIGN 16
#define MAFPC_MAX_PAYLOAD_SIZE 200
/* Align & Paylod Size for Non-Embedded mode(Mass) Command Transfer */
#define MAFPC_MASS_CMD_ALIGN 256
#define MAFPC_MAX_PAYLOAD_SIZE_MASS 0xFFFF0 /* 983,024 */
/*
* ioctl
*/
#define MAFPC_IOCTL_MAGIC 'M'
#define IOCTL_MAFPC_ON _IOW(MAFPC_IOCTL_MAGIC, 1, int)
#define IOCTL_MAFPC_ON_INSTANT _IOW(MAFPC_IOCTL_MAGIC, 2, int)
#define IOCTL_MAFPC_OFF _IOW(MAFPC_IOCTL_MAGIC, 3, int)
#define IOCTL_MAFPC_OFF_INSTANT _IOW(MAFPC_IOCTL_MAGIC, 4, int)
#define IOCTL_MAFPC_MAX 10
struct ss_cmd_desc;
struct MAFPC {
struct miscdevice dev;
int is_support;
int factory_support;
int file_open;
int need_to_write;
int is_br_table_updated;
int force_delay;
int en;
char *enable_cmd_buf;
u32 enable_cmd_size;
u32 header_cmd_size;
char *img_buf;
u32 img_size;
char *scale_table_buf;
u32 scale_table_size;
int scale_idx;
u32 wpos;
u64 wsize;
int img_checksum;
u32 img_checksum_cal;
struct mutex vdd_mafpc_lock;
struct mutex vdd_mafpc_crc_check_lock;
char *crc_img_buf;
u32 crc_img_size;
u8 crc_pass_data[2]; /*implemented in dtsi */
u8 crc_read_data[2];
int crc_size;
/* mafpc Function */
int (*init)(struct samsung_display_driver_data *vdd);
int (*data_init)(struct samsung_display_driver_data *vdd);
int (*make_img_cmds)(struct samsung_display_driver_data *vdd, char *data, u32 data_size, int cmd_type);
int (*make_img_mass_cmds)(struct samsung_display_driver_data *vdd, char *data, u32 data_size, struct ss_cmd_desc *ss_cmd);
int (*img_write)(struct samsung_display_driver_data *vdd, bool is_instant);
int (*enable)(struct samsung_display_driver_data *vdd, int enable);
int (*crc_check)(struct samsung_display_driver_data *vdd);
int (*debug)(struct samsung_display_driver_data *vdd);
};
int ss_mafpc_init(struct samsung_display_driver_data *vdd);
/* 2551 (Normal: 0~2550) + 1 (HBM) */
#define MAX_MAFPC_BL_SCALE 2552
static int brightness_scale_idx[MAX_MAFPC_BL_SCALE] = {
[0 ... 9] = 0,
[10 ... 19] = 1,
[20 ... 30] = 2,
[31 ... 40] = 3,
[41 ... 50] = 4,
[51 ... 59] = 5,
[60 ... 68] = 6,
[69 ... 79] = 7,
[80 ... 90] = 8,
[91 ... 100] = 9,
[101 ... 120] = 10,
[121 ... 129] = 11,
[130 ... 139] = 12,
[140 ... 158] = 13,
[159 ... 178] = 14,
[179 ... 188] = 15,
[189 ... 200] = 16,
[201 ... 210] = 17,
[211 ... 229] = 18,
[230 ... 249] = 19,
[250 ... 258] = 20,
[259 ... 269] = 21,
[270 ... 299] = 22,
[300 ... 319] = 23,
[320 ... 339] = 24,
[340 ... 359] = 25,
[360 ... 379] = 26,
[380 ... 409] = 27,
[410 ... 439] = 28,
[440 ... 459] = 29,
[460 ... 499] = 30,
[500 ... 530] = 31,
[531 ... 570] = 32,
[571 ... 599] = 33,
[600 ... 639] = 34,
[640 ... 678] = 35,
[679 ... 729] = 36,
[730 ... 769] = 37,
[770 ... 829] = 38,
[830 ... 880] = 39,
[881 ... 930] = 40,
[931 ... 999] = 41,
[1000 ... 1059] = 42,
[1060 ... 1129] = 43,
[1130 ... 1199] = 44,
[1200 ... 1280] = 45,
[1281 ... 1339] = 46,
[1340 ... 1409] = 47,
[1410 ... 1479] = 48,
[1480 ... 1559] = 49,
[1560 ... 1599] = 50,
[1600 ... 1640] = 51,
[1641 ... 1679] = 52,
[1680 ... 1729] = 53,
[1730 ... 1769] = 54,
[1770 ... 1819] = 55,
[1820 ... 1919] = 56,
[1920 ... 2000] = 57,
[2001 ... 2099] = 58,
[2100 ... 2179] = 59,
[2180 ... 2189] = 60,
[2190 ... 2209] = 61,
[2210 ... 2229] = 62,
[2230 ... 2249] = 63,
[2250 ... 2269] = 64,
[2270 ... 2289] = 65,
[2290 ... 2309] = 66,
[2310 ... 2329] = 67,
[2330 ... 2350] = 68,
[2351 ... 2400] = 69,
[2401 ... 2449] = 70,
[2450 ... 2499] = 71,
[2500 ... 2549] = 72,
[2550] = 73,
[2551] = 74, /* for HBM */
};
#endif /* __SELF_DISPLAY_H__ */

View File

@ -0,0 +1,35 @@
/*
* =================================================================
*
*
* Description: samsung display panel file
*
* Author: jb09.kim
* Company: Samsung Electronics
*
* ================================================================
*/
/*
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2012, Samsung Electronics. 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 "ss_dsi_panel_PBA_BOOTING_fhd.h"
void PBA_BOOTING_FHD_init(struct samsung_display_driver_data *vdd)
{
LCD_INFO(vdd, "%s\n", ss_get_panel_name(vdd));
vdd->mdnie.support_mdnie = false;
vdd->dtsi_data.tft_common_support = true;
}

View File

@ -0,0 +1,32 @@
/*
* =================================================================
*
*
* Description: samsung display panel file
*
* Author: jb09.kim
* Company: Samsung Electronics
*
* ================================================================
*/
/*
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2012, Samsung Electronics. 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.
*
*/
#ifndef SAMSUNG_DSI_PANEL_EA8061V_AMS391DT01_H
#define SAMSUNG_DSI_PANEL_EA8061V_AMS391DT01_H
#include "ss_dsi_panel_common.h"
#endif

View File

@ -0,0 +1,619 @@
/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* DDI operation : self clock, self mask, self icon.. etc.
* Author: QC LCD driver <cj1225.jang@samsung.com>
*
* 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 "ss_dsi_panel_common.h"
#include "self_display.h"
int make_mass_self_display_img_cmds(struct samsung_display_driver_data *vdd,
enum self_display_data_flag op, struct ss_cmd_desc *ss_cmd)
{
char *data = vdd->self_disp.operation[op].img_buf;
u32 data_size = vdd->self_disp.operation[op].img_size;
int ret = 0;
u32 check_sum_0 = 0;
u32 check_sum_1 = 0;
u32 check_sum_2 = 0;
u32 check_sum_3 = 0;
int i;
if (!data) {
LCD_ERR(vdd, "data is null..\n");
return -EINVAL;
}
if (!data_size) {
LCD_ERR(vdd, "data size is zero..\n");
return -EINVAL;
}
SDE_ATRACE_BEGIN("mass_cmd_generation");
ret = ss_convert_img_to_mass_cmds(vdd, data, data_size, ss_cmd);
/* Image Check Sum Calculation */
for (i = 0; i < data_size; i += 4)
check_sum_0 += data[i];
for (i = 1; i < data_size; i += 4)
check_sum_1 += data[i];
for (i = 2; i < data_size; i += 4)
check_sum_2 += data[i];
for (i = 3; i < data_size; i += 4)
check_sum_3 += data[i];
vdd->self_disp.operation[op].img_checksum_cal = (check_sum_3 & 0xFF);
vdd->self_disp.operation[op].img_checksum_cal |= ((check_sum_2 & 0xFF) << 8);
vdd->self_disp.operation[op].img_checksum_cal |= ((check_sum_1 & 0xFF) << 16);
vdd->self_disp.operation[op].img_checksum_cal |= ((check_sum_0 & 0xFF) << 24);
SDE_ATRACE_END("mass_cmd_generation");
LCD_INFO(vdd, "[checksum] op=%d, checksum: 0x%X, 0x%X, 0x%X, 0x%X\n",
op, check_sum_0, check_sum_1, check_sum_2, check_sum_3);
return ret;
}
/* TODO: set last_cmd for cmd[cur-1] and cmd[cur+1] */
static int update_self_mask_image(struct samsung_display_driver_data *vdd,
char *val, struct ss_cmd_desc *cmd)
{
struct ss_cmd_desc *src_ss_cmd;
struct dsi_cmd_desc *tmp_qc_cmd;
int ret;
cmd->skip_by_cond = false;
if (!vdd->self_disp.is_support) {
LCD_INFO(vdd, "self_disp is not enabled\n");
ret = -EPERM;
goto err_skip;
}
src_ss_cmd = &vdd->self_disp.self_mask_cmd;
/* update target ss cmd except qc_cmd pointer */
tmp_qc_cmd = cmd->qc_cmd;
cmd = src_ss_cmd;
cmd->qc_cmd = tmp_qc_cmd;
/* update target qc cmd except ss_cmd pointer */
cmd->qc_cmd = src_ss_cmd->qc_cmd;
cmd->qc_cmd->ss_cmd = cmd;
/* data pass of ss_bridge_qc_cmd_update */
cmd->qc_cmd->msg.tx_buf = vdd->self_disp.self_mask_cmd.txbuf;
cmd->qc_cmd->msg.tx_len = vdd->self_disp.self_mask_cmd.tx_len;
LCD_INFO(vdd, "cmd->tx_len:%d, tx_buf:[0x%x, 0x%x..\n",
cmd->qc_cmd->msg.tx_len, vdd->self_disp.self_mask_cmd.txbuf[0],
vdd->self_disp.self_mask_cmd.txbuf[1]);
return 0;
err_skip:
cmd->skip_by_cond = true;
return ret;
}
static int update_self_mask_crc_image(struct samsung_display_driver_data *vdd,
char *val, struct ss_cmd_desc *cmd)
{
struct ss_cmd_desc *src_ss_cmd;
struct dsi_cmd_desc *tmp_qc_cmd;
int ret;
cmd->skip_by_cond = false;
if (!vdd->self_disp.is_support) {
LCD_INFO(vdd, "self_disp is not enabled\n");
ret = -EPERM;
goto err_skip;
}
src_ss_cmd = &vdd->self_disp.self_mask_crc_cmd;
/* update target ss cmd except qc_cmd pointer */
tmp_qc_cmd = cmd->qc_cmd;
cmd = src_ss_cmd;
cmd->qc_cmd = tmp_qc_cmd;
/* update target qc cmd except ss_cmd pointer */
cmd->qc_cmd = src_ss_cmd->qc_cmd;
cmd->qc_cmd->ss_cmd = cmd;
/* data pass of ss_bridge_qc_cmd_update */
cmd->qc_cmd->msg.tx_buf = vdd->self_disp.self_mask_crc_cmd.txbuf;
cmd->qc_cmd->msg.tx_len = vdd->self_disp.self_mask_crc_cmd.tx_len;
LCD_INFO(vdd, "cmd->tx_len:%d, tx_buf:[0x%x, 0x%x..\n",
cmd->qc_cmd->msg.tx_len, vdd->self_disp.self_mask_crc_cmd.txbuf[0],
vdd->self_disp.self_mask_crc_cmd.txbuf[1]);
return 0;
err_skip:
cmd->skip_by_cond = true;
return ret;
}
static int self_display_debug(struct samsung_display_driver_data *vdd)
{
int rx_len;
char buf[4];
rx_len = ss_send_cmd_get_rx(vdd, RX_SELF_DISP_DEBUG, buf);
if (rx_len < 0) {
LCD_ERR(vdd, "invalid rx_len(%d)\n", rx_len);
return false;
}
vdd->self_disp.debug.SM_SUM_O = ((buf[0] & 0xFF) << 24);
vdd->self_disp.debug.SM_SUM_O |= ((buf[1] & 0xFF) << 16);
vdd->self_disp.debug.SM_SUM_O |= ((buf[2] & 0xFF) << 8);
vdd->self_disp.debug.SM_SUM_O |= (buf[3] & 0xFF);
if (vdd->self_disp.operation[FLAG_SELF_MASK].img_checksum != vdd->self_disp.debug.SM_SUM_O) {
LCD_ERR(vdd, "self mask img checksum fail!! %X %X\n",
vdd->self_disp.operation[FLAG_SELF_MASK].img_checksum, vdd->self_disp.debug.SM_SUM_O);
SS_XLOG(vdd->self_disp.debug.SM_SUM_O);
return -1;
}
return 0;
}
static void self_mask_img_write(struct samsung_display_driver_data *vdd)
{
if (!vdd->self_disp.is_support) {
LCD_ERR(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return;
}
LCD_INFO(vdd, "tx self mask\n");
atomic_inc(&vdd->block_commit_cnt);
ss_wait_for_kickoff_done(vdd);
ss_send_cmd(vdd, TX_SELF_MASK_SETTING);
ss_release_commit(vdd);
}
static int self_mask_on(struct samsung_display_driver_data *vdd, int enable)
{
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->self_disp.is_support) {
LCD_ERR(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return -EACCES;
}
LCD_INFO(vdd, "++ (%d)\n", enable);
mutex_lock(&vdd->self_disp.vdd_self_display_lock);
if (enable) {
if (vdd->is_factory_mode && vdd->self_disp.factory_support)
ss_send_cmd(vdd, TX_SELF_MASK_ON_FACTORY);
else
ss_send_cmd(vdd, TX_SELF_MASK_ON);
} else {
ss_send_cmd(vdd, TX_SELF_MASK_OFF);
}
mutex_unlock(&vdd->self_disp.vdd_self_display_lock);
LCD_INFO(vdd, "-- \n");
return ret;
}
static int self_mask_udc_on(struct samsung_display_driver_data *vdd, int enable)
{
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->self_disp.is_support) {
LCD_ERR(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return -EACCES;
}
LCD_INFO(vdd, "++ (%d)\n", enable);
mutex_lock(&vdd->self_disp.vdd_self_display_lock);
if (enable)
ss_send_cmd(vdd, TX_SELF_MASK_UDC_ON);
else
ss_send_cmd(vdd, TX_SELF_MASK_UDC_OFF);
mutex_unlock(&vdd->self_disp.vdd_self_display_lock);
LCD_INFO(vdd, "-- \n");
return ret;
}
#define WAIT_FRAME (2)
static int self_mask_check(struct samsung_display_driver_data *vdd)
{
int rx_len;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->self_disp.is_support) {
LCD_ERR(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return -EPERM;
}
LCD_INFO(vdd, "++ \n");
mutex_lock(&vdd->self_disp.vdd_self_display_lock);
ss_block_commit(vdd);
rx_len = ss_send_cmd_get_rx(vdd, TX_SELF_MASK_CHECK, vdd->self_disp.mask_crc_read_data);
ss_release_commit(vdd);
mutex_unlock(&vdd->self_disp.vdd_self_display_lock);
if (memcmp(vdd->self_disp.mask_crc_read_data,
vdd->self_disp.mask_crc_pass_data,
vdd->self_disp.mask_crc_size)) {
LCD_ERR(vdd, "self mask check fail !!\n");
return -1;
}
LCD_INFO(vdd, "-- \n");
return 0;
}
static int self_partial_hlpm_scan_set(struct samsung_display_driver_data *vdd)
{
u8 *cmd_pload;
struct self_partial_hlpm_scan sphs_info;
struct dsi_panel_cmd_set *pcmds;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
LCD_INFO(vdd, "++\n");
sphs_info = vdd->self_disp.sphs_info;
LCD_INFO(vdd, "Self Partial HLPM hlpm_en(%d) \n", sphs_info.hlpm_en);
pcmds = ss_get_cmds(vdd, TX_SELF_PARTIAL_HLPM_SCAN_SET);
if (SS_IS_CMDS_NULL(pcmds)) {
LCD_ERR(vdd, "No cmds for TX_SELF_PARTIAL_HLPM_SCAN_SET..\n");
return -ENODEV;
}
cmd_pload = pcmds->cmds[1].ss_txbuf;
/* Partial HLPM */
if (sphs_info.hlpm_en)
cmd_pload[1] = 0x03;
else
cmd_pload[1] = 0x00;
ss_send_cmd(vdd, TX_SELF_PARTIAL_HLPM_SCAN_SET);
LCD_INFO(vdd, "--\n");
return 0;
}
static int self_display_aod_enter(struct samsung_display_driver_data *vdd)
{
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->self_disp.is_support) {
LCD_DEBUG(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return -ENODEV;
}
LCD_INFO(vdd, "++\n");
if (!vdd->self_disp.on) {
/* Self display on */
ss_send_cmd(vdd, TX_SELF_DISP_ON);
self_mask_on(vdd, false);
}
vdd->self_disp.on = true;
LCD_INFO(vdd, "--\n");
return ret;
}
static int self_display_aod_exit(struct samsung_display_driver_data *vdd)
{
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->self_disp.is_support) {
LCD_DEBUG(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return -ENODEV;
}
LCD_INFO(vdd, "++\n");
/* self display off */
ss_send_cmd(vdd, TX_SELF_DISP_OFF);
ret = self_display_debug(vdd);
if (!ret)
self_mask_on(vdd, true);
else
LCD_ERR(vdd, "Self Mask CheckSum Error! Skip Self Mask On\n");
LCD_INFO(vdd, "write self_mask_udc %s cmd \n",
vdd->self_disp.udc_mask_enable ? "enable" : "disable");
if (vdd->self_disp.udc_mask_enable)
ss_send_cmd(vdd, TX_SELF_MASK_UDC_ON);
else
ss_send_cmd(vdd, TX_SELF_MASK_UDC_OFF);
if (vdd->self_disp.reset_status)
vdd->self_disp.reset_status(vdd);
LCD_INFO(vdd, "--\n");
return ret;
}
static void self_display_reset_status(struct samsung_display_driver_data *vdd)
{
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return;
}
if (!vdd->self_disp.is_support) {
LCD_DEBUG(vdd, "self display is not supported..(%d) \n",
vdd->self_disp.is_support);
return;
}
vdd->self_disp.sa_info.en = false;
vdd->self_disp.sd_info.en = false;
vdd->self_disp.si_info.en = false;
vdd->self_disp.sg_info.en = false;
vdd->self_disp.time_set = false;
vdd->self_disp.on = false;
return;
}
/*
* self_display_ioctl() : get ioctl from aod framework.
* set self display related registers.
*/
static long self_display_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
void __user *argp = (void __user *)arg;
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_ERR(vdd, "Panel is not ready. Panel State(%d)\n", vdd->panel_state);
return -ENODEV;
}
if (!vdd->self_disp.on) {
LCD_ERR(vdd, "self_display was turned off\n");
return -EPERM;
}
if ((_IOC_TYPE(cmd) != SELF_DISPLAY_IOCTL_MAGIC) ||
(_IOC_NR(cmd) >= IOCTL_SELF_MAX)) {
LCD_ERR(vdd, "TYPE(%u) NR(%u) is wrong..\n",
_IOC_TYPE(cmd), _IOC_NR(cmd));
return -EINVAL;
}
mutex_lock(&vdd->self_disp.vdd_self_display_ioctl_lock);
LCD_INFO(vdd, "cmd = %s\n", cmd == IOCTL_SELF_MOVE_EN ? "IOCTL_SELF_MOVE_EN" :
cmd == IOCTL_SELF_MOVE_OFF ? "IOCTL_SELF_MOVE_OFF" :
cmd == IOCTL_SET_ICON ? "IOCTL_SET_ICON" :
cmd == IOCTL_SET_GRID ? "IOCTL_SET_GRID" :
cmd == IOCTL_SET_ANALOG_CLK ? "IOCTL_SET_ANALOG_CLK" :
cmd == IOCTL_SET_DIGITAL_CLK ? "IOCTL_SET_DIGITAL_CLK" :
cmd == IOCTL_SET_TIME ? "IOCTL_SET_TIME" :
cmd == IOCTL_SET_PARTIAL_HLPM_SCAN ? "IOCTL_PARTIAL_HLPM_SCAN" : "IOCTL_ERR");
switch (cmd) {
case IOCTL_SET_PARTIAL_HLPM_SCAN:
ret = copy_from_user(&vdd->self_disp.sphs_info, argp,
sizeof(vdd->self_disp.sphs_info));
if (ret) {
LCD_ERR(vdd, "fail to copy_from_user.. (%d)\n", ret);
goto error;
}
ret = self_partial_hlpm_scan_set(vdd);
break;
default:
LCD_ERR(vdd, "invalid cmd : %u \n", cmd);
break;
}
error:
mutex_unlock(&vdd->self_disp.vdd_self_display_ioctl_lock);
return ret;
}
/*
* self_display_write() : get image data from aod framework.
* prepare for dsi_panel_cmds.
*/
static ssize_t self_display_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static int self_display_open(struct inode *inode, struct file *file)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
vdd->self_disp.file_open = 1;
LCD_DEBUG(vdd, "[open]\n");
return 0;
}
static int self_display_release(struct inode *inode, struct file *file)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
vdd->self_disp.file_open = 0;
LCD_DEBUG(vdd, "[release]\n");
return 0;
}
static const struct file_operations self_display_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = self_display_ioctl,
.open = self_display_open,
.release = self_display_release,
.write = self_display_write,
};
#define DEV_NAME_SIZE 24
int self_display_init(struct samsung_display_driver_data *vdd)
{
int ret = 0;
static char devname[DEV_NAME_SIZE] = {'\0', };
struct dsi_panel *panel = NULL;
struct mipi_dsi_host *host = NULL;
struct dsi_display *display = NULL;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
if (!vdd->self_disp.is_support) {
LCD_ERR(vdd, "Self Display is not supported\n");
return -EINVAL;
}
panel = (struct dsi_panel *)vdd->msm_private;
host = panel->mipi_device.host;
display = container_of(host, struct dsi_display, host);
mutex_init(&vdd->self_disp.vdd_self_display_lock);
mutex_init(&vdd->self_disp.vdd_self_display_ioctl_lock);
if (vdd->ndx == PRIMARY_DISPLAY_NDX)
snprintf(devname, DEV_NAME_SIZE, "self_display");
else
snprintf(devname, DEV_NAME_SIZE, "self_display%d", vdd->ndx);
vdd->self_disp.dev.minor = MISC_DYNAMIC_MINOR;
vdd->self_disp.dev.name = devname;
vdd->self_disp.dev.fops = &self_display_fops;
vdd->self_disp.dev.parent = &display->pdev->dev;
vdd->self_disp.reset_status = self_display_reset_status;
vdd->self_disp.aod_enter = self_display_aod_enter;
vdd->self_disp.aod_exit = self_display_aod_exit;
vdd->self_disp.self_mask_img_write = self_mask_img_write;
vdd->self_disp.self_mask_on = self_mask_on;
vdd->self_disp.self_mask_udc_on = self_mask_udc_on;
vdd->self_disp.self_mask_check = self_mask_check;
vdd->self_disp.self_partial_hlpm_scan_set = self_partial_hlpm_scan_set;
vdd->self_disp.self_display_debug = self_display_debug;
vdd->self_disp.debug.SM_SUM_O = 0xFF; /* initial value */
ret = ss_wrapper_misc_register(vdd, &vdd->self_disp.dev);
if (ret) {
LCD_ERR(vdd, "failed to register driver : %d\n", ret);
vdd->self_disp.is_support = false;
return -ENODEV;
}
register_op_sym_cb(vdd, "SELF_MASK_IMAGE", update_self_mask_image, true);
register_op_sym_cb(vdd, "SELF_MASK_CRC_IMAGE", update_self_mask_crc_image, true);
LCD_INFO(vdd, "Success to register self_disp device..(%d)\n", ret);
return ret;
}
MODULE_DESCRIPTION("Self Display driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,197 @@
/*
*
* Source file for Self Display Driver
*
* Copyright (c) 2019 Samsung Electronics
*
* 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.
*
*/
/*
* SELF DISLAY interface
*
* Author: Samsung display driver team
* Company: Samsung Electronics
*/
#ifndef _SELF_DISPLAY_H_
#define _SELF_DISPLAY_H_
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/file.h>
/* Align & Paylod Size for Embedded mode Transfer */
#define CMD_ALIGN 16
#define MAX_PAYLOAD_SIZE 200
/* Align & Paylod Size for Non-Embedded mode(Mass) Command Transfer */
#define MASS_CMD_ALIGN 256
#define MAX_PAYLOAD_SIZE_MASS 0xFFFF0 /* 983,024 */
/*
* ioctl
*/
#define SELF_DISPLAY_IOCTL_MAGIC 'S'
#define IOCTL_SELF_MOVE_EN _IOW(SELF_DISPLAY_IOCTL_MAGIC, 1, int)
#define IOCTL_SELF_MOVE_OFF _IOW(SELF_DISPLAY_IOCTL_MAGIC, 2, int)
#define IOCTL_SELF_MOVE_RESET _IOW(SELF_DISPLAY_IOCTL_MAGIC, 3, int)
#define IOCTL_SET_TIME _IOW(SELF_DISPLAY_IOCTL_MAGIC, 11, struct self_time_info)
#define IOCTL_SET_ICON _IOW(SELF_DISPLAY_IOCTL_MAGIC, 21, struct self_icon_info)
#define IOCTL_SET_GRID _IOW(SELF_DISPLAY_IOCTL_MAGIC, 31, struct self_grid_info)
#define IOCTL_SET_ANALOG_CLK _IOW(SELF_DISPLAY_IOCTL_MAGIC, 41, struct self_analog_clk_info)
#define IOCTL_SET_DIGITAL_CLK _IOW(SELF_DISPLAY_IOCTL_MAGIC, 51, struct self_digital_clk_info)
#define IOCTL_SET_PARTIAL_HLPM_SCAN _IOW(SELF_DISPLAY_IOCTL_MAGIC, 61, struct self_partial_hlpm_scan)
#define IOCTL_SELF_MAX 70
#define IMAGE_HEADER_SIZE 2
#define IMAGE_HEADER_SELF_ICON "IC"
#define IMAGE_HEADER_ANALOG_CLK "AC"
#define IMAGE_HEADER_DIGITAL_CLK "DC"
#define ROTATE_0 0
#define ROTATE_90 1
#define ROTATE_180 2
#define ROTATE_270 3
#define INTERVAL_100 0
#define INTERVAL_200 1
#define INTERVAL_500 2
#define INTERVAL_1000 3
#define INTERVAL_DEBUG 999
/*
* data flag
* This flag is used to distinguish what data will be passed and added at first 2byte of data.
* ex ) ioctl write data : flag (2byte) + data (bytes)
*/
enum self_display_data_flag {
FLAG_SELF_MOVE = 1,
FLAG_SELF_MASK = 2,
FLAG_SELF_ICON = 3,
FLAG_SELF_GRID = 4,
FLAG_SELF_ACLK = 5,
FLAG_SELF_DCLK = 6,
FLAG_SELF_VIDEO = 7,
FLAG_SELF_MAFPC = 8,
FLAG_SELF_MASK_CRC = 9,
FLAG_SELF_DISP_MAX,
};
struct self_time_info {
u32 cur_h;
u32 cur_m;
u32 cur_s;
u32 cur_ms;
u32 disp_24h;
u32 interval;
};
struct self_icon_info {
u32 en;
u32 pos_x;
u32 pos_y;
u32 width;
u32 height;
u32 color;
};
struct self_grid_info {
u32 en;
u32 s_pos_x;
u32 s_pos_y;
u32 e_pos_x;
u32 e_pos_y;
};
struct self_analog_clk_info {
u32 en;
u32 pos_x;
u32 pos_y;
u32 rotate;
u32 mem_mask_en;
u32 mem_reuse_en;
};
struct self_digital_clk_info {
u32 en;
u32 en_hh;
u32 en_mm;
u32 pos1_x;
u32 pos1_y;
u32 pos2_x;
u32 pos2_y;
u32 pos3_x;
u32 pos3_y;
u32 pos4_x;
u32 pos4_y;
u32 img_width;
u32 img_height;
u32 color;
u32 unicode_attr;
u32 unicode_width;
};
struct self_partial_hlpm_scan {
u32 hlpm_en;
u32 hlpm_mode_sel;
u32 hlpm_area_1;
u32 hlpm_area_2;
u32 hlpm_area_3;
u32 hlpm_area_4;
u32 scan_en;
u32 scan_sl;
u32 scan_el;
};
enum self_move {
SELF_MOVE_OFF,
SELF_MOVE_ON,
SELF_MOVE_RESET,
SELF_MOVE_MAX,
};
struct self_display_op {
bool select;
bool on;
u32 wpos;
u64 wsize;
char *img_buf;
u32 img_size;
int img_checksum;
u32 img_checksum_cal;
};
struct self_display_debug {
u32 SI_X_O;
u32 SI_Y_O;
u32 MEM_SUM_O; /* sum of image data from side memory */
u32 SM_SUM_O; /* sum of image data from self mask */
u32 MEM_WR_DONE_O;
u32 mem_wr_icon_pk_err_o;
u32 mem_wr_var_pk_err_o;
u32 sv_dec_err_fg_o;
u32 scd_dec_err_fg_o;
u32 si_dec_err_fg_o;
u32 CUR_HH_O;
u32 CUR_MM_O;
u32 CUR_MSS_O;
u32 CUR_SS_O;
u32 SM_WR_DONE_O;
u32 SM_PK_ERR_O;
u32 SM_DEC_ERR_O;
};
struct ss_cmd_desc;
int self_display_init(struct samsung_display_driver_data *vdd);
void make_self_dispaly_img_cmds(struct samsung_display_driver_data *vdd,
int cmd, u32 op);
int make_mass_self_display_img_cmds(struct samsung_display_driver_data *vdd,
enum self_display_data_flag op, struct ss_cmd_desc *ss_cmd);
#endif

View File

@ -0,0 +1 @@
export CONFIG_DISPLAY_SAMSUNG=y

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DISPLAY_SAMSUNG 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* COPR :
* Author: QC LCD driver <kr0124.cho@samsung.com>
*
* 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 "ss_dsi_panel_common.h"
#include "ss_copr_common.h"
void ss_set_copr_sum(struct samsung_display_driver_data *vdd, enum COPR_CD_INDEX idx)
{
s64 delta;
mutex_lock(&vdd->copr.copr_val_lock);
vdd->copr.copr_cd[idx].last_t = vdd->copr.copr_cd[idx].cur_t;
vdd->copr.copr_cd[idx].cur_t = ktime_get();
delta = ktime_ms_delta(vdd->copr.copr_cd[idx].cur_t, vdd->copr.copr_cd[idx].last_t);
vdd->copr.copr_cd[idx].total_t += delta;
if (vdd->br_info.common_br.gamma_mode2_support)
vdd->copr.copr_cd[idx].cd_sum += (vdd->br_info.common_br.cd_level * delta);
mutex_unlock(&vdd->copr.copr_val_lock);
LCD_DEBUG(vdd, "[%d] cd (%d) delta (%lld) cd_sum (%lld) total_t (%lld)\n",
idx,
vdd->br_info.common_br.cd_level,
delta,
vdd->copr.copr_cd[idx].cd_sum,
vdd->copr.copr_cd[idx].total_t);
}
int ss_copr_init(struct samsung_display_driver_data *vdd)
{
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR(vdd, "vdd is null or error\n");
return -ENODEV;
}
mutex_init(&vdd->copr.copr_val_lock);
mutex_init(&vdd->copr.copr_lock);
LCD_INFO(vdd, "Success to initialized copr\n");
return 0;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* COPR :
* Author: QC LCD driver <kr0124.cho@samsung.com>
*
* 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.
*/
#ifndef __SS_COPR_COMMON_H__
#define __SS_COPR_COMMON_H__
/* MAX COPR ROI is from copr ver. 3.0 */
#define MAX_COPR_ROI_CNT 6
enum COPR_CD_INDEX {
COPR_CD_INDEX_0, /* for copr show - SSRM */
COPR_CD_INDEX_1, /* for brt_avg show - mDNIe */
MAX_COPR_CD_INDEX,
};
struct COPR_CD {
s64 cd_sum;
int cd_avr;
ktime_t cur_t;
ktime_t last_t;
s64 total_t;
};
struct COPR {
/* read data */
struct mutex copr_lock;
struct mutex copr_val_lock;
struct COPR_CD copr_cd[MAX_COPR_CD_INDEX];
};
void ss_set_copr_sum(struct samsung_display_driver_data *vdd, enum COPR_CD_INDEX idx);
int ss_copr_init(struct samsung_display_driver_data *vdd);
#endif /* __SS_COPR_COMMON_H__ */

View File

@ -0,0 +1,769 @@
/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* Samsung's POC Driver
* Author: ChangJae Jang <cj1225.jang@samsung.com>
*
* 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 "ss_dsi_panel_common.h"
#include "ss_ddi_poc_common.h"
/*
* ERASE (SECTOR)
* Erase operation is not a misc device file operation.
* This is called from sysfs.
*/
static int ss_poc_erase_sector(struct samsung_display_driver_data *vdd, int start, int len)
{
// int pos = 0;
int image_size = 0;
int ret = 0;
// int erase_size = 0;
int target_pos = 0;
image_size = vdd->poc_driver.image_size;
if(!start)
start = vdd->poc_driver.start_addr;
target_pos = start + len;
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_INFO(vdd, "Panel is not ready. Panel State(%d)\n", vdd->panel_state);
return -EBUSY;
}
if (start < 0 || len <= 0) {
LCD_INFO(vdd, "invalid sector erase params.. start(%d), len(%d)\n", start, len);
return -EINVAL;
}
if ((target_pos - start) > vdd->poc_driver.image_size) {
LCD_INFO(vdd, "sould not erase over %d, start(%d) len(%d) target_pos(%d)\n",
vdd->poc_driver.image_size, start, len, target_pos);
return -EINVAL;
}
LCD_INFO(vdd, "start(%d) len(%d) target(%d)\n", start, len, target_pos);
#if 0
for (pos = start; pos < target_pos; pos += erase_size) {
if (unlikely(atomic_read(&vdd->poc_driver.cancel))) {
LCD_INFO(vdd, "cancel poc read by user\n");
ret = -EIO;
goto cancel_poc;
}
if (vdd->poc_driver.poc_erase) {
if (!(pos % POC_ERASE_64KB) && (pos + POC_ERASE_64KB <= target_pos))
erase_size = POC_ERASE_64KB;
else if (!(pos % POC_ERASE_32KB) && (pos + POC_ERASE_32KB <= target_pos))
erase_size = POC_ERASE_32KB;
else
erase_size = POC_ERASE_4KB;
ret = vdd->poc_driver.poc_erase(vdd, pos, erase_size, target_pos);
if (ret) {
LCD_INFO(vdd, "fail to erase, pos(%d)\n", pos);
return -EIO;
}
} else {
LCD_INFO(vdd, "No poc_erase function. \n");
return -EIO;
}
}
#else
if (vdd->poc_driver.poc_erase)
ret = vdd->poc_driver.poc_erase(vdd, start, len, target_pos);
else
LCD_ERR(vdd, "No poc_erase function..\n");
#endif
#if 0
cancel_poc:
if (unlikely(atomic_read(&vdd->poc_driver.cancel))) {
LCD_INFO(vdd, "cancel poc read by user\n");
atomic_set(&vdd->poc_driver.cancel, 0);
ret = -EIO;
}
#endif
return ret;
}
static int ss_poc_write(struct samsung_display_driver_data *vdd, u8 *data, u32 write_pos, u32 write_size)
{
int ret = 0;
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_INFO(vdd, "Panel is not ready. Panel State(%d)\n", vdd->panel_state);
return -EBUSY;
}
if (vdd->poc_driver.poc_write) {
ret = vdd->poc_driver.poc_write(vdd, data, write_pos, write_size);
}
else {
LCD_INFO(vdd, "No poc_write function. \n");
return -ENODEV;
}
return ret;
}
static int ss_poc_read(struct samsung_display_driver_data *vdd, u8 *buf, u32 read_pos, u32 read_size)
{
int ret = 0;
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_INFO(vdd, "Panel is not ready. Panel State(%d)\n", vdd->panel_state);
return -EBUSY;
}
if (vdd->poc_driver.poc_read) {
ret = vdd->poc_driver.poc_read(vdd, buf, read_pos, read_size);
}
else {
LCD_INFO(vdd, "No poc_read function. \n");
return -ENODEV;
}
return ret;
}
#if 0
int ss_poc_read_mca(struct samsung_display_driver_data *vdd)
{
struct dsi_panel_cmd_set *mca_rx_cmds = NULL;
int rx_len;
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver!\n");
return -ENODEV;
}
mca_rx_cmds = ss_get_cmds(vdd, RX_POC_MCA_CHECK);
if (SS_IS_CMDS_NULL(mca_rx_cmds)) {
LCD_INFO(vdd, "no cmds for RX_POC_MCA_CHECK..\n");
return -ENOMEM;
}
vdd->poc_driver.mca_size = mca_rx_cmds->cmds->msg.rx_len;
LCD_INFO(vdd, "mca rx size (%d)\n", vdd->poc_driver.mca_size);
if (vdd->poc_driver.mca_size) {
if (vdd->poc_driver.mca_data == NULL) {
vdd->poc_driver.mca_data = kmalloc(vdd->poc_driver.mca_size, GFP_KERNEL);
if (!vdd->poc_driver.mca_data) {
LCD_INFO(vdd, "fail to kmalloc for mca_data\n");
return -ENOMEM;
}
}
} else {
LCD_INFO(vdd, "No rx size!\n");
return -EINVAL;
}
rx_len = ss_send_cmd_get_rx(vdd, RX_POC_MCA_CHECK, vdd->poc_driver.mca_data);
if (rx_len < 0) {
LCD_ERR(vdd, "failed to read_mca\n");
return rx_len;
}
return 0;
}
#endif
void ss_poc_comp(struct samsung_display_driver_data *vdd)
{
if (vdd->poc_driver.poc_comp)
vdd->poc_driver.poc_comp(vdd);
return;
}
static int ss_poc_checksum(struct samsung_display_driver_data *vdd)
{
LCD_INFO(vdd, "POC: checksum\n");
return 0;
}
static int ss_poc_check_flash(struct samsung_display_driver_data *vdd)
{
LCD_INFO(vdd, "POC: check flash\n");
return 0;
}
static int ss_dsi_poc_ctrl(struct samsung_display_driver_data *vdd, u32 cmd, const char *buf)
{
int ret = 0;
int erase_start = 0;
int erase_len = 0;
if (cmd >= MAX_POC_OP) {
LCD_INFO(vdd, "invalid poc_op %d\n", cmd);
return -EINVAL;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver!\n");
return -ENODEV;
}
LCD_INFO(vdd, " cmd = %d ++\n", cmd);
switch (cmd) {
case POC_OP_ERASE:
break;
case POC_OP_ERASE_SECTOR:
if (buf == NULL) {
LCD_INFO(vdd, "buf is null..\n");
return -EINVAL;
}
ret = sscanf(buf, "%*d %d %d", &erase_start, &erase_len);
if (unlikely(ret < 2)) {
LCD_INFO(vdd, "fail to get erase param..\n");
return -EINVAL;
}
vdd->poc_driver.er_try_cnt++;
ret = ss_poc_erase_sector(vdd, erase_start, erase_len);
if (unlikely(ret < 0)) {
LCD_INFO(vdd, "failed to poc-erase-sector-seq\n");
vdd->poc_driver.er_fail_cnt++;
return ret;
}
break;
case POC_OP_WRITE:
if (vdd->poc_driver.wbuf == NULL) {
LCD_INFO(vdd, "poc_driver.wbuf is NULL\n");
return -EINVAL;
}
ret = ss_poc_write(vdd,
vdd->poc_driver.wbuf,
POC_IMG_ADDR + vdd->poc_driver.wpos,
vdd->poc_driver.wsize);
if (unlikely(ret < 0)) {
LCD_INFO(vdd, "failed to write poc-write-seq\n");
return ret;
}
break;
case POC_OP_READ:
if (vdd->poc_driver.rbuf == NULL) {
LCD_INFO(vdd, "poc_driver.rbuf is NULL\n");
return -EINVAL;
}
ret = ss_poc_read(vdd,
vdd->poc_driver.rbuf,
POC_IMG_ADDR + vdd->poc_driver.rpos,
vdd->poc_driver.rsize);
if (unlikely(ret < 0)) {
LCD_INFO(vdd, "failed to write poc-read-seq\n");
return ret;
}
break;
case POC_OP_ERASE_WRITE_IMG:
break;
case POC_OP_ERASE_WRITE_TEST:
break;
case POC_OP_BACKUP:
break;
case POC_OP_CHECKSUM:
ss_poc_checksum(vdd);
break;
case POC_OP_CHECK_FLASH:
ss_poc_check_flash(vdd);
break;
case POC_OP_SET_FLASH_WRITE:
break;
case POC_OP_SET_FLASH_EMPTY:
break;
case POC_OP_NONE:
break;
default:
LCD_INFO(vdd, "%s invalid poc_op %d\n", __func__, cmd);
break;
}
LCD_INFO(vdd, " cmd = %d --\n", cmd);
return ret;
}
static long ss_dsi_poc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int ret = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_INFO(vdd, "no vdd\n");
return -EINVAL;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver!\n");
return -ENODEV;
}
if (!ss_is_ready_to_send_cmd(vdd)) {
LCD_INFO(vdd, "Panel is not ready. Panel State(%d)\n", vdd->panel_state);
return -ENODEV;
}
LCD_INFO(vdd, "POC IOCTL CMD=%d\n", cmd);
switch (cmd) {
case IOC_GET_POC_CHKSUM:
ret = ss_dsi_poc_ctrl(vdd, POC_OP_CHECKSUM, NULL);
if (ret) {
LCD_INFO(vdd, "%s error set_panel_poc\n", __func__);
ret = -EFAULT;
}
if (copy_to_user((u8 __user *)arg,
&vdd->poc_driver.chksum_res,
sizeof(vdd->poc_driver.chksum_res))) {
ret = -EFAULT;
break;
}
break;
case IOC_GET_POC_CSDATA:
ret = ss_dsi_poc_ctrl(vdd, POC_OP_CHECKSUM, NULL);
if (ret) {
LCD_INFO(vdd, "%s error set_panel_poc\n", __func__);
ret = -EFAULT;
}
if (copy_to_user((u8 __user *)arg,
vdd->poc_driver.chksum_data,
sizeof(vdd->poc_driver.chksum_data))) {
ret = -EFAULT;
break;
}
break;
default:
break;
};
return ret;
}
static atomic_t poc_open_check = ATOMIC_INIT(1); /* OPEN/RELEASE CHECK */
static int ss_dsi_poc_open(struct inode *inode, struct file *file)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int ret = 0;
int image_size = 0;
LCD_INFO(vdd, "POC Open !!\n");
if (IS_ERR_OR_NULL(vdd)) {
LCD_INFO(vdd, "no vdd\n");
return -ENOMEM;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver! \n");
return -ENODEV;
}
if (!atomic_dec_and_test (&poc_open_check)) {
atomic_inc(&poc_open_check);
LCD_INFO(vdd, "Already open_ongoing : counter (%d)\n", poc_open_check.counter);
return -ENOMEM;
}
image_size = vdd->poc_driver.image_size;
if (likely(!vdd->poc_driver.wbuf)) {
vdd->poc_driver.wbuf = vmalloc(image_size);
if (unlikely(!vdd->poc_driver.wbuf)) {
LCD_INFO(vdd, "%s: fail to allocate poc wbuf\n", __func__);
return -ENOMEM;
}
}
vdd->poc_driver.wpos = 0;
vdd->poc_driver.wsize = 0;
if (likely(!vdd->poc_driver.rbuf)) {
vdd->poc_driver.rbuf = vmalloc(image_size);
if (unlikely(!vdd->poc_driver.rbuf)) {
vfree(vdd->poc_driver.wbuf);
vdd->poc_driver.wbuf = NULL;
LCD_INFO(vdd, "%s: fail to allocate poc rbuf\n", __func__);
return -ENOMEM;
}
}
vdd->poc_driver.rpos = 0;
vdd->poc_driver.rsize = 0;
atomic_set(&vdd->poc_driver.cancel, 0);
return ret;
}
static int ss_dsi_poc_release(struct inode *inode, struct file *file)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int ret = 0;
LCD_INFO(vdd, "POC Release\n");
if (IS_ERR_OR_NULL(vdd)) {
LCD_INFO(vdd, "no vdd\n");
return -ENOMEM;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver! \n");
return -ENODEV;
}
if (unlikely(vdd->poc_driver.wbuf)) {
vfree(vdd->poc_driver.wbuf);
}
if (unlikely(vdd->poc_driver.rbuf)) {
vfree(vdd->poc_driver.rbuf);
}
vdd->poc_driver.wbuf = NULL;
vdd->poc_driver.wpos = 0;
vdd->poc_driver.wsize = 0;
vdd->poc_driver.rbuf = NULL;
vdd->poc_driver.rpos = 0;
vdd->poc_driver.rsize = 0;
atomic_set(&vdd->poc_driver.cancel, 0);
atomic_inc(&poc_open_check);
LCD_INFO(vdd, "poc_open counter (%d)\n", poc_open_check.counter); /* 1 */
return ret;
}
static int _ss_dsi_poc_read(struct samsung_display_driver_data *vdd, char __user *buf, size_t count,
loff_t *ppos)
{
int image_size = 0;
int ret = 0;
LCD_DEBUG(vdd, "ss_dsi_poc_read \n");
if (IS_ERR_OR_NULL(vdd)) {
LCD_INFO(vdd, "no vdd");
return -ENODEV;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver! \n");
return -ENODEV;
}
if (IS_ERR_OR_NULL(vdd->poc_driver.rbuf)) {
LCD_INFO(vdd, "poc_driver.rbuf is NULL\n");
return -EINVAL;
}
if (unlikely(!buf)) {
LCD_INFO(vdd, "invalid read buffer\n");
return -EINVAL;
}
image_size = vdd->poc_driver.rx_size;
if (unlikely(*ppos == image_size)) {
LCD_INFO(vdd, "read is done.. pos (%d), size (%d)\n", (int)*ppos, image_size);
return -EINVAL;
}
if (unlikely(*ppos < 0 || *ppos >= image_size)) {
LCD_INFO(vdd, "invalid read pos (%d), size (%d)\n", (int)*ppos, image_size);
return -EINVAL;
}
if (unlikely(*ppos + count > image_size)) {
LCD_INFO(vdd, "invalid read size, pos %d, count %d, size %d\n",
(int)*ppos, (int)count, image_size);
count = image_size - (int)*ppos;
LCD_INFO(vdd, "resizing: pos %d, count %d, size %d",
(int)*ppos, (int)count, image_size);
}
vdd->poc_driver.rpos = *ppos + vdd->poc_driver.rx_addr;
vdd->poc_driver.rsize = (u32)count;
LCD_INFO(vdd, "rpos 0x%X (%X + %X) rsize %d\n",
vdd->poc_driver.rpos, (int)*ppos, vdd->poc_driver.rx_addr, vdd->poc_driver.rsize);
ret = ss_dsi_poc_ctrl(vdd, POC_OP_READ, NULL);
if (ret) {
LCD_INFO(vdd, "fail to read poc (%d)\n", ret);
return ret;
}
return simple_read_from_buffer(buf, count, ppos, vdd->poc_driver.rbuf, image_size);
}
static ssize_t ss_dsi_poc_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int ret;
vdd->poc_driver.rd_try_cnt++;
ret = _ss_dsi_poc_read(vdd, buf, count, ppos);
if (ret < 0) {
LCD_INFO(vdd, "fail to poc read..\n");
vdd->poc_driver.rd_fail_cnt++;
}
return ret;
}
static ssize_t _ss_dsi_poc_write(struct samsung_display_driver_data *vdd, const char __user *buf,
size_t count, loff_t *ppos)
{
int image_size = 0;
int pos, ret = 0;
LCD_INFO(vdd, "ss_dsi_poc_write : count (%d), ppos(%d)\n", (int)count, (int)*ppos);
if (IS_ERR_OR_NULL(vdd)) {
LCD_INFO(vdd, "no vdd");
return -ENODEV;;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver! \n");
return -ENODEV;
}
if (IS_ERR_OR_NULL(vdd->poc_driver.wbuf)) {
LCD_INFO(vdd, "poc_driver.wbuf is NULL\n");
return -EINVAL;
}
if (unlikely(!buf)) {
LCD_INFO(vdd, "invalid read buffer\n");
return -EINVAL;
}
image_size = vdd->poc_driver.image_size;
if (unlikely(*ppos < 0 || *ppos >= image_size)) {
LCD_INFO(vdd, "invalid write pos (%d) - size (%d)\n", (int)*ppos, image_size);
return -EINVAL;
}
if (unlikely(*ppos + count > image_size)) {
LCD_INFO(vdd, "invalid write size pos %d, count %d, size %d\n",
(int)*ppos, (int)count, image_size);
count = image_size - (int)*ppos;
LCD_INFO(vdd, "resizing: pos %d, count %d, size %d",
(int)*ppos, (int)count, image_size);
}
pos = (int)*ppos;
vdd->poc_driver.wpos = *ppos + vdd->poc_driver.start_addr;
vdd->poc_driver.wsize = (u32)count;
LCD_INFO(vdd, "wpos 0x%X (ppos 0x%X)/ wsize %d (0x%X) / total_image_size (%d)\n",
vdd->poc_driver.wpos, (int)*ppos, vdd->poc_driver.wsize, vdd->poc_driver.wsize, image_size);
ret = simple_write_to_buffer(vdd->poc_driver.wbuf, image_size, ppos, buf, count);
if (unlikely(ret < 0)) {
LCD_INFO(vdd, "failed to simple_write_to_buffer \n");
return ret;
}
ret = ss_dsi_poc_ctrl(vdd, POC_OP_WRITE, NULL);
if (ret) {
LCD_INFO(vdd, "fail to write poc (%d)\n", ret);
return ret;
}
return count;
}
static ssize_t ss_dsi_poc_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct miscdevice *c = file->private_data;
struct dsi_display *display = dev_get_drvdata(c->parent);
struct dsi_panel *panel = display->panel;
struct samsung_display_driver_data *vdd = panel->panel_private;
int ret = 0;
vdd->poc_driver.wr_try_cnt++;
ret = _ss_dsi_poc_write(vdd, buf, count, ppos);
if (ret < 0) {
LCD_INFO(vdd, "fail to poc write..\n");
vdd->poc_driver.wr_fail_cnt++;
}
return ret;
}
static const struct file_operations poc_fops = {
.owner = THIS_MODULE,
.read = ss_dsi_poc_read,
.write = ss_dsi_poc_write,
.unlocked_ioctl = ss_dsi_poc_ioctl,
.open = ss_dsi_poc_open,
.release = ss_dsi_poc_release,
.llseek = generic_file_llseek,
};
#define EPOCEFS_IMGIDX (100)
enum {
EPOCEFS_NOENT = 1, /* No such file or directory */
EPOCEFS_EMPTY = 2, /* Empty file */
EPOCEFS_READ = 3, /* Read failed */
MAX_EPOCEFS,
};
#define POC_TOTAL_TRY_COUNT_FILE_PATH ("/efs/afc/apply_count")
#define POC_TOTAL_FAIL_COUNT_FILE_PATH ("/efs/afc/fail_count")
#define POC_INFO_FILE_PATH ("/efs/FactoryApp/poc_info")
#define POC_USER_FILE_PATH ("/efs/FactoryApp/poc_user")
__visible_for_testing int poc_dpui_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct POC *poc = container_of(self, struct POC, dpui_notif);
struct samsung_display_driver_data *vdd = container_of(poc, struct samsung_display_driver_data, poc_driver);
char tbuf[MAX_DPUI_VAL_LEN];
int total_fail_cnt = 0;
int total_try_cnt = 0;
int size = 0, poci = 0, poci_org = 0;
if (poc == NULL) {
LCD_INFO(vdd, "err: poc is null\n");
return -EINVAL;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver!\n");
return -ENODEV;
}
size = scnprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", total_try_cnt);
set_dpui_field(DPUI_KEY_PNPOCT, tbuf, size);
size = scnprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", total_fail_cnt);
set_dpui_field(DPUI_KEY_PNPOCF, tbuf, size);
size = scnprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poci_org);
set_dpui_field(DPUI_KEY_PNPOCI_ORG, tbuf, size);
size = scnprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poci);
set_dpui_field(DPUI_KEY_PNPOCI, tbuf, size);
inc_dpui_u32_field(DPUI_KEY_PNPOC_ER_TRY, poc->er_try_cnt);
inc_dpui_u32_field(DPUI_KEY_PNPOC_ER_FAIL, poc->er_fail_cnt);
inc_dpui_u32_field(DPUI_KEY_PNPOC_WR_TRY, poc->wr_try_cnt);
inc_dpui_u32_field(DPUI_KEY_PNPOC_WR_FAIL, poc->wr_fail_cnt);
inc_dpui_u32_field(DPUI_KEY_PNPOC_RD_TRY, poc->rd_try_cnt);
inc_dpui_u32_field(DPUI_KEY_PNPOC_RD_FAIL, poc->rd_fail_cnt);
LCD_INFO(vdd, "poc dpui: try=%d, fail=%d, id=%d, %d\n",
total_try_cnt, total_fail_cnt, poci, poci_org);
LCD_INFO(vdd, "poc dpui: er (%d/%d), wr (%d/%d), rd (%d/%d)\n",
poc->er_try_cnt, poc->er_fail_cnt,
poc->wr_try_cnt, poc->wr_fail_cnt,
poc->rd_try_cnt, poc->rd_fail_cnt);
poc->er_try_cnt = 0;
poc->er_fail_cnt = 0;
poc->wr_try_cnt = 0;
poc->wr_fail_cnt = 0;
poc->rd_try_cnt = 0;
poc->rd_fail_cnt = 0;
return 0;
}
static int ss_dsi_poc_register_dpui(struct POC *poc)
{
memset(&poc->dpui_notif, 0,
sizeof(poc->dpui_notif));
poc->dpui_notif.notifier_call = poc_dpui_notifier_callback;
return dpui_logging_register(&poc->dpui_notif, DPUI_TYPE_PANEL);
}
#define POC_DEV_NAME_SIZE 10
int ss_dsi_poc_init(struct samsung_display_driver_data *vdd)
{
int ret;
static char devname[POC_DEV_NAME_SIZE] = {'\0', };
struct dsi_panel *panel = NULL;
struct mipi_dsi_host *host = NULL;
struct dsi_display *display = NULL;
if (IS_ERR_OR_NULL(vdd)) {
LCD_INFO(vdd, "no vdd");
return -ENODEV;
}
if (!vdd->poc_driver.is_support) {
LCD_INFO(vdd, "Not Support POC Driver!\n");
return -ENODEV;
}
LCD_INFO(vdd, "++\n");
panel = (struct dsi_panel *)vdd->msm_private;
host = panel->mipi_device.host;
display = container_of(host, struct dsi_display, host);
if (vdd->ndx == PRIMARY_DISPLAY_NDX)
snprintf(devname, POC_DEV_NAME_SIZE, "poc");
else
snprintf(devname, POC_DEV_NAME_SIZE, "poc%d", vdd->ndx);
vdd->poc_driver.dev.minor = MISC_DYNAMIC_MINOR;
vdd->poc_driver.dev.name = devname;
vdd->poc_driver.dev.fops = &poc_fops;
vdd->poc_driver.dev.parent = &display->pdev->dev;
vdd->poc_driver.wbuf = NULL;
vdd->poc_driver.rbuf = NULL;
atomic_set(&vdd->poc_driver.cancel, 0);
/* Drvier level big data for POC operation */
vdd->poc_driver.er_try_cnt = 0;
vdd->poc_driver.er_fail_cnt = 0;
vdd->poc_driver.wr_try_cnt = 0;
vdd->poc_driver.wr_fail_cnt = 0;
vdd->poc_driver.rd_try_cnt = 0;
vdd->poc_driver.rd_fail_cnt = 0;
vdd->panel_func.samsung_poc_ctrl = ss_dsi_poc_ctrl;
ret = ss_wrapper_misc_register(vdd, &vdd->poc_driver.dev);
if (ret) {
LCD_INFO(vdd, "failed to register POC driver : %d\n", ret);
return ret;
}
ss_dsi_poc_register_dpui(&vdd->poc_driver);
LCD_INFO(vdd, "--\n");
return ret;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* Samsung's POC Driver
* Author: ChangJae Jang <cj1225.jang@samsung.com>
*
* 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.
*/
#ifndef SAMSUNG_POC_COMMON_H
#define SAMSUNG_POC_COMMON_H
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/err.h>
#include <linux/mutex.h>
#define POC_IMG_ADDR (0x000000)
#define POC_ERASE_4KB (4096)
#define POC_ERASE_32KB (32768)
#define POC_ERASE_64KB (65536)
#define DEBUG_POC_CNT 4096
int ss_dsi_poc_init(struct samsung_display_driver_data *vdd);
int ss_poc_read_mca(struct samsung_display_driver_data *vdd);
void ss_poc_comp(struct samsung_display_driver_data *vdd);
#endif

View File

@ -0,0 +1,362 @@
/*
* Samsung Common LCD DPUI(display use info) LOGGING Driver.
*
* Copyright (c) 2017 Samsung Electronics
*
* 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/fb.h>
#include <linux/notifier.h>
#include <linux/export.h>
#include "ss_dpui_common.h"
/*
* DPUI : display use info (panel common info)
* DPCI : display controller info (ap dependent info)
*/
static BLOCKING_NOTIFIER_HEAD(dpui_notifier_list);
static BLOCKING_NOTIFIER_HEAD(dpci_notifier_list);
static DEFINE_MUTEX(dpui_lock);
/**
* dpui_logging_notify - notify clients of fb_events
* @val: dpui log type
* @v : data
*
*/
void dpui_logging_notify(unsigned long val, enum dpui_type type, void *v)
{
if (type == DPUI_TYPE_CTRL)
blocking_notifier_call_chain(&dpci_notifier_list, val, v);
else
blocking_notifier_call_chain(&dpui_notifier_list, val, v);
}
EXPORT_SYMBOL_GPL(dpui_logging_notify);
/**
* dpui_logging_register - register a client notifier
* @n: notifier block to callback on events
*/
int dpui_logging_register(struct notifier_block *n, enum dpui_type type)
{
int ret;
if (type <= DPUI_TYPE_NONE || type >= MAX_DPUI_TYPE) {
pr_err("%s out of dpui_type range (%d)\n", __func__, type);
return -EINVAL;
}
if (type == DPUI_TYPE_CTRL)
ret = blocking_notifier_chain_register(&dpci_notifier_list, n);
else
ret = blocking_notifier_chain_register(&dpui_notifier_list, n);
if (ret < 0) {
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
__func__, ret);
return ret;
}
pr_info("%s register type %s\n", __func__, dpui_type_name[type]);
return 0;
}
EXPORT_SYMBOL_GPL(dpui_logging_register);
/**
* dpui_logging_unregister - unregister a client notifier
* @n: notifier block to callback on events
*/
int dpui_logging_unregister(struct notifier_block *n)
{
return blocking_notifier_chain_unregister(&dpui_notifier_list, n);
}
EXPORT_SYMBOL_GPL(dpui_logging_unregister);
static bool is_dpui_var_u32(enum dpui_key key)
{
return (dpui.field[key].var_type == DPUI_VAR_U32);
}
void update_dpui_log(enum dpui_log_level level, enum dpui_type type)
{
if (level < 0 || level >= MAX_DPUI_LOG_LEVEL) {
pr_err("%s invalid log level %d\n", __func__, level);
return;
}
dpui_logging_notify(level, type, &dpui);
pr_info("%s update dpui log(%d) done\n",
__func__, level);
}
int clear_dpui_log(enum dpui_log_level level, enum dpui_type type)
{
int i;
int ret = 0;
if (level < 0 || level >= MAX_DPUI_LOG_LEVEL) {
pr_err("%s invalid log level %d\n", __func__, level);
return -EINVAL;
}
mutex_lock(&dpui_lock);
for (i = 0; i < ARRAY_SIZE(dpui.field); i++) {
if (dpui.field[i].type != type)
continue;
if (dpui.field[i].auto_clear)
dpui.field[i].initialized = false;
}
mutex_unlock(&dpui_lock);
pr_info("%s clear dpui log(%d) done\n",
__func__, level);
return ret;
}
static int __get_dpui_field(enum dpui_key key, char *buf)
{
if (!buf) {
pr_err("%s buf is null\n", __func__);
return 0;
}
if (!DPUI_VALID_KEY(key)) {
pr_err("%s out of dpui_key range (%d)\n", __func__, key);
return 0;
}
if (!dpui.field[key].initialized) {
pr_debug("%s DPUI:%s not initialized, so use default value\n",
__func__, dpui_key_name[key]);
return snprintf(buf, MAX_DPUI_KEY_LEN + MAX_DPUI_VAL_LEN,
"\"%s\":\"%s\"", dpui_key_name[key], dpui.field[key].default_value);
}
return snprintf(buf, MAX_DPUI_KEY_LEN + MAX_DPUI_VAL_LEN,
"\"%s\":\"%s\"", dpui_key_name[key], dpui.field[key].buf);
}
void print_dpui_field(enum dpui_key key)
{
char tbuf[MAX_DPUI_KEY_LEN + MAX_DPUI_VAL_LEN];
if (!DPUI_VALID_KEY(key)) {
pr_err("%s out of dpui_key range (%d)\n", __func__, key);
return;
}
__get_dpui_field(key, tbuf);
pr_info("DPUI: %s\n", tbuf);
}
static int __set_dpui_field(enum dpui_key key, char *buf, int size)
{
if (!buf) {
pr_err("%s buf is null\n", __func__);
return -EINVAL;
}
if (!DPUI_VALID_KEY(key)) {
pr_err("%s out of dpui_key range (%d)\n", __func__, key);
return -EINVAL;
}
if (size > MAX_DPUI_VAL_LEN - 1) {
pr_err("%s exceed dpui value size (%d)\n", __func__, size);
return -EINVAL;
}
memcpy(dpui.field[key].buf, buf, size);
dpui.field[key].buf[size] = '\0';
dpui.field[key].initialized = true;
return 0;
}
static int __get_dpui_u32_field(enum dpui_key key, u32 *value)
{
int rc, cur_val;
if (value == NULL) {
pr_err("%s invalid value pointer\n", __func__);
return -EINVAL;
}
if (!DPUI_VALID_KEY(key)) {
pr_err("%s out of dpui_key range (%d)\n", __func__, key);
return -EINVAL;
}
rc = kstrtouint(dpui.field[key].buf, (unsigned int)0, &cur_val);
if (rc < 0) {
pr_err("%s failed to get value\n", __func__);
return rc;
}
*value = cur_val;
return 0;
}
static int __set_dpui_u32_field(enum dpui_key key, u32 value)
{
char tbuf[MAX_DPUI_VAL_LEN];
int size;
if (!DPUI_VALID_KEY(key)) {
pr_err("%s out of dpui_key range (%d)\n", __func__, key);
return -EINVAL;
}
if (!is_dpui_var_u32(key)) {
pr_err("%s invalid type %d\n", __func__, dpui.field[key].var_type);
return -EINVAL;
}
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%u", value);
if (size > MAX_DPUI_VAL_LEN) {
pr_err("%s exceed dpui value size (%d)\n", __func__, size);
return -EINVAL;
}
__set_dpui_field(key, tbuf, size);
return 0;
}
static int __inc_dpui_u32_field(enum dpui_key key, u32 value)
{
int ret;
u32 cur_val = 0;
if (!DPUI_VALID_KEY(key)) {
pr_err("%s out of dpui_key range (%d)\n", __func__, key);
return -EINVAL;
}
if (!is_dpui_var_u32(key)) {
pr_err("%s invalid type %d\n", __func__, dpui.field[key].var_type);
return -EINVAL;
}
if (dpui.field[key].initialized) {
ret = __get_dpui_u32_field(key, &cur_val);
if (ret < 0) {
pr_err("%s failed to get u32 field (%d)\n", __func__, ret);
return -EINVAL;
}
}
__set_dpui_u32_field(key, cur_val + value);
return 0;
}
int get_dpui_field(enum dpui_key key, char *buf)
{
int ret;
mutex_lock(&dpui_lock);
ret = __get_dpui_field(key, buf);
mutex_unlock(&dpui_lock);
return ret;
}
int set_dpui_field(enum dpui_key key, char *buf, int size)
{
int ret;
mutex_lock(&dpui_lock);
ret = __set_dpui_field(key, buf, size);
mutex_unlock(&dpui_lock);
return ret;
}
int get_dpui_u32_field(enum dpui_key key, u32 *value)
{
int ret;
mutex_lock(&dpui_lock);
ret = __get_dpui_u32_field(key, value);
mutex_unlock(&dpui_lock);
return ret;
}
int set_dpui_u32_field(enum dpui_key key, u32 value)
{
int ret;
mutex_lock(&dpui_lock);
ret = __set_dpui_u32_field(key, value);
mutex_unlock(&dpui_lock);
return ret;
}
int inc_dpui_u32_field(enum dpui_key key, u32 value)
{
int ret;
mutex_lock(&dpui_lock);
ret = __inc_dpui_u32_field(key, value);
mutex_unlock(&dpui_lock);
return ret;
}
EXPORT_SYMBOL(inc_dpui_u32_field);
int inc_dpui_u32_field_nolock(enum dpui_key key, u32 value)
{
return __inc_dpui_u32_field(key, value);
}
int __get_dpui_log(char *buf, enum dpui_log_level level, enum dpui_type type)
{
int i, ret, len = 0;
char tbuf[MAX_DPUI_KEY_LEN + MAX_DPUI_VAL_LEN];
if (!buf) {
pr_err("%s buf is null\n", __func__);
return -EINVAL;
}
if (level < 0 || level >= MAX_DPUI_LOG_LEVEL) {
pr_err("%s invalid log level %d\n", __func__, level);
return -EINVAL;
}
mutex_lock(&dpui_lock);
for (i = DPUI_KEY_NONE + 1; i < MAX_DPUI_KEY; i++) {
if (level != DPUI_LOG_LEVEL_ALL && dpui.field[i].level != level) {
pr_warn("%s DPUI:%s different log level %d %d\n",
__func__, dpui_key_name[dpui.field[i].key],
dpui.field[i].level, level);
continue;
}
if (type != dpui.field[i].type)
continue;
ret = __get_dpui_field(i, tbuf);
if (ret == 0)
continue;
if (len)
len += snprintf(buf + len, 3, ",");
len += snprintf(buf + len, MAX_DPUI_KEY_LEN + MAX_DPUI_VAL_LEN,
"%s", tbuf);
}
mutex_unlock(&dpui_lock);
return len;
}
int get_dpui_log(char *buf, enum dpui_log_level level, enum dpui_type type)
{
return __get_dpui_log(buf, level, type);
}
EXPORT_SYMBOL_GPL(get_dpui_log);

View File

@ -0,0 +1,322 @@
/*
* Header file for Samsung Common LCD Driver.
*
* Copyright (c) 2017 Samsung Electronics
* Gwanghui Lee <gwanghui.lee@samsung.com>
*
* 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.
*/
#ifndef __DPUI_H__
#define __DPUI_H__
#define MAX_DPUI_KEY_LEN (20)
/* Increase buf size for SS LOG.
* TODO: Instead of fixed length, allocate buf dynamically using kmalloc.
*/
#define MAX_DPUI_VAL_LEN (700) // (128)
enum {
DPUI_AUTO_CLEAR_OFF,
DPUI_AUTO_CLEAR_ON,
MAX_DPUI_AUTO_CLEAR,
};
enum dpui_log_level {
DPUI_LOG_LEVEL_DEBUG,
DPUI_LOG_LEVEL_INFO,
DPUI_LOG_LEVEL_ALL,
MAX_DPUI_LOG_LEVEL,
};
enum dpui_type {
DPUI_TYPE_NONE,
DPUI_TYPE_PANEL,
DPUI_TYPE_CTRL,
MAX_DPUI_TYPE,
};
enum dpui_var_type {
DPUI_VAR_BOOL,
DPUI_VAR_S32,
DPUI_VAR_S64,
DPUI_VAR_U32,
DPUI_VAR_U64,
DPUI_VAR_STR,
MAX_DPUI_VAR_TYPE,
};
#define DPUI_VALID_VAR_TYPE(_vtype_) \
(((_vtype_) >= DPUI_VAR_BOOL) && ((_vtype_) < MAX_DPUI_VAR_TYPE))
enum dpui_key {
DPUI_KEY_NONE,
DPUI_KEY_WCRD_X, /* white color x-coordinate */
DPUI_KEY_WCRD_Y, /* white color y-coordinate */
DPUI_KEY_WOFS_R, /* mdnie whiteRGB red offset from user and factory */
DPUI_KEY_WOFS_G, /* mdnie whiteRGB green offset from user and factory */
DPUI_KEY_WOFS_B, /* mdnie whiteRGB blue offset from user and factory */
DPUI_KEY_WOFS_R_ORG, /* mdnie whiteRGB red offset from factory */
DPUI_KEY_WOFS_G_ORG, /* mdnie whiteRGB green offset from factory */
DPUI_KEY_WOFS_B_ORG, /* mdnie whiteRGB blue offset from factory */
DPUI_KEY_LCDID1, /* panel id 1 */
DPUI_KEY_LCDID2, /* panel id 2 */
DPUI_KEY_LCDID3, /* panel id 3 */
DPUI_KEY_MAID_DATE, /* panel manufacture date */
DPUI_KEY_CELLID, /* panel cell id */
DPUI_KEY_OCTAID, /* panel octa id */
DPUI_KEY_PNDSIE, /* panel dsi error count */
DPUI_KEY_PNELVDE, /* panel ELVDD error count */
DPUI_KEY_PNVLI1E, /* panel VLIN1 error count */
DPUI_KEY_PNVLO3E, /* panel VLOUT3 error count */
DPUI_KEY_PNSDRE, /* panel OTP loading error count */
/* POC */
DPUI_KEY_PNPOCT, /* panel POC try count */
DPUI_KEY_PNPOCF, /* panel POC fail count */
DPUI_KEY_PNPOCI, /* panel POC image index */
DPUI_KEY_PNPOCI_ORG, /* panel POC image index in factory */
DPUI_KEY_PNPOC_ER_TRY, /* panel POC erase try count */
DPUI_KEY_PNPOC_ER_FAIL, /* panel POC erase fail count */
DPUI_KEY_PNPOC_WR_TRY, /* panel POC write try count */
DPUI_KEY_PNPOC_WR_FAIL, /* panel POC write fail count */
DPUI_KEY_PNPOC_RD_TRY, /* panel POC read try count */
DPUI_KEY_PNPOC_RD_FAIL, /* panel POC read fail count */
/* GAMMA FLASH */
DPUI_KEY_PNGFLS, /* panel gamma flash loading result */
/* dependent on processor */
DPUI_KEY_QCT_DSIE, /* display controller dsi error count */
DPUI_KEY_QCT_PPTO, /* display controller pingpong timeout count */
DPUI_KEY_QCT_NO_TE, /* display controller no TE response count */
DPUI_KEY_QCT_RCV_CNT, /* display controller ESD recovery count */
DPUI_KEY_QCT_SSLOG, /* display controller ss debugging log */
DPUI_KEY_QCT_MAIN_RX_FAIL_CNT, /* main display mipi rx fail count */
DPUI_KEY_QCT_SUB_RX_FAIL_CNT, /* sub display mipi rx fail count */
/* GPU */
DPUI_KEY_QCT_GPU_PF, /* GPU Page Fault Count */
DPUI_KEY_UB_CON, /* UB con detect */
DPUI_KEY_SUB_UB_CON, /* SUB UB con detect */
DPUI_KEY_ECC_ERR, /* ECC Error */
DPUI_KEY_FLASH_LOAD, /* Flash loading failure count */
MAX_DPUI_KEY,
};
#define DPUI_VALID_KEY(_key_) \
(((_key_) > DPUI_KEY_NONE) && ((_key_) < MAX_DPUI_KEY))
struct dpui_field {
enum dpui_type type;
enum dpui_var_type var_type;
bool auto_clear;
bool initialized;
enum dpui_key key;
enum dpui_log_level level;
char *default_value;
char buf[MAX_DPUI_VAL_LEN];
};
#define DPUI_FIELD_INIT(_level_, _type_, _var_type_, _auto_clr_, _default_val_, _key_) \
[(_key_)] = { \
.type = (_type_), \
.var_type = (_var_type_), \
.auto_clear = (_auto_clr_), \
.initialized = (false), \
.key = (_key_), \
.level = (_level_), \
.default_value = (_default_val_), \
.buf[0] = ('\0'), \
}
struct dpui_info {
void *pdata;
struct dpui_field field[MAX_DPUI_KEY];
};
static const char * const dpui_key_name[] = {
[DPUI_KEY_NONE] = "NONE",
/* common hw parameter */
[DPUI_KEY_WCRD_X] = "WCRD_X",
[DPUI_KEY_WCRD_Y] = "WCRD_Y",
[DPUI_KEY_WOFS_R] = "WOFS_R",
[DPUI_KEY_WOFS_G] = "WOFS_G",
[DPUI_KEY_WOFS_B] = "WOFS_B",
[DPUI_KEY_WOFS_R_ORG] = "WOFS_R_ORG",
[DPUI_KEY_WOFS_G_ORG] = "WOFS_G_ORG",
[DPUI_KEY_WOFS_B_ORG] = "WOFS_B_ORG",
[DPUI_KEY_LCDID1] = "LCDM_ID1",
[DPUI_KEY_LCDID2] = "LCDM_ID2",
[DPUI_KEY_LCDID3] = "LCDM_ID3",
[DPUI_KEY_MAID_DATE] = "MAID_DATE",
[DPUI_KEY_CELLID] = "CELLID",
[DPUI_KEY_OCTAID] = "OCTAID",
[DPUI_KEY_PNDSIE] = "PNDSIE",
[DPUI_KEY_PNELVDE] = "PNELVDE",
[DPUI_KEY_PNVLI1E] = "PNVLI1E",
[DPUI_KEY_PNVLO3E] = "PNVLO3E",
[DPUI_KEY_PNSDRE] = "PNSDRE",
[DPUI_KEY_PNPOCT] = "PNPOCT",
[DPUI_KEY_PNPOCF] = "PNPOCF",
[DPUI_KEY_PNPOCI] = "PNPOCI",
[DPUI_KEY_PNPOCI_ORG] = "PNPOCI_ORG",
[DPUI_KEY_PNPOC_ER_TRY] = "PNPOC_ER_T",
[DPUI_KEY_PNPOC_ER_FAIL] = "PNPOC_ER_F",
[DPUI_KEY_PNPOC_WR_TRY] = "PNPOC_WR_T",
[DPUI_KEY_PNPOC_WR_FAIL] = "PNPOC_WR_F",
[DPUI_KEY_PNPOC_RD_TRY] = "PNPOC_RD_T",
[DPUI_KEY_PNPOC_RD_FAIL] = "PNPOC_RD_F",
[DPUI_KEY_PNGFLS] = "PNGFLS",
/* dependent on processor */
[DPUI_KEY_QCT_DSIE] = "QCT_DSIE",
[DPUI_KEY_QCT_PPTO] = "QCT_PPTO",
[DPUI_KEY_QCT_NO_TE] = "QCT_NO_TE",
[DPUI_KEY_QCT_RCV_CNT] = "QCT_RCV_CNT",
[DPUI_KEY_QCT_SSLOG] = "QCT_SSLOG",
[DPUI_KEY_QCT_MAIN_RX_FAIL_CNT] = "QCT_MAIN_RX_FAIL_CNT",
[DPUI_KEY_QCT_SUB_RX_FAIL_CNT] = "QCT_SUB_RX_FAIL_CNT",
/* GPU */
[DPUI_KEY_QCT_GPU_PF] = "QCT_GPU_PF",
/* ub con detect */
[DPUI_KEY_UB_CON] = "UB_CON",
[DPUI_KEY_SUB_UB_CON] = "SUB_UB_CON",
[DPUI_KEY_ECC_ERR] = "ECC_ERR",
/* Flash loading failure count */
[DPUI_KEY_FLASH_LOAD] = "FLASH_LOAD",
};
static const char * const dpui_type_name[] = {
[DPUI_TYPE_NONE] = "NONE",
/* common hw parameter */
[DPUI_TYPE_PANEL] = "PANEL",
/* dependent on processor */
[DPUI_TYPE_CTRL] = "CTRL",
};
static struct dpui_info dpui = {
.pdata = NULL,
.field = {
/* common hw parameter */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WCRD_X),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WCRD_Y),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WOFS_R),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WOFS_G),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WOFS_B),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WOFS_R_ORG),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WOFS_G_ORG),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_WOFS_B_ORG),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-1", DPUI_KEY_LCDID1),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-1", DPUI_KEY_LCDID2),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-1", DPUI_KEY_LCDID3),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_STR, DPUI_AUTO_CLEAR_OFF, "19000000 000000", DPUI_KEY_MAID_DATE),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL, DPUI_VAR_STR, DPUI_AUTO_CLEAR_OFF, "0000000000000000000000", DPUI_KEY_CELLID),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL, DPUI_VAR_STR, DPUI_AUTO_CLEAR_OFF, "00000000000000000000000", DPUI_KEY_OCTAID),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNDSIE),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNELVDE),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNVLI1E),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNVLO3E),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNSDRE),
/* POC */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-1", DPUI_KEY_PNPOCT),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-1", DPUI_KEY_PNPOCF),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-100", DPUI_KEY_PNPOCI),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "-100", DPUI_KEY_PNPOCI_ORG),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNPOC_ER_TRY),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNPOC_ER_FAIL),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNPOC_WR_TRY),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNPOC_WR_FAIL),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNPOC_RD_TRY),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_PNPOC_RD_FAIL),
/* Gamma Flash */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_S32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_PNGFLS),
/* dependent on processor */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_DSIE),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_PPTO),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_NO_TE),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_RCV_CNT),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_OFF, "0", DPUI_KEY_QCT_SSLOG),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_MAIN_RX_FAIL_CNT),
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_SUB_RX_FAIL_CNT),
/* GPU */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_GPU_PF),
/* UB CON */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_UB_CON),
/* SUB UB CON */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_SUB_UB_CON),
/* ECC Error */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_ECC_ERR),
/* Flash loading failure count */
DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL,
DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_FLASH_LOAD),
},
};
int dpui_logging_register(struct notifier_block *n, enum dpui_type type);
int dpui_logging_unregister(struct notifier_block *n);
void update_dpui_log(enum dpui_log_level level, enum dpui_type type);
int clear_dpui_log(enum dpui_log_level level, enum dpui_type type);
int get_dpui_log(char *buf, enum dpui_log_level level, enum dpui_type type);
int set_dpui_field(enum dpui_key key, char *buf, int size);
int get_dpui_field(enum dpui_key key, char *buf);
int set_dpui_u32_field(enum dpui_key key, u32 value);
int get_dpui_u32_field(enum dpui_key key, u32 *value);
int inc_dpui_u32_field(enum dpui_key key, u32 value);
int inc_dpui_u32_field_nolock(enum dpui_key key, u32 value);
#endif /* __DPUI_H__ */

Some files were not shown because too many files have changed in this diff Show More