diff --git a/Makefile b/Makefile index 8a8bb92edbe1..ef0fcc19bacf 100644 --- a/Makefile +++ b/Makefile @@ -982,6 +982,14 @@ ifeq ($(CONFIG_STRIP_ASM_SYMS),y) LDFLAGS_vmlinux += $(call ld-option, -X,) endif +ifneq ($(SEC_BUILD_CONF_USE_FINGERPRINT_TZ),false) + ifeq ($(CONFIG_SENSORS_FINGERPRINT),y) + ifneq ($(CONFIG_SEC_FACTORY),y) + export KBUILD_FP_SENSOR_CFLAGS := -DENABLE_SENSORS_FPRINT_SECURE + endif + endif +endif + # Default kernel image to build when no specific target is given. # KBUILD_IMAGE may be overruled on the command line or # set in the environment diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index bca2c3c13530..cd94d253444c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1270,6 +1270,8 @@ config KEYS_COMPAT endmenu +source "arch/arm64/Kconfig.sec" + menu "Power management options" source "kernel/power/Kconfig" diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index 3d9d6f36f8a5..3c20841246f7 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -65,6 +65,22 @@ config DEBUG_ALIGN_RODATA config ARM64_STRICT_BREAK_BEFORE_MAKE bool "Enforce strict break-before-make on page table updates " +comment "PowerManagement Feature" +menuconfig SEC_PM + bool "Samsung TN PowerManagement Feature" + default y + help + Samsung TN PowerManagement Feature. + +if SEC_PM +config SEC_PM_DEBUG + bool "Samsung TN PowerManagement Debug Feature" + default n + help + Samsung TN PowerManagement Debug Feature. + +endif + source "drivers/hwtracing/coresight/Kconfig" endmenu diff --git a/arch/arm64/Kconfig.sec b/arch/arm64/Kconfig.sec new file mode 100644 index 000000000000..9e3bac1843cf --- /dev/null +++ b/arch/arm64/Kconfig.sec @@ -0,0 +1,49 @@ +config SEC_GTS4LV_PROJECT + depends on ARCH_SDM670 + default n + bool "Samsung GTS4LV Project" + help + Support for Samsung GTS4LV Project + +config SEC_GTS4LV_MEA_PROJECT + depends on SEC_GTS4LV_PROJECT + default n + bool "Samsung GTS4LV MEA Project" + help + Support for Samsung GTS4LV MEA Project + +config SEC_GTS4LV_EUR_PROJECT + depends on SEC_GTS4LV_PROJECT + default n + bool "Samsung GTS4LV EUR OPEN Project" + help + Support for Samsung GTS4LV EUR OPEN Project + +config SEC_GTS4LVWIFI_EUR_PROJECT + depends on SEC_GTS4LV_PROJECT + default n + bool "Samsung GTS4LVWIFI EUR OPEN Project" + help + Support for Samsung GTS4LVWIFI EUR OPEN Project + +config SEC_GTS4LVWIFI_EUR_LDU_PROJECT + depends on SEC_GTS4LV_PROJECT + default n + bool "Samsung GTS4LVWIFI EUR LDU Project" + help + Support for Samsung GTS4LVWIFI EUR LDU Project + +config SEC_GTS4LV_KOR_PROJECT + depends on SEC_GTS4LV_PROJECT + default n + bool "Samsung GTS4LV KOR OPEN Project" + help + Support for Samsung GTS4LV KOR OPEN Project + +config SEC_GTS4LV_USA_PROJECT + depends on SEC_GTS4LV_PROJECT + default n + bool "Samsung GTS4LV USA Project" + help + Support for Samsung GTS4LV USA Project + diff --git a/arch/arm64/include/asm/sec_debug.h b/arch/arm64/include/asm/sec_debug.h new file mode 100644 index 000000000000..fa6f9322df2e --- /dev/null +++ b/arch/arm64/include/asm/sec_debug.h @@ -0,0 +1,194 @@ +/* + * arch/arm64/include/asm/sec_debug.h + * + * COPYRIGHT(C) 2006-2016 Samsung Electronics Co., Ltd. All Right Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SEC_DEBUG_ARM64_H +#define SEC_DEBUG_ARM64_H + +#if defined(CONFIG_ARM64) && defined(CONFIG_SEC_DEBUG) + +struct sec_debug_mmu_reg_t { + uint64_t TTBR0_EL1; + uint64_t TTBR1_EL1; + uint64_t TCR_EL1; + uint64_t MAIR_EL1; + uint64_t ATCR_EL1; + uint64_t AMAIR_EL1; + + uint64_t HSTR_EL2; + uint64_t HACR_EL2; + uint64_t TTBR0_EL2; + uint64_t VTTBR_EL2; + uint64_t TCR_EL2; + uint64_t VTCR_EL2; + uint64_t MAIR_EL2; + uint64_t ATCR_EL2; + + uint64_t TTBR0_EL3; + uint64_t MAIR_EL3; + uint64_t ATCR_EL3; +}; + +/* ARM CORE regs mapping structure */ +struct sec_debug_core_t { + /* COMMON */ + uint64_t x0; + uint64_t x1; + uint64_t x2; + uint64_t x3; + uint64_t x4; + uint64_t x5; + uint64_t x6; + uint64_t x7; + uint64_t x8; + uint64_t x9; + uint64_t x10; + uint64_t x11; + uint64_t x12; + uint64_t x13; + uint64_t x14; + uint64_t x15; + uint64_t x16; + uint64_t x17; + uint64_t x18; + uint64_t x19; + uint64_t x20; + uint64_t x21; + uint64_t x22; + uint64_t x23; + uint64_t x24; + uint64_t x25; + uint64_t x26; + uint64_t x27; + uint64_t x28; + uint64_t x29; /* sp */ + uint64_t x30; /* lr */ + + uint64_t pc; /* pc */ + uint64_t cpsr; /* cpsr */ + + /* EL0 */ + uint64_t sp_el0; + + /* EL1 */ + uint64_t sp_el1; + uint64_t elr_el1; + uint64_t spsr_el1; + + /* EL2 */ + uint64_t sp_el2; + uint64_t elr_el2; + uint64_t spsr_el2; + + /* EL3 */ + /* uint64_t sp_el3; */ + /* uint64_t elr_el3; */ + /* uint64_t spsr_el3; */ +}; + +#define READ_SPECIAL_REG(x) ({ \ + uint64_t val; \ + asm volatile ("mrs %0, " # x : "=r"(val)); \ + val; \ +}) + +static inline void sec_debug_save_mmu_reg(struct sec_debug_mmu_reg_t *mmu_reg) +{ + uint64_t pstate, which_el; + + pstate = READ_SPECIAL_REG(CurrentEl); + which_el = pstate & PSR_MODE_MASK; + + /* pr_emerg("%s: sec_debug EL mode=%d\n", __func__,which_el); */ + + mmu_reg->TTBR0_EL1 = READ_SPECIAL_REG(TTBR0_EL1); + mmu_reg->TTBR1_EL1 = READ_SPECIAL_REG(TTBR1_EL1); + mmu_reg->TCR_EL1 = READ_SPECIAL_REG(TCR_EL1); + mmu_reg->MAIR_EL1 = READ_SPECIAL_REG(MAIR_EL1); + mmu_reg->AMAIR_EL1 = READ_SPECIAL_REG(AMAIR_EL1); +} + +static inline void sec_debug_save_core_reg(struct sec_debug_core_t *core_reg) +{ + uint64_t pstate,which_el; + + pstate = READ_SPECIAL_REG(CurrentEl); + which_el = pstate & PSR_MODE_MASK; + + /* pr_emerg("%s: sec_debug EL mode=%d\n", __func__,which_el); */ + + asm volatile ( + "str x0, [%0,#0]\n\t" /* x0 is pushed first to core_reg */ + "mov x0, %0\n\t" + "add x0, x0, 0x8\n\t" + "stp x1, x2, [x0], #0x10\n\t" + "stp x3, x4, [x0], #0x10\n\t" + "stp x5, x6, [x0], #0x10\n\t" + "stp x7, x8, [x0], #0x10\n\t" + "stp x9, x10, [x0], #0x10\n\t" + "stp x11, x12, [x0], #0x10\n\t" + "stp x13, x14, [x0], #0x10\n\t" + "stp x15, x16, [x0], #0x10\n\t" + "stp x17, x18, [x0], #0x10\n\t" + "stp x19, x20, [x0], #0x10\n\t" + "stp x21, x22, [x0], #0x10\n\t" + "stp x23, x24, [x0], #0x10\n\t" + "stp x25, x26, [x0], #0x10\n\t" + "stp x27, x28, [x0], #0x10\n\t" + "stp x29, x30, [x0], #0x10\n\t" + + /* pc */ + "adr x1, .\n\t" + + /* pstate */ + "mrs x15, NZCV\n\t" + "bic x15, x15, #0xFFFFFFFF0FFFFFFF\n\t" + "mrs x9, DAIF\n\t" + "bic x9, x9, #0xFFFFFFFFFFFFFC3F\n\t" + "orr x15, x15, x9\n\t" + "mrs x10, CurrentEL\n\t" + "bic x10, x10, #0xFFFFFFFFFFFFFFF3\n\t" + "orr x15, x15, x10\n\t" + "mrs x11, SPSel\n\t" + "bic x11, x11, #0xFFFFFFFFFFFFFFFE\n\t" + "orr x15, x15, x11\n\t" + + /* store pc & pstate */ + "stp x1, x15, [x0], #0x10\n\t" + + : /* output */ + : "r"(core_reg) /* input */ + : "%x0", "%x1" /* clobbered registers */ + ); + + core_reg->sp_el0 = READ_SPECIAL_REG(sp_el0); + + if(which_el >= PSR_MODE_EL2t){ + core_reg->sp_el0 = READ_SPECIAL_REG(sp_el1); + core_reg->elr_el1 = READ_SPECIAL_REG(elr_el1); + core_reg->spsr_el1 = READ_SPECIAL_REG(spsr_el1); + core_reg->sp_el2 = READ_SPECIAL_REG(sp_el2); + core_reg->elr_el2 = READ_SPECIAL_REG(elr_el2); + core_reg->spsr_el2 = READ_SPECIAL_REG(spsr_el2); + } +} + +#endif /* defined(CONFIG_ARM64) && defined(CONFIG_SEC_DEBUG) */ + +#endif /* SEC_DEBUG_ARM64_H */ diff --git a/arch/arm64/include/uapi/asm/setup.h b/arch/arm64/include/uapi/asm/setup.h index 9cf2e46fbbdf..7474c813d5ab 100644 --- a/arch/arm64/include/uapi/asm/setup.h +++ b/arch/arm64/include/uapi/asm/setup.h @@ -21,6 +21,6 @@ #include -#define COMMAND_LINE_SIZE 2048 +#define COMMAND_LINE_SIZE 4096 #endif diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index b6d3274ab4d7..f193a63830fa 100755 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -59,6 +59,8 @@ #define CREATE_TRACE_POINTS #include +#include + DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number); EXPORT_PER_CPU_SYMBOL(cpu_number); @@ -853,6 +855,7 @@ static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs) pr_crit("CPU%u: stopping\n", cpu); show_regs(regs); dump_stack(); + sec_debug_save_context(); dump_stack_minidump(regs->sp); raw_spin_unlock(&stop_lock); } diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index e5733cbd0c84..7a343a580476 100755 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -46,6 +46,10 @@ #include #include +#include +#include +#include + static const char *handler[]= { "Synchronous Abort", "IRQ", @@ -256,6 +260,17 @@ static int __die(const char *str, int err, struct pt_regs *regs) end_of_stack(tsk)); if (!user_mode(regs)) { +#ifdef CONFIG_SEC_DEBUG + if (THREAD_SIZE + (unsigned long)task_stack_page(tsk) - regs->sp + > THREAD_SIZE) { + dump_mem(KERN_EMERG, "Stack: ", regs->sp, + THREAD_SIZE / 4 + regs->sp); + } else { + dump_mem(KERN_EMERG, "Stack: ", regs->sp, THREAD_SIZE + + (unsigned long)task_stack_page(tsk)); + } +#endif + dump_backtrace(regs, tsk); dump_instr(KERN_EMERG, regs); } @@ -273,6 +288,7 @@ static unsigned long oops_begin(void) unsigned long flags; oops_enter(); + secdbg_sched_msg("!!die!!"); /* racy, but better than risking deadlock. */ raw_local_irq_save(flags); @@ -327,6 +343,8 @@ void die(const char *str, struct pt_regs *regs, int err) if (bug_type != BUG_TRAP_TYPE_NONE && !strlen(str)) str = "Oops - BUG"; + sec_debug_save_die_info(str, regs); + ret = __die(str, err, regs); oops_end(flags, regs, ret); @@ -801,6 +819,9 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr) { console_verbose(); + sec_debug_save_badmode_info(reason, handler[reason], + esr, esr_get_class_string(esr)); + pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n", handler[reason], smp_processor_id(), esr, esr_get_class_string(esr)); diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index dd084f3eda32..d8aff1668564 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -43,6 +43,8 @@ #include #include +#include + struct fault_info { int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); @@ -102,13 +104,19 @@ void show_pte(unsigned long addr) } else { pr_alert("[%016lx] address between user and kernel address ranges\n", addr); + sec_debug_store_pte((unsigned long)addr, 1); return; } pr_alert("pgd = %p\n", mm->pgd); + sec_debug_store_pte((unsigned long)mm->pgd, 0); + pgd = pgd_offset(mm, addr); pr_alert("[%016lx] *pgd=%016llx", addr, pgd_val(*pgd)); + sec_debug_store_pte((unsigned long)addr, 1); + sec_debug_store_pte((unsigned long)pgd_val(*pgd), 2); + do { pud_t *pud; pmd_t *pmd; @@ -119,16 +127,20 @@ void show_pte(unsigned long addr) pud = pud_offset(pgd, addr); pr_cont(", *pud=%016llx", pud_val(*pud)); + sec_debug_store_pte((unsigned long)pud_val(*pud), 3); + if (pud_none(*pud) || pud_bad(*pud)) break; pmd = pmd_offset(pud, addr); pr_cont(", *pmd=%016llx", pmd_val(*pmd)); + sec_debug_store_pte((unsigned long)pmd_val(*pmd), 4); if (pmd_none(*pmd) || pmd_bad(*pmd)) break; pte = pte_offset_map(pmd, addr); pr_cont(", *pte=%016llx", pte_val(*pte)); + sec_debug_store_pte((unsigned long)pte_val(*pte), 5); pte_unmap(pte); } while(0); @@ -203,6 +215,7 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, if (!is_el1_instruction_abort(esr) && fixup_exception(regs)) return; + sec_debug_store_extc_idx(false); /* * No handler, we'll have to terminate things with extreme prejudice. */ @@ -639,6 +652,8 @@ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, const struct fault_info *inf = esr_to_fault_info(esr); struct siginfo info; + sec_debug_save_fault_info(esr, inf->name, addr, 0UL); + if (!inf->fn(addr, esr, regs)) return; @@ -696,6 +711,9 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr, esr_get_class_string(esr), (void *)regs->pc, (void *)regs->sp); + sec_debug_save_fault_info(esr, esr_get_class_string(esr), + (unsigned long)regs->pc, (unsigned long)regs->sp); + info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRALN; @@ -743,6 +761,8 @@ asmlinkage int __exception do_debug_exception(unsigned long addr_if_watchpoint, struct siginfo info; int rv; + sec_debug_save_fault_info(esr, inf->name, addr_if_watchpoint, 0UL); + /* * Tell lockdep we disabled irqs in entry.S. Do nothing if they were * already disabled to preserve the last enabled/disabled addresses. diff --git a/block/blk-core.c b/block/blk-core.c index 0d5edebde8eb..da877530b1f0 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -379,7 +379,7 @@ EXPORT_SYMBOL(blk_put_queue); * If not, only ELVPRIV requests are drained. The caller is responsible * for ensuring that no new requests which need to be drained are queued. */ -static void __blk_drain_queue(struct request_queue *q, bool drain_all) +void __blk_drain_queue(struct request_queue *q, bool drain_all) __releases(q->queue_lock) __acquires(q->queue_lock) { diff --git a/drivers/Kconfig b/drivers/Kconfig index 59608162435d..b56756a9ed27 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -24,6 +24,14 @@ source "drivers/nvme/Kconfig" source "drivers/misc/Kconfig" +source "drivers/staging/samsung/sec_notifier/Kconfig" + +source "drivers/muic/Kconfig" + +source "drivers/muic/universal/Kconfig" + +source "drivers/ccic/Kconfig" + source "drivers/ide/Kconfig" source "drivers/scsi/Kconfig" @@ -106,6 +114,8 @@ source "drivers/memstick/Kconfig" source "drivers/leds/Kconfig" +source "drivers/switch/Kconfig" + source "drivers/accessibility/Kconfig" source "drivers/infiniband/Kconfig" @@ -212,4 +222,20 @@ source "drivers/sensors/Kconfig" source "drivers/tee/Kconfig" +source "drivers/adsp_factory/Kconfig" + +source "drivers/fingerprint/Kconfig" + +source "drivers/redriver/Kconfig" + +source "drivers/debug/Kconfig" + +source "drivers/gud/Kconfig" + +source "drivers/battery_v2/Kconfig" + +source "drivers/motor/Kconfig" + +source "drivers/security/samsung/tzic/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 557cba5af5c9..49bdeed18864 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -130,6 +130,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ +obj-$(CONFIG_SWITCH) += switch/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ @@ -161,6 +162,9 @@ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_PM_DEVFREQ) += devfreq/ obj-$(CONFIG_EXTCON) += extcon/ +obj-$(CONFIG_VBUS_NOTIFIER) += staging/samsung/sec_notifier/ +obj-$(CONFIG_USE_MUIC) += muic/ +obj-y += ccic/ obj-$(CONFIG_MEMORY) += memory/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ @@ -181,3 +185,20 @@ obj-$(CONFIG_ESOC) += esoc/ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_SENSORS_SSC) += sensors/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ADSP_FACTORY) += adsp_factory/ +obj-y += debug/ +obj-$(CONFIG_BATTERY_SAMSUNG) += battery_v2/ +# COMBO REDRIVER +obj-$(CONFIG_COMBO_REDRIVER) += redriver/ + +# FINGERPRINT +obj-$(CONFIG_SENSORS_FINGERPRINT) += fingerprint/ + +# mobicore +obj-$(CONFIG_TRUSTONIC_TEE) += gud/ + +# Motor +obj-y += motor/ + +#TZIC +obj-y += security/samsung/tzic/ diff --git a/drivers/adsp_factory/Kconfig b/drivers/adsp_factory/Kconfig new file mode 100644 index 000000000000..5d036aaf2160 --- /dev/null +++ b/drivers/adsp_factory/Kconfig @@ -0,0 +1,81 @@ +# +# factory sensor drivers configuration +# +config ADSP_FACTORY + tristate "MSM ADSP factory driver" + default n + help + This driver communicate with SSC DAEMON. + register each sensor device. + send selftest request using netlink. + receive test result using netlink. + +config LSM6DSM_FACTORY + tristate "factory test for SSC - LSM6DSM" + default n + help + lsm6dsM factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config ICM42605_FACTORY + tristate "factory test for SSC - ICM42605" + default n + help + icm42605 factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config AK09918_FACTORY + tristate "factory test for SSC - ak09918" + default n + help + ak09918 factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config LPS22HH_FACTORY + tristate "factory test for SSC - lps22hh" + default n + help + lps22hh factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config VCNL36863_FACTORY + tristate "factory test for SSC - vcnl36863" + default n + help + vcnl36863 factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config VEML3328_FACTORY + tristate "factory test for SSC - veml3328" + default n + help + veml3328 factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config VCNL36658_FACTORY + tristate "factory test for SSC - vcnl36658" + default n + help + vcnl36658 factory driver. + provide sysfs for factory test. + request selftest to adsp_factory. + receive test result from adsp_factory. + +config SUPPORT_PROX_AUTO_CAL + tristate "Support auto cal function for proximity sensor" + default n + depends on ADSP_FACTORY + help + Support the auto cal function. diff --git a/drivers/adsp_factory/Makefile b/drivers/adsp_factory/Makefile new file mode 100644 index 000000000000..993c53767c39 --- /dev/null +++ b/drivers/adsp_factory/Makefile @@ -0,0 +1,9 @@ + +obj-$(CONFIG_ADSP_FACTORY) += adsp_factory.o ssc_core.o +obj-$(CONFIG_LSM6DSM_FACTORY) += lsm6dsl_accel.o lsm6dsl_gyro.o +obj-$(CONFIG_ICM42605_FACTORY) += icm42605_accel.o icm42605_gyro.o +obj-$(CONFIG_AK09918_FACTORY) += ak09918_mag.o +obj-$(CONFIG_VCNL36863_FACTORY) += vcnl36863_light.o vcnl36863_prox.o +obj-$(CONFIG_VCNL36658_FACTORY) += vcnl36658_light.o vcnl36658_prox.o +obj-$(CONFIG_VEML3328_FACTORY) += veml3328_light.o +obj-$(CONFIG_LPS22HH_FACTORY) += lps22hh_pressure.o \ No newline at end of file diff --git a/drivers/adsp_factory/adsp.h b/drivers/adsp_factory/adsp.h new file mode 100644 index 000000000000..9a5af57a6625 --- /dev/null +++ b/drivers/adsp_factory/adsp.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __ADSP_SENSOR_H__ +#define __ADSP_SENSOR_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#define TIMEOUT_CNT 200 + +/* Main struct containing all the data */ +struct adsp_data { + struct device *adsp; + struct device *sensor_device[MSG_SENSOR_MAX]; + struct device_attribute **sensor_attr[MSG_SENSOR_MAX]; + struct device *mobeam_device; + struct sock *adsp_skt; + int32_t *msg_buf[MSG_SENSOR_MAX]; + unsigned int ready_flag[MSG_TYPE_MAX]; + bool sysfs_created[MSG_SENSOR_MAX]; + struct mutex prox_factory_mutex; + struct mutex light_factory_mutex; + struct mutex accel_factory_mutex; + struct mutex remove_sysfs_mutex; +}; + +#ifdef CONFIG_SUPPORT_MOBEAM +void adsp_mobeam_register(struct device_attribute *attributes[]); +void adsp_mobeam_unregister(struct device_attribute *attributes[]); +#endif +#ifdef CONFIG_SEC_FACTORY +int get_mag_raw_data(int32_t *raw_data); +#endif +int get_accel_raw_data(int32_t *raw_data); +int get_prox_raw_data(int *raw_data, int *offset); +int adsp_get_sensor_data(int sensor_type); +int adsp_factory_register(unsigned int type, + struct device_attribute *attributes[]); +int adsp_factory_unregister(unsigned int type); +int adsp_unicast(void *param, int param_size, u16 sensor_type, + u32 portid, u16 msg_type); +int sensors_register(struct device **dev, void *drvdata, + struct device_attribute *attributes[], char *name); +void sensors_unregister(struct device *dev, + struct device_attribute *attributes[]); +void hidden_hole_init_work(void); +void accel_factory_init_work(void); +#endif diff --git a/drivers/adsp_factory/adsp_factory.c b/drivers/adsp_factory/adsp_factory.c new file mode 100644 index 000000000000..cea0d8b462f7 --- /dev/null +++ b/drivers/adsp_factory/adsp_factory.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp.h" + +static u8 msg_size[MSG_SENSOR_MAX] = { + MSG_ACCEL_MAX, + MSG_GYRO_MAX, + MSG_MAG_MAX, + MSG_PRESSURE_MAX, + MSG_LIGHT_MAX, + MSG_PROX_MAX, + MSG_TYPE_SIZE_ZERO, //hole + MSG_MOBEAM_MAX, + MSG_TYPE_SIZE_ZERO, //physical + MSG_GYRO_TEMP_MAX, + MSG_PRESSURE_TEMP_MAX, + MSG_TYPE_SIZE_ZERO, + MSG_TYPE_SIZE_ZERO, + MSG_TYPE_SIZE_ZERO, +}; + +/* The netlink socket */ +struct adsp_data *data; + +DEFINE_MUTEX(factory_mutex); + +/* Function used to send message to the user space */ +int adsp_unicast(void *param, int param_size, u16 sensor_type, + u32 portid, u16 msg_type) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + void *msg; + int ret = -1; + u16 nlmsg_type = (sensor_type << 8) | msg_type; + + data->ready_flag[msg_type] &= ~(1 << sensor_type); + skb = nlmsg_new(param_size, GFP_KERNEL); + if (!skb) { + pr_err("[FACTORY] %s - nlmsg_new fail\n", __func__); + return -ENOMEM; + } + + nlh = nlmsg_put(skb, portid, 0, nlmsg_type, param_size, 0); + if (nlh == NULL) { + pr_err("[FACTORY] %s - nlmsg_put fail\n", __func__); + nlmsg_free(skb); + return -EMSGSIZE; + } + msg = nlmsg_data(nlh); + memcpy(msg, param, param_size); + NETLINK_CB(skb).dst_group = 0; + ret = nlmsg_unicast(data->adsp_skt, skb, PID); + if (ret != 0) + pr_err("[FACTORY] %s - ret = %d\n", __func__, ret); + + return ret; +} + +int adsp_factory_register(unsigned int type, + struct device_attribute *attributes[]) +{ + int ret = 0; + char *dev_name; + + switch (type) { + case MSG_ACCEL: + dev_name = "accelerometer_sensor"; + break; + case MSG_GYRO: + dev_name = "gyro_sensor"; + break; + case MSG_MAG: + dev_name = "magnetic_sensor"; + break; + case MSG_PRESSURE: + dev_name = "barometer_sensor"; + break; + case MSG_LIGHT: + dev_name = "light_sensor"; + break; + case MSG_PROX: + dev_name = "proximity_sensor"; + break; + case MSG_SSC_CORE: + dev_name = "ssc_core"; + break; + case MSG_HH_HOLE: + dev_name = "hidden_hole"; + break; + default: + dev_name = "unknown_sensor"; + break; + } + + data->sensor_attr[type] = attributes; + ret = sensors_register(&data->sensor_device[type], data, + data->sensor_attr[type], dev_name); + + data->sysfs_created[type] = true; + pr_info("[FACTORY] %s - type:%u ptr:%pK\n", + __func__, type, data->sensor_device[type]); + + return ret; +} + +int adsp_factory_unregister(unsigned int type) +{ + pr_info("[FACTORY] %s - type:%u ptr:%pK\n", + __func__, type, data->sensor_device[type]); + + if (data->sysfs_created[type]) { + sensors_unregister(data->sensor_device[type], + data->sensor_attr[type]); + data->sysfs_created[type] = false; + } else { + pr_info("[FACTORY] %s: skip type %u\n", __func__, type); + } + return 0; +} + +int get_prox_raw_data(int *raw_data, int *offset) +{ + uint8_t cnt = 0; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(NULL, 0, MSG_PROX, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + mutex_unlock(&data->prox_factory_mutex); + return -1; + } + + *raw_data = data->msg_buf[MSG_PROX][0]; + *offset = data->msg_buf[MSG_PROX][1]; + mutex_unlock(&data->prox_factory_mutex); + + return 0; +} + +int get_accel_raw_data(int32_t *raw_data) +{ + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_ACCEL); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return -1; + } + + memcpy(raw_data, &data->msg_buf[MSG_ACCEL][0], sizeof(int32_t) * 3); + + return 0; +} + +#ifdef CONFIG_SEC_FACTORY +int get_mag_raw_data(int32_t *raw_data) +{ + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_MAG, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_MAG) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_MAG); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return -1; + } + + memcpy(raw_data, &data->msg_buf[MSG_MAG][0], sizeof(int32_t) * 3); + + return 0; +} +#endif + +#ifdef CONFIG_SUPPORT_MOBEAM +void adsp_mobeam_register(struct device_attribute *attributes[]) +{ + int i; + + data->mobeam_device = sec_device_create(0, data, "sec_barcode_emul"); + + for (i = 0; attributes[i] != NULL; i++) { + if (device_create_file(data->mobeam_device, attributes[i]) < 0) + pr_err("%s fail to create %d", __func__, i); + } +} + +void adsp_mobeam_unregister(struct device_attribute *attributes[]) +{ + int i; + + for (i = 0; attributes[i] != NULL; i++) + device_remove_file(data->mobeam_device, attributes[i]); +} +#endif + +static int process_received_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + u16 sensor_type = nlh->nlmsg_type >> 8; + u16 msg_type = nlh->nlmsg_type & 0xff; + + /* check the boundary to prevent memory attack */ + if (msg_type >= MSG_TYPE_MAX || sensor_type >= MSG_SENSOR_MAX || + nlh->nlmsg_len - (int32_t)sizeof(struct nlmsghdr) > + sizeof(int32_t) * msg_size[sensor_type]) { + pr_err("[FACTORY] %d, %d, %d\n", msg_type, sensor_type, nlh->nlmsg_len); + return 0; + } + + if (sensor_type == MSG_FACTORY_INIT_CMD) { + accel_factory_init_work(); +#if defined(CONFIG_SUPPORT_HIDDEN_HOLE) + hidden_hole_init_work(); +#endif + return 0; + } + + memcpy(data->msg_buf[sensor_type], + (int32_t *)NLMSG_DATA(nlh), + nlh->nlmsg_len - (int32_t)sizeof(struct nlmsghdr)); + data->ready_flag[msg_type] |= 1 << sensor_type; + + return 0; +} + +static void factory_receive_skb(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + int len; + int err; + + nlh = (struct nlmsghdr *)skb->data; + len = skb->len; + while (NLMSG_OK(nlh, len)) { + err = process_received_msg(skb, nlh); + /* if err or if this message says it wants a response */ + if (err || (nlh->nlmsg_flags & NLM_F_ACK)) + netlink_ack(skb, nlh, err); + nlh = NLMSG_NEXT(nlh, len); + } +} + +/* Receive messages from netlink socket. */ +static void factory_test_result_receive(struct sk_buff *skb) +{ + mutex_lock(&factory_mutex); + factory_receive_skb(skb); + mutex_unlock(&factory_mutex); +} + +struct netlink_kernel_cfg netlink_cfg = { + .input = factory_test_result_receive, +}; + +static int __init factory_adsp_init(void) +{ + int i; + + pr_info("[FACTORY] %s\n", __func__); + data = kzalloc(sizeof(*data), GFP_KERNEL); + + for (i = 0; i < MSG_SENSOR_MAX; i++) { + if (msg_size[i] > 0) + data->msg_buf[i] = kzalloc(sizeof(int32_t) * msg_size[i], + GFP_KERNEL); + } + + data->adsp_skt = netlink_kernel_create(&init_net, + NETLINK_ADSP_FAC, &netlink_cfg); + + for (i = 0; i < MSG_SENSOR_MAX; i++) + data->sysfs_created[i] = false; + for (i = 0; i < MSG_TYPE_MAX; i++) + data->ready_flag[i] = 0; + + mutex_init(&data->accel_factory_mutex); + mutex_init(&data->prox_factory_mutex); + mutex_init(&data->light_factory_mutex); + mutex_init(&data->remove_sysfs_mutex); + + pr_info("[FACTORY] %s: Timer Init\n", __func__); + return 0; +} + +static void __exit factory_adsp_exit(void) +{ + int i; + + mutex_destroy(&data->accel_factory_mutex); + mutex_destroy(&data->prox_factory_mutex); + mutex_destroy(&data->light_factory_mutex); + mutex_destroy(&data->remove_sysfs_mutex); + + for (i = 0; i < MSG_SENSOR_MAX; i++) + kfree(data->msg_buf[i]); + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(factory_adsp_init); +module_exit(factory_adsp_exit); +MODULE_DESCRIPTION("Support for factory test sensors (adsp)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/adsp_factory/ak09918_mag.c b/drivers/adsp_factory/ak09918_mag.c new file mode 100644 index 000000000000..a80aa1976b87 --- /dev/null +++ b/drivers/adsp_factory/ak09918_mag.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#define VENDOR "AKM" +#define CHIP_ID "AK09918" + +#define MAG_ST_TRY_CNT 3 + +static ssize_t mag_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t mag_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t mag_check_cntl(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "OK\n"); +} + +static ssize_t mag_check_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} + +static ssize_t mag_get_asa(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* Do not have Fuserom */ + return snprintf(buf, PAGE_SIZE, "%u,%u,%u\n", 128, 128, 128); +} + +static ssize_t mag_get_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* Do not have Fuserom */ + return snprintf(buf, PAGE_SIZE, "%s\n", "OK"); +} + +static ssize_t mag_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_MAG, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_MAG) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_MAG); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, "0,0,0\n"); + } + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + data->msg_buf[MSG_MAG][0], + data->msg_buf[MSG_MAG][1], + data->msg_buf[MSG_MAG][2]); +} + +static ssize_t mag_raw_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_ENABLE); + msleep(20); + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_SET_CAL_DATA); + msleep(20); + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_DISABLE); + + return size; +} + +static ssize_t mag_selttest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int retry = 0, i; + +RETRY_MAG_SELFTEST: + pr_info("[FACTORY] %s - start", __func__); + adsp_unicast(NULL, 0, MSG_MAG, 0, MSG_TYPE_ST_SHOW_DATA); + + while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_MAG) && + cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_MAG); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + data->msg_buf[MSG_MAG][0] = -1; + } + + if (!(data->msg_buf[MSG_MAG][0] == 0)) { + if (retry < MAG_ST_TRY_CNT) { + retry++; + for (i = 0; i < 10; i++) + data->msg_buf[MSG_MAG][i] = 0; + + msleep(100); + pr_info("[FACTORY] %s - retry %d", __func__, retry); + goto RETRY_MAG_SELFTEST; + } + + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_ENABLE); + msleep(20); + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_SET_CAL_DATA); + msleep(20); + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_DISABLE); + return snprintf(buf, PAGE_SIZE, "-1,0,0,0,0,0,0,0,0,0\n"); + } + + pr_info("[FACTORY] status=%d, sf_status=%d, sf_x=%d, sf_y=%d, sf_z=%d\n dac=%d, adc=%d, adc_x=%d, adc_y=%d, adc_z=%d\n", + data->msg_buf[MSG_MAG][0], data->msg_buf[MSG_MAG][1], + data->msg_buf[MSG_MAG][2], data->msg_buf[MSG_MAG][3], + data->msg_buf[MSG_MAG][4], data->msg_buf[MSG_MAG][5], + data->msg_buf[MSG_MAG][6], data->msg_buf[MSG_MAG][7], + data->msg_buf[MSG_MAG][8], data->msg_buf[MSG_MAG][9]); + + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_ENABLE); + msleep(20); + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_SET_CAL_DATA); + msleep(20); + adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_DISABLE); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_MAG][0], data->msg_buf[MSG_MAG][1], + data->msg_buf[MSG_MAG][2], data->msg_buf[MSG_MAG][3], + data->msg_buf[MSG_MAG][4], data->msg_buf[MSG_MAG][5], + data->msg_buf[MSG_MAG][6], data->msg_buf[MSG_MAG][7], + data->msg_buf[MSG_MAG][8], data->msg_buf[MSG_MAG][9]); +} + +static ssize_t mag_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if 0 + struct adsp_data *data = dev_get_drvdata(dev); + struct msg_data message; + uint8_t cnt = 0; + + message.msg_type = MSG_MAG; + data->calib_ready_flag &= ~(1 << MSG_MAG); + adsp_unicast(&message, sizeof(message), + MSG_TYPE_GET_CALIB_DATA, 0, 0); + + while (!(data->calib_ready_flag & 1 << MSG_MAG) && + cnt++ < TIMEOUT_CNT) + msleep(20); + + data->calib_ready_flag &= ~(1 << MSG_MAG); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pr_info("[FACTORY] %s\n", __func__); + pr_info("[FACTORY] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + data->sensor_calib_data[MSG_MAG].si_mat[0], + data->sensor_calib_data[MSG_MAG].si_mat[1], + data->sensor_calib_data[MSG_MAG].si_mat[2], + data->sensor_calib_data[MSG_MAG].si_mat[3], + data->sensor_calib_data[MSG_MAG].si_mat[4], + data->sensor_calib_data[MSG_MAG].si_mat[5], + data->sensor_calib_data[MSG_MAG].si_mat[6], + data->sensor_calib_data[MSG_MAG].si_mat[7], + data->sensor_calib_data[MSG_MAG].si_mat[8]); + + return snprintf(buf, PAGE_SIZE, + "\"SI_PARAMETER\":\"0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\"\n", + data->sensor_calib_data[MSG_MAG].si_mat[0], + data->sensor_calib_data[MSG_MAG].si_mat[1], + data->sensor_calib_data[MSG_MAG].si_mat[2], + data->sensor_calib_data[MSG_MAG].si_mat[3], + data->sensor_calib_data[MSG_MAG].si_mat[4], + data->sensor_calib_data[MSG_MAG].si_mat[5], + data->sensor_calib_data[MSG_MAG].si_mat[6], + data->sensor_calib_data[MSG_MAG].si_mat[7], + data->sensor_calib_data[MSG_MAG].si_mat[8]); +#endif + return 0; +} + +static DEVICE_ATTR(name, 0444, mag_name_show, NULL); +static DEVICE_ATTR(vendor, 0444, mag_vendor_show, NULL); +static DEVICE_ATTR(raw_data, 0664, mag_raw_data_show, mag_raw_data_store); +static DEVICE_ATTR(adc, 0444, mag_raw_data_show, NULL); +static DEVICE_ATTR(dac, 0444, mag_check_cntl, NULL); +static DEVICE_ATTR(chk_registers, 0444, mag_check_registers, NULL); +static DEVICE_ATTR(selftest, 0440, mag_selttest_show, NULL); +static DEVICE_ATTR(asa, 0444, mag_get_asa, NULL); +static DEVICE_ATTR(status, 0444, mag_get_status, NULL); +static DEVICE_ATTR(dhr_sensor_info, 0440, mag_dhr_sensor_info_show, NULL); + +static struct device_attribute *mag_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_raw_data, + &dev_attr_adc, + &dev_attr_dac, + &dev_attr_chk_registers, + &dev_attr_selftest, + &dev_attr_asa, + &dev_attr_status, + &dev_attr_dhr_sensor_info, + NULL, +}; + +static int __init ak09918_factory_init(void) +{ + adsp_factory_register(MSG_MAG, mag_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit ak09918_factory_exit(void) +{ + adsp_factory_unregister(MSG_MAG); + + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(ak09918_factory_init); +module_exit(ak09918_factory_exit); diff --git a/drivers/adsp_factory/icm42605_accel.c b/drivers/adsp_factory/icm42605_accel.c new file mode 100644 index 000000000000..fd82b612c1cc --- /dev/null +++ b/drivers/adsp_factory/icm42605_accel.c @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#ifdef CONFIG_SLPI_MOTOR +#include +#endif +#define VENDOR "TDK" +#define CHIP_ID "ICM42605" +#define ACCEL_ST_TRY_CNT 3 +#define ACCEL_FACTORY_CAL_CNT 20 +#define ACCEL_RAW_DATA_CNT 3 +#define MAX_ACCEL_1G 4096 + +/* Haptic Pattern A vibrate during 7ms. + * touch, touchkey, operation feedback use this. + * Do not call motor_workfunc when duration is 7ms. + */ +#define DURATION_SKIP 10 +#define MOTOR_OFF 0 + +#define ACCEL_FACTORY_CAL_PATH "/efs/FactoryApp/accel_factory_cal" + +#ifdef CONFIG_SLPI_MOTOR +struct accel_motor_data { + struct workqueue_struct *slpi_motor_wq; + struct work_struct work_slpi_motor; + int motor_state; +}; + +struct accel_motor_data *pdata_motor; +#endif + +struct accel_data { + struct work_struct work_accel; + struct workqueue_struct *accel_wq; + struct adsp_data *dev_data; + bool is_complete_cal; + int32_t raw_data[ACCEL_RAW_DATA_CNT]; + int32_t avg_data[ACCEL_RAW_DATA_CNT]; +}; + +static struct accel_data *pdata; + +static ssize_t accel_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t accel_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t sensor_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", "ADSP"); +} + +int get_accel_cal_data(int32_t *cal_data) +{ + struct file *factory_cal_filp = NULL; + mm_segment_t old_fs; + int ret = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, + O_RDONLY, 0440); + + if (IS_ERR(factory_cal_filp)) { + set_fs(old_fs); + ret = PTR_ERR(factory_cal_filp); + pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n", + __func__, ret); + return ret; + } + + ret = vfs_read(factory_cal_filp, (char *)cal_data, + ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos); + if (ret < 0) { + pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret); + filp_close(factory_cal_filp, current->files); + set_fs(old_fs); + return ret; + } + + filp_close(factory_cal_filp, current->files); + set_fs(old_fs); + + return ret; +} + +int set_accel_cal_data(int32_t *cal_data, bool first_booting) +{ + struct file *factory_cal_filp = NULL; + mm_segment_t old_fs; + int flag, ret = 0; + umode_t mode = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + if (first_booting) { + flag = O_TRUNC | O_RDWR | O_CREAT; + mode = 0600; + } else { + flag = O_RDWR; + mode = 0660; + } + + factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, flag, mode); + + if (IS_ERR(factory_cal_filp)) { + set_fs(old_fs); + ret = PTR_ERR(factory_cal_filp); + pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n", + __func__, ret); + return ret; + } + + ret = vfs_write(factory_cal_filp, (char *)cal_data, + ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos); + if (ret < 0) + pr_err("[FACTORY] %s: fd write %d\n", __func__, ret); + + filp_close(factory_cal_filp, current->files); + set_fs(old_fs); + + adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + return ret; +} + +static ssize_t accel_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int32_t cal_data[ACCEL_RAW_DATA_CNT] = {0, }; + + if (get_accel_cal_data(cal_data) > 0) { + pr_info("[FACTORY] %s: %d, %d, %d\n", __func__, + cal_data[0], cal_data[1], cal_data[2]); + if (cal_data[0] == 0 && cal_data[1] == 0 && cal_data[2] == 0) + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", + 0, 0, 0, 0); + else + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", + true, cal_data[0], cal_data[1], cal_data[2]); + } else { + pr_err("[FACTORY] %s: get_accel_cal_data fail\n", __func__); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", 0, 0, 0, 0); + } +} + +static ssize_t accel_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + + pdata->dev_data = data; + if (sysfs_streq(buf, "0")) { + mutex_lock(&data->accel_factory_mutex); + memset(pdata->avg_data, 0, sizeof(pdata->avg_data)); + set_accel_cal_data(pdata->avg_data, false); + mutex_unlock(&data->accel_factory_mutex); + } else { + pdata->is_complete_cal = false; + queue_work(pdata->accel_wq, &pdata->work_accel); + while (pdata->is_complete_cal == false) { + pr_info("[FACTORY] %s: In factory cal\n", __func__); + msleep(20); + } + mutex_lock(&data->accel_factory_mutex); + set_accel_cal_data(pdata->avg_data, false); + mutex_unlock(&data->accel_factory_mutex); + } + + return size; +} + +static void accel_work_func(struct work_struct *work) +{ + struct accel_data *data = container_of((struct work_struct *)work, + struct accel_data, work_accel); + int i; + + mutex_lock(&data->dev_data->accel_factory_mutex); + memset(pdata->avg_data, 0, sizeof(pdata->avg_data)); + adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + msleep(30); /* for init of bias */ + for (i = 0; i < ACCEL_FACTORY_CAL_CNT; i++) { + msleep(20); + get_accel_raw_data(pdata->raw_data); + pdata->avg_data[0] += pdata->raw_data[0]; + pdata->avg_data[1] += pdata->raw_data[1]; + pdata->avg_data[2] += pdata->raw_data[2]; + pr_info("[FACTORY] %s: %d, %d, %d\n", __func__, + pdata->raw_data[0], pdata->raw_data[1], pdata->raw_data[2]); + } + + for (i = 0; i < ACCEL_RAW_DATA_CNT; i++) { + pdata->avg_data[i] /= ACCEL_FACTORY_CAL_CNT; + pr_err("[FACTORY] %s: avg : %d\n", __func__, pdata->avg_data[i]); + } + + if (pdata->avg_data[2] > 0) + pdata->avg_data[2] -= MAX_ACCEL_1G; + else if (pdata->avg_data[2] < 0) + pdata->avg_data[2] += MAX_ACCEL_1G; + + mutex_unlock(&data->dev_data->accel_factory_mutex); + pdata->is_complete_cal = true; + + return; +} + +static ssize_t accel_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_ST_SHOW_DATA); + + while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_ACCEL); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + data->msg_buf[MSG_ACCEL][1] = -1; + } + + pr_info("[FACTORY] %s : init = %d, result = %d, XYZ = %d, %d, %d, nXYZ = %d, %d, %d\n", + __func__, data->msg_buf[MSG_ACCEL][0], + data->msg_buf[MSG_ACCEL][7], data->msg_buf[MSG_ACCEL][1], + data->msg_buf[MSG_ACCEL][2], data->msg_buf[MSG_ACCEL][3], + data->msg_buf[MSG_ACCEL][4], data->msg_buf[MSG_ACCEL][5], + data->msg_buf[MSG_ACCEL][6]); + + if (data->msg_buf[MSG_ACCEL][7] == 0) { + pr_info("[FACTORY] %s : Pass - result = %d\n", + __func__, data->msg_buf[MSG_ACCEL][7]); + } else { + pr_err("[FACTORY] %s : Fail - result = %d\n", + __func__, data->msg_buf[MSG_ACCEL][7]); + } + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_ACCEL][7], + (int)abs(data->msg_buf[MSG_ACCEL][1]), + (int)abs(data->msg_buf[MSG_ACCEL][2]), + (int)abs(data->msg_buf[MSG_ACCEL][3]), + (int)abs(data->msg_buf[MSG_ACCEL][4]), + (int)abs(data->msg_buf[MSG_ACCEL][5]), + (int)abs(data->msg_buf[MSG_ACCEL][6])); +} + +static ssize_t accel_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, }; + + mutex_lock(&data->accel_factory_mutex); + get_accel_raw_data(raw_data); + mutex_unlock(&data->accel_factory_mutex); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + raw_data[0], raw_data[1], raw_data[2]); +} + +static ssize_t accel_reactive_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + bool success = false; + int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, }; + + mutex_lock(&data->accel_factory_mutex); + get_accel_raw_data(raw_data); + mutex_unlock(&data->accel_factory_mutex); + + if (raw_data[0] != 0 || raw_data[1] != 0 || raw_data[2] != 0) + success = true; + + return snprintf(buf, PAGE_SIZE, "%d\n", success); +} + +static ssize_t accel_reactive_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + if (sysfs_streq(buf, "1")) + pr_info("[FACTORY]: %s - on\n", __func__); + else if (sysfs_streq(buf, "0")) + pr_info("[FACTORY]: %s - off\n", __func__); + else if (sysfs_streq(buf, "2")) + pr_info("[FACTORY]: %s - factory\n", __func__); + + return size; +} + +static ssize_t accel_lowpassfilter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int32_t msg_buf; + + if (sysfs_streq(buf, "1")) { + msg_buf = 1; + } else if (sysfs_streq(buf, "0")) { + msg_buf = 0; + } else { + pr_info("[FACTORY] %s: wrong value\n", __func__); + return size; + } + + mutex_lock(&data->accel_factory_mutex); + adsp_unicast(&msg_buf, sizeof(int32_t), MSG_ACCEL, 0, MSG_TYPE_SET_ACCEL_LPF); + + while (!(data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] &= ~(1 << MSG_ACCEL); + mutex_unlock(&data->accel_factory_mutex); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return size; + } + + pr_info("[FACTORY] %s: lpf_on_off done (%d)(0x%x)\n", __func__, + data->msg_buf[MSG_ACCEL][0], data->msg_buf[MSG_ACCEL][1]); + + return size; +} + +#ifdef CONFIG_SLPI_MOTOR +int setSensorCallback(int state, int duration) +{ + if (duration > MOTOR_OFF && duration <= DURATION_SKIP) + return 0; + + if (pdata_motor->motor_state != state) { + pr_info("[FACTORY] %s: state = %d, duration = %d\n", + __func__, pdata_motor->motor_state, duration); + pdata_motor->motor_state = state; + queue_work(pdata_motor->slpi_motor_wq, &pdata_motor->work_slpi_motor); + } + + return 0; +} + +void slpi_motor_work_func(struct work_struct *work) +{ +#if 0 + struct msg_data message; + int motor = 0; + + if (pdata_motor->motor_state == 1) { + motor = MSG_TYPE_ACCEL_MOTOR_ON; + message.msg_type = MSG_ACCEL_MOT_ON; + } else if (pdata_motor->motor_state == 0) { + motor = MSG_TYPE_ACCEL_MOTOR_OFF; + message.msg_type = MSG_ACCEL_MOT_OFF; + } + + pr_info("[FACTORY] %s: state = %d\n", __func__, pdata_motor->motor_state); + + adsp_unicast(&message, sizeof(message), motor, 0, 0); +#endif +} +#endif + +static ssize_t accel_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + char ctrl1_xl = 0; + uint8_t fullscale = 0; + int32_t *info = data->msg_buf[MSG_ACCEL]; + + adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_ACCEL); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + ctrl1_xl = *info; + + ctrl1_xl &= 0xC; + + switch (ctrl1_xl) { + case 0xC: + fullscale = 8; + break; + case 0x8: + fullscale = 4; + break; + case 0x4: + fullscale = 16; + break; + case 0: + fullscale = 2; + break; + default: + break; + } + + pr_info("[FACTORY] %s: f/s %u\n", __func__, fullscale); + + return snprintf(buf, PAGE_SIZE, "\"FULL_SCALE\":\"%uG\"\n", fullscale); +} + +static DEVICE_ATTR(name, 0444, accel_name_show, NULL); +static DEVICE_ATTR(vendor, 0444, accel_vendor_show, NULL); +static DEVICE_ATTR(type, 0444, sensor_type_show, NULL); +static DEVICE_ATTR(calibration, 0664, + accel_calibration_show, accel_calibration_store); +static DEVICE_ATTR(selftest, 0440, + accel_selftest_show, NULL); +static DEVICE_ATTR(raw_data, 0444, accel_raw_data_show, NULL); +static DEVICE_ATTR(reactive_alert, 0664, + accel_reactive_show, accel_reactive_store); +static DEVICE_ATTR(lowpassfilter, 0220, + NULL, accel_lowpassfilter_store); +#ifdef CONFIG_SEC_FACTORY +static DEVICE_ATTR(dhr_sensor_info, 0444, + accel_dhr_sensor_info_show, NULL); +#else +static DEVICE_ATTR(dhr_sensor_info, 0440, + accel_dhr_sensor_info_show, NULL); +#endif + +static struct device_attribute *acc_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_type, + &dev_attr_calibration, + &dev_attr_selftest, + &dev_attr_raw_data, + &dev_attr_reactive_alert, + &dev_attr_lowpassfilter, + &dev_attr_dhr_sensor_info, + NULL, +}; + +void accel_factory_init_work(void) +{ + struct file *cal_filp = NULL; + mm_segment_t old_fs; + int32_t zero_data[ACCEL_RAW_DATA_CNT] = {0, }; + int ret = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, O_RDONLY, 0440); + if (PTR_ERR(cal_filp) == -ENOENT || PTR_ERR(cal_filp) == -ENXIO) { + pr_info("[FACTORY] %s : no accel_factory_cal file\n", __func__); + set_fs(old_fs); + set_accel_cal_data(zero_data, true); + } else if (IS_ERR(cal_filp)) { + pr_err("[FACTORY]: %s - filp_open error\n", __func__); + set_fs(old_fs); + } else { + pr_info("[FACTORY] %s : already exist\n", __func__); + ret = vfs_read(cal_filp, (char *)zero_data, + ACCEL_RAW_DATA_CNT * sizeof(int32_t), &cal_filp->f_pos); + if (ret < 0) { + pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret); + zero_data[0] = zero_data[1] = zero_data[2] = 0; + adsp_unicast(zero_data, sizeof(zero_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + filp_close(cal_filp, current->files); + set_fs(old_fs); + return; + } + adsp_unicast(zero_data, sizeof(zero_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + filp_close(cal_filp, current->files); + set_fs(old_fs); + } +} + +static int __init lsm6dsl_accel_factory_init(void) +{ + adsp_factory_register(MSG_ACCEL, acc_attrs); +#ifdef CONFIG_SLPI_MOTOR + pdata_motor = kzalloc(sizeof(*pdata_motor), GFP_KERNEL); + + if (pdata_motor == NULL) + return -ENOMEM; + + pdata_motor->slpi_motor_wq = + create_singlethread_workqueue("slpi_motor_wq"); + + if (pdata_motor->slpi_motor_wq == NULL) { + pr_err("[FACTORY]: %s - could not create motor wq\n", __func__); + kfree(pdata_motor); + return -ENOMEM; + } + + INIT_WORK(&pdata_motor->work_slpi_motor, slpi_motor_work_func); + + pdata_motor->motor_state = 0; +#endif + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + pdata->accel_wq = create_singlethread_workqueue("accel_wq"); + INIT_WORK(&pdata->work_accel, accel_work_func); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit lsm6dsl_accel_factory_exit(void) +{ + adsp_factory_unregister(MSG_ACCEL); +#ifdef CONFIG_SLPI_MOTOR + if (pdata_motor != NULL && pdata_motor->slpi_motor_wq != NULL) { + cancel_work_sync(&pdata_motor->work_slpi_motor); + destroy_workqueue(pdata_motor->slpi_motor_wq); + pdata_motor->slpi_motor_wq = NULL; + } +#endif + pr_info("[FACTORY] %s\n", __func__); +} +module_init(lsm6dsl_accel_factory_init); +module_exit(lsm6dsl_accel_factory_exit); diff --git a/drivers/adsp_factory/icm42605_gyro.c b/drivers/adsp_factory/icm42605_gyro.c new file mode 100644 index 000000000000..9743e8c8d5fc --- /dev/null +++ b/drivers/adsp_factory/icm42605_gyro.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#define VENDOR "TDK" +#define CHIP_ID "ICM42605" +#define ST_PASS 1 +#define ST_FAIL 0 +#define ST_MAX_DPS 10 +#define ST_MIN_DPS -10 +#define ST_MIN_RATIO 50 +/* Scale Factor = 32768lsb(MAX) / 250dps(ST_FS) */ +#define ST_SENS_DEF 131 +#define SELFTEST_REVISED 1 + +static ssize_t gyro_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t gyro_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t selftest_revised_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", SELFTEST_REVISED); +} + +static ssize_t gyro_power_off(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_info("[FACTORY]: %s\n", __func__); + + return snprintf(buf, PAGE_SIZE, "%d\n", 1); +} + +static ssize_t gyro_power_on(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_info("[FACTORY]: %s\n", __func__); + + return snprintf(buf, PAGE_SIZE, "%d\n", 1); +} + +static ssize_t gyro_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_GYRO_TEMP, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_GYRO_TEMP) + && cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_GYRO_TEMP); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, "-99\n"); + } + + pr_info("[FACTORY] %s: gyro_temp = %d\n", __func__, + data->msg_buf[MSG_GYRO_TEMP][0]); + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->msg_buf[MSG_GYRO_TEMP][0]); +} + +static ssize_t gyro_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int st_nost_data[3] = {0,}; + int st_data[3] = {0,}; + int st_fifo_data[3] = {0,}; + int st_zro_res = ST_FAIL; + int st_ratio_res = ST_FAIL; + + pr_info("[FACTORY] %s - start", __func__); + adsp_unicast(NULL, 0, MSG_GYRO, 0, MSG_TYPE_ST_SHOW_DATA); + + while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_GYRO) && + cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_GYRO); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, + "0,0,0,0,0,0,0,0,0,0,0,0,%d,%d\n", + ST_FAIL, ST_FAIL); + } + + /* TDK : selftest fifo data check */ + st_fifo_data[0] = data->msg_buf[MSG_GYRO][2] / ST_SENS_DEF; + st_fifo_data[1] = data->msg_buf[MSG_GYRO][3] / ST_SENS_DEF; + st_fifo_data[2] = data->msg_buf[MSG_GYRO][4] / ST_SENS_DEF; + + /* TDK : selftest zro(NOST) check */ + st_nost_data[0] = data->msg_buf[MSG_GYRO][6] / ST_SENS_DEF; + st_nost_data[1] = data->msg_buf[MSG_GYRO][7] / ST_SENS_DEF; + st_nost_data[2] = data->msg_buf[MSG_GYRO][8] / ST_SENS_DEF; + + if((ST_MIN_DPS <= st_nost_data[0]) && (st_nost_data[0] <= ST_MAX_DPS) + && (ST_MIN_DPS <= st_nost_data[1]) && (st_nost_data[1] <= ST_MAX_DPS) + && (ST_MIN_DPS <= st_nost_data[2]) && (st_nost_data[2] <= ST_MAX_DPS)) + st_zro_res = ST_PASS; + + /* TDK : selftest ST data */ + st_data[0] = data->msg_buf[MSG_GYRO][9] / ST_SENS_DEF; + st_data[1] = data->msg_buf[MSG_GYRO][10] / ST_SENS_DEF; + st_data[2] = data->msg_buf[MSG_GYRO][11] / ST_SENS_DEF; + + /* TDK : selftest ratio check */ + if(ST_MIN_RATIO < data->msg_buf[MSG_GYRO][12] + && ST_MIN_RATIO < data->msg_buf[MSG_GYRO][13] + && ST_MIN_RATIO < data->msg_buf[MSG_GYRO][14]) + st_ratio_res = ST_PASS; + + if (data->msg_buf[MSG_GYRO][1] == ST_FAIL) { + pr_info("[FACTORY]: %s - %d,%d,%d\n", __func__, + st_fifo_data[0], st_fifo_data[1], st_fifo_data[2]); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + st_fifo_data[0], st_fifo_data[1], st_fifo_data[2]); + } + + pr_info("[FACTORY]: %s - %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + __func__, + st_fifo_data[0], st_fifo_data[1], st_fifo_data[2], + st_nost_data[0], st_nost_data[1], st_nost_data[2], + st_data[0], st_data[1], st_data[2], + data->msg_buf[MSG_GYRO][12], + data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14], + st_ratio_res, st_zro_res); + + return snprintf(buf, PAGE_SIZE, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + st_fifo_data[0], st_fifo_data[1], st_fifo_data[2], + st_nost_data[0], st_nost_data[1], st_nost_data[2], + st_data[0], st_data[1], st_data[2], + data->msg_buf[MSG_GYRO][12], + data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14], + st_ratio_res, st_zro_res); +} + +static DEVICE_ATTR(name, 0444, gyro_name_show, NULL); +static DEVICE_ATTR(vendor, 0444, gyro_vendor_show, NULL); +static DEVICE_ATTR(selftest, 0440, gyro_selftest_show, NULL); +static DEVICE_ATTR(power_on, 0444, gyro_power_on, NULL); +static DEVICE_ATTR(power_off, 0444, gyro_power_off, NULL); +static DEVICE_ATTR(temperature, 0440, gyro_temp_show, NULL); +static DEVICE_ATTR(selftest_revised, 0440, selftest_revised_show, NULL); + +static struct device_attribute *gyro_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_selftest, + &dev_attr_power_on, + &dev_attr_power_off, + &dev_attr_temperature, + &dev_attr_selftest_revised, + NULL, +}; + +static int __init lsm6dsl_gyro_factory_init(void) +{ + adsp_factory_register(MSG_GYRO, gyro_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit lsm6dsl_gyro_factory_exit(void) +{ + adsp_factory_unregister(MSG_GYRO); + + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(lsm6dsl_gyro_factory_init); +module_exit(lsm6dsl_gyro_factory_exit); diff --git a/drivers/adsp_factory/lps22hh_pressure.c b/drivers/adsp_factory/lps22hh_pressure.c new file mode 100755 index 000000000000..787fb9a9c4a7 --- /dev/null +++ b/drivers/adsp_factory/lps22hh_pressure.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#define VENDOR "STM" +#define CHIP_ID "LPS22HH" + +#define CALIBRATION_FILE_PATH "/efs/FactoryApp/baro_delta" + +#define PR_MAX 8388607 /* 24 bit 2'compl */ +#define PR_MIN -8388608 +#define SNS_SUCCESS 0 +#define ST_PASS 1 +#define ST_FAIL 0 + +static int sea_level_pressure; +static int pressure_cal; + +static ssize_t pressure_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t pressure_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t sea_level_pressure_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sea_level_pressure); +} + +static ssize_t sea_level_pressure_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + if (sscanf(buf, "%10d", &sea_level_pressure) != 1) { + pr_err("[FACTORY] %s: sscanf error\n", __func__); + return size; + } + + sea_level_pressure = sea_level_pressure / 100; + + pr_info("[FACTORY] %s: sea_level_pressure = %d\n", __func__, + sea_level_pressure); + + return size; +} + +/* +int pressure_open_calibration(struct adsp_data *data) +{ + int error = 0; + + return error; +} +*/ + +static ssize_t pressure_cabratioin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int temp = 0, error = 0; + + error = kstrtoint(buf, 10, &temp); + if (error < 0) { + pr_err("[FACTORY] %s : kstrtoint failed.(%d)", __func__, error); + return error; + } + + if (temp < PR_MIN || temp > PR_MAX) + return -EINVAL; + + pressure_cal = temp; + + return size; +} + +static ssize_t pressure_cabratioin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + //struct adsp_data *data = dev_get_drvdata(dev); + + //pressure_open_calibration(data); + + return snprintf(buf, PAGE_SIZE, "%d\n", pressure_cal); +} + +static ssize_t temperature_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_PRESSURE_TEMP, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & + 1 << MSG_PRESSURE_TEMP) && cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_PRESSURE_TEMP); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, "-99\n"); + } + return snprintf(buf, PAGE_SIZE, "%d\n", + data->msg_buf[MSG_PRESSURE_TEMP][0]); +} + +static ssize_t selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_PRESSURE, 0, MSG_TYPE_ST_SHOW_DATA); + + while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & + 1 << MSG_PRESSURE) && cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_PRESSURE); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, "0\n"); + } + + pr_info("[FACTORY] %s : P:%d, T:%d, RES:%d\n", + __func__, data->msg_buf[MSG_PRESSURE][0], + data->msg_buf[MSG_PRESSURE][1], data->msg_buf[MSG_PRESSURE][2]); + + if (SNS_SUCCESS == data->msg_buf[MSG_PRESSURE][2]) + return snprintf(buf, PAGE_SIZE, "%d\n", ST_PASS); + else + return snprintf(buf, PAGE_SIZE, "%d\n", ST_FAIL); +} + +static ssize_t pressure_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_PRESSURE, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_PRESSURE) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_PRESSURE); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + } else { + pr_info("[FACTORY] %s - THS_P_L/H: %02x/%02x, WHOAMI: %02x, CTRL1/2/3: %02x/%02x/%02x, STATUS: %02x, DATA: %02x/%02x/%02x/%02x/%02x\n", + __func__, + data->msg_buf[MSG_PRESSURE][0], data->msg_buf[MSG_PRESSURE][1], + data->msg_buf[MSG_PRESSURE][2], data->msg_buf[MSG_PRESSURE][9], + data->msg_buf[MSG_PRESSURE][3], data->msg_buf[MSG_PRESSURE][4], + data->msg_buf[MSG_PRESSURE][5], data->msg_buf[MSG_PRESSURE][6], + data->msg_buf[MSG_PRESSURE][7], data->msg_buf[MSG_PRESSURE][8], + data->msg_buf[MSG_PRESSURE][10], data->msg_buf[MSG_PRESSURE][11]); + + pr_info("[FACTORY] %s - 4Fh:%02x, 50h:%02x, 51h:%02x, 52h:%02x, 53h:%02x, 54h:%02x, 4Dh:%02x, 4Ah:%02x", + __func__, + data->msg_buf[MSG_PRESSURE][12], data->msg_buf[MSG_PRESSURE][13], + data->msg_buf[MSG_PRESSURE][14], data->msg_buf[MSG_PRESSURE][15], + data->msg_buf[MSG_PRESSURE][16], data->msg_buf[MSG_PRESSURE][17], + data->msg_buf[MSG_PRESSURE][18], data->msg_buf[MSG_PRESSURE][19]); + } + + return snprintf(buf, PAGE_SIZE, "%s\n", "Done"); +} + +static DEVICE_ATTR(vendor, 0444, pressure_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, pressure_name_show, NULL); +static DEVICE_ATTR(calibration, 0664, + pressure_cabratioin_show, pressure_cabratioin_store); +static DEVICE_ATTR(sea_level_pressure, 0664, + sea_level_pressure_show, sea_level_pressure_store); +static DEVICE_ATTR(temperature, 0444, temperature_show, NULL); +static DEVICE_ATTR(selftest, 0444, selftest_show, NULL); +#ifdef CONFIG_SEC_FACTORY +static DEVICE_ATTR(dhr_sensor_info, 0444, + pressure_dhr_sensor_info_show, NULL); +#else +static DEVICE_ATTR(dhr_sensor_info, 0440, + pressure_dhr_sensor_info_show, NULL); +#endif + +static struct device_attribute *pressure_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_calibration, + &dev_attr_sea_level_pressure, + &dev_attr_temperature, + &dev_attr_selftest, + &dev_attr_dhr_sensor_info, + NULL, +}; + +static int __init lps22hh_pressure_factory_init(void) +{ + adsp_factory_register(MSG_PRESSURE, pressure_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit lps22hh_pressure_factory_exit(void) +{ + adsp_factory_unregister(MSG_PRESSURE); + + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(lps22hh_pressure_factory_init); +module_exit(lps22hh_pressure_factory_exit); diff --git a/drivers/adsp_factory/lsm6dsl_accel.c b/drivers/adsp_factory/lsm6dsl_accel.c new file mode 100644 index 000000000000..fdb60060fc00 --- /dev/null +++ b/drivers/adsp_factory/lsm6dsl_accel.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#ifdef CONFIG_SLPI_MOTOR +#include +#endif +#define VENDOR "STM" +#define CHIP_ID "LSM6DSL" +#define ACCEL_ST_TRY_CNT 3 +#define ACCEL_FACTORY_CAL_CNT 20 +#define ACCEL_RAW_DATA_CNT 3 +#define MAX_ACCEL_1G 4096 + +/* Haptic Pattern A vibrate during 7ms. + * touch, touchkey, operation feedback use this. + * Do not call motor_workfunc when duration is 7ms. + */ +#define DURATION_SKIP 10 +#define MOTOR_OFF 0 + +#define ACCEL_FACTORY_CAL_PATH "/efs/FactoryApp/accel_factory_cal" + +#ifdef CONFIG_SLPI_MOTOR +struct accel_motor_data { + struct workqueue_struct *slpi_motor_wq; + struct work_struct work_slpi_motor; + int motor_state; +}; + +struct accel_motor_data *pdata_motor; +#endif + +struct accel_data { + struct work_struct work_accel; + struct workqueue_struct *accel_wq; + struct adsp_data *dev_data; + bool is_complete_cal; + int32_t raw_data[ACCEL_RAW_DATA_CNT]; + int32_t avg_data[ACCEL_RAW_DATA_CNT]; +}; + +static struct accel_data *pdata; + +static ssize_t accel_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t accel_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t sensor_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", "ADSP"); +} + +int get_accel_cal_data(int32_t *cal_data) +{ + struct file *factory_cal_filp = NULL; + mm_segment_t old_fs; + int ret = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, + O_RDONLY, 0440); + + if (IS_ERR(factory_cal_filp)) { + set_fs(old_fs); + ret = PTR_ERR(factory_cal_filp); + pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n", + __func__, ret); + return ret; + } + + ret = vfs_read(factory_cal_filp, (char *)cal_data, + ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos); + if (ret < 0) { + pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret); + filp_close(factory_cal_filp, current->files); + set_fs(old_fs); + return ret; + } + + filp_close(factory_cal_filp, current->files); + set_fs(old_fs); + + return ret; +} + +int set_accel_cal_data(int32_t *cal_data, bool first_booting) +{ + struct file *factory_cal_filp = NULL; + mm_segment_t old_fs; + int flag, ret = 0; + umode_t mode = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + if (first_booting) { + flag = O_TRUNC | O_RDWR | O_CREAT; + mode = 0600; + } else { + flag = O_RDWR; + mode = 0660; + } + + factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, flag, mode); + + if (IS_ERR(factory_cal_filp)) { + set_fs(old_fs); + ret = PTR_ERR(factory_cal_filp); + pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n", + __func__, ret); + return ret; + } + + ret = vfs_write(factory_cal_filp, (char *)cal_data, + ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos); + if (ret < 0) + pr_err("[FACTORY] %s: fd write %d\n", __func__, ret); + + filp_close(factory_cal_filp, current->files); + set_fs(old_fs); + + adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + return ret; +} + +static ssize_t accel_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int32_t cal_data[ACCEL_RAW_DATA_CNT] = {0, }; + + if (get_accel_cal_data(cal_data) > 0) { + pr_info("[FACTORY] %s: %d, %d, %d\n", __func__, + cal_data[0], cal_data[1], cal_data[2]); + if (cal_data[0] == 0 && cal_data[1] == 0 && cal_data[2] == 0) + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", + 0, 0, 0, 0); + else + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", + true, cal_data[0], cal_data[1], cal_data[2]); + } else { + pr_err("[FACTORY] %s: get_accel_cal_data fail\n", __func__); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", 0, 0, 0, 0); + } +} + +static ssize_t accel_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + + pdata->dev_data = data; + if (sysfs_streq(buf, "0")) { + mutex_lock(&data->accel_factory_mutex); + memset(pdata->avg_data, 0, sizeof(pdata->avg_data)); + set_accel_cal_data(pdata->avg_data, false); + mutex_unlock(&data->accel_factory_mutex); + } else { + pdata->is_complete_cal = false; + queue_work(pdata->accel_wq, &pdata->work_accel); + while (pdata->is_complete_cal == false) { + pr_info("[FACTORY] %s: In factory cal\n", __func__); + msleep(20); + } + mutex_lock(&data->accel_factory_mutex); + set_accel_cal_data(pdata->avg_data, false); + mutex_unlock(&data->accel_factory_mutex); + } + + return size; +} + +static void accel_work_func(struct work_struct *work) +{ + struct accel_data *data = container_of((struct work_struct *)work, + struct accel_data, work_accel); + int i; + + mutex_lock(&data->dev_data->accel_factory_mutex); + memset(pdata->avg_data, 0, sizeof(pdata->avg_data)); + adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + msleep(30); /* for init of bias */ + for (i = 0; i < ACCEL_FACTORY_CAL_CNT; i++) { + msleep(20); + get_accel_raw_data(pdata->raw_data); + pdata->avg_data[0] += pdata->raw_data[0]; + pdata->avg_data[1] += pdata->raw_data[1]; + pdata->avg_data[2] += pdata->raw_data[2]; + pr_info("[FACTORY] %s: %d, %d, %d\n", __func__, + pdata->raw_data[0], pdata->raw_data[1], pdata->raw_data[2]); + } + + for (i = 0; i < ACCEL_RAW_DATA_CNT; i++) { + pdata->avg_data[i] /= ACCEL_FACTORY_CAL_CNT; + pr_err("[FACTORY] %s: avg : %d\n", __func__, pdata->avg_data[i]); + } + + if (pdata->avg_data[2] > 0) + pdata->avg_data[2] -= MAX_ACCEL_1G; + else if (pdata->avg_data[2] < 0) + pdata->avg_data[2] += MAX_ACCEL_1G; + + mutex_unlock(&data->dev_data->accel_factory_mutex); + pdata->is_complete_cal = true; + + return; +} + +static ssize_t accel_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int retry = 0; + +RETRY_ACCEL_SELFTEST: + adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_ST_SHOW_DATA); + + while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_ACCEL); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + data->msg_buf[MSG_ACCEL][1] = -1; + } + + pr_info("[FACTORY] %s : init = %d, result = %d, XYZ = %d, %d, %d, nXYZ = %d, %d, %d\n", + __func__, data->msg_buf[MSG_ACCEL][0], + data->msg_buf[MSG_ACCEL][1], data->msg_buf[MSG_ACCEL][2], + data->msg_buf[MSG_ACCEL][3], data->msg_buf[MSG_ACCEL][4], + data->msg_buf[MSG_ACCEL][5], data->msg_buf[MSG_ACCEL][6], + data->msg_buf[MSG_ACCEL][7]); + + if (data->msg_buf[MSG_ACCEL][1] == 1) { + pr_info("[FACTORY] %s : Pass - result = %d, retry = %d\n", + __func__, data->msg_buf[MSG_ACCEL][1], retry); + } else { + data->msg_buf[MSG_ACCEL][1] = -5; + pr_err("[FACTORY] %s : Fail - result = %d, retry = %d\n", + __func__, data->msg_buf[MSG_ACCEL][1], retry); + + if (retry < ACCEL_ST_TRY_CNT && + data->msg_buf[MSG_ACCEL][2] == 0) { + retry++; + msleep(200); + pr_info("[FACTORY] %s: retry\n", __func__); + goto RETRY_ACCEL_SELFTEST; + } + } + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_ACCEL][1], + (int)abs(data->msg_buf[MSG_ACCEL][2]), + (int)abs(data->msg_buf[MSG_ACCEL][3]), + (int)abs(data->msg_buf[MSG_ACCEL][4]), + (int)abs(data->msg_buf[MSG_ACCEL][5]), + (int)abs(data->msg_buf[MSG_ACCEL][6]), + (int)abs(data->msg_buf[MSG_ACCEL][7])); +} + +static ssize_t accel_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, }; + + mutex_lock(&data->accel_factory_mutex); + get_accel_raw_data(raw_data); + mutex_unlock(&data->accel_factory_mutex); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + raw_data[0], raw_data[1], raw_data[2]); +} + +static ssize_t accel_reactive_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + bool success = false; + int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, }; + + mutex_lock(&data->accel_factory_mutex); + get_accel_raw_data(raw_data); + mutex_unlock(&data->accel_factory_mutex); + + if (raw_data[0] != 0 || raw_data[1] != 0 || raw_data[2] != 0) + success = true; + + return snprintf(buf, PAGE_SIZE, "%d\n", success); +} + +static ssize_t accel_reactive_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + if (sysfs_streq(buf, "1")) + pr_info("[FACTORY]: %s - on\n", __func__); + else if (sysfs_streq(buf, "0")) + pr_info("[FACTORY]: %s - off\n", __func__); + else if (sysfs_streq(buf, "2")) + pr_info("[FACTORY]: %s - factory\n", __func__); + + return size; +} + +static ssize_t accel_lowpassfilter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int32_t msg_buf; + + if (sysfs_streq(buf, "1")) { + msg_buf = 1; + } else if (sysfs_streq(buf, "0")) { + msg_buf = 0; + } else { + pr_info("[FACTORY] %s: wrong value\n", __func__); + return size; + } + + mutex_lock(&data->accel_factory_mutex); + adsp_unicast(&msg_buf, sizeof(int32_t), MSG_ACCEL, 0, MSG_TYPE_SET_ACCEL_LPF); + + while (!(data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] &= ~(1 << MSG_ACCEL); + mutex_unlock(&data->accel_factory_mutex); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return size; + } + + pr_info("[FACTORY] %s: lpf_on_off done (%d)(0x%x)\n", __func__, + data->msg_buf[MSG_ACCEL][0], data->msg_buf[MSG_ACCEL][1]); + + return size; +} + +#ifdef CONFIG_SLPI_MOTOR +int setSensorCallback(int state, int duration) +{ + if (duration > MOTOR_OFF && duration <= DURATION_SKIP) + return 0; + + if (pdata_motor->motor_state != state) { + pr_info("[FACTORY] %s: state = %d, duration = %d\n", + __func__, pdata_motor->motor_state, duration); + pdata_motor->motor_state = state; + queue_work(pdata_motor->slpi_motor_wq, &pdata_motor->work_slpi_motor); + } + + return 0; +} + +void slpi_motor_work_func(struct work_struct *work) +{ +#if 0 + struct msg_data message; + int motor = 0; + + if (pdata_motor->motor_state == 1) { + motor = MSG_TYPE_ACCEL_MOTOR_ON; + message.msg_type = MSG_ACCEL_MOT_ON; + } else if (pdata_motor->motor_state == 0) { + motor = MSG_TYPE_ACCEL_MOTOR_OFF; + message.msg_type = MSG_ACCEL_MOT_OFF; + } + + pr_info("[FACTORY] %s: state = %d\n", __func__, pdata_motor->motor_state); + + adsp_unicast(&message, sizeof(message), motor, 0, 0); +#endif +} +#endif + +static ssize_t accel_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + char ctrl1_xl = 0; + uint8_t fullscale = 0; + int32_t *info = data->msg_buf[MSG_ACCEL]; + + adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_ACCEL); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + ctrl1_xl = *info; + + ctrl1_xl &= 0xC; + + switch (ctrl1_xl) { + case 0xC: + fullscale = 8; + break; + case 0x8: + fullscale = 4; + break; + case 0x4: + fullscale = 16; + break; + case 0: + fullscale = 2; + break; + default: + break; + } + + pr_info("[FACTORY] %s: f/s %u\n", __func__, fullscale); + + return snprintf(buf, PAGE_SIZE, "\"FULL_SCALE\":\"%uG\"\n", fullscale); +} + +static DEVICE_ATTR(name, 0444, accel_name_show, NULL); +static DEVICE_ATTR(vendor, 0444, accel_vendor_show, NULL); +static DEVICE_ATTR(type, 0444, sensor_type_show, NULL); +static DEVICE_ATTR(calibration, 0664, + accel_calibration_show, accel_calibration_store); +static DEVICE_ATTR(selftest, 0440, + accel_selftest_show, NULL); +static DEVICE_ATTR(raw_data, 0444, accel_raw_data_show, NULL); +static DEVICE_ATTR(reactive_alert, 0664, + accel_reactive_show, accel_reactive_store); +static DEVICE_ATTR(lowpassfilter, 0220, + NULL, accel_lowpassfilter_store); +#ifdef CONFIG_SEC_FACTORY +static DEVICE_ATTR(dhr_sensor_info, 0444, + accel_dhr_sensor_info_show, NULL); +#else +static DEVICE_ATTR(dhr_sensor_info, 0440, + accel_dhr_sensor_info_show, NULL); +#endif + +static struct device_attribute *acc_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_type, + &dev_attr_calibration, + &dev_attr_selftest, + &dev_attr_raw_data, + &dev_attr_reactive_alert, + &dev_attr_lowpassfilter, + &dev_attr_dhr_sensor_info, + NULL, +}; + +void accel_factory_init_work(void) +{ + struct file *cal_filp = NULL; + mm_segment_t old_fs; + int32_t zero_data[ACCEL_RAW_DATA_CNT] = {0, }; + int ret = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, O_RDONLY, 0440); + if (PTR_ERR(cal_filp) == -ENOENT || PTR_ERR(cal_filp) == -ENXIO) { + pr_info("[FACTORY] %s : no accel_factory_cal file\n", __func__); + set_fs(old_fs); + set_accel_cal_data(zero_data, true); + } else if (IS_ERR(cal_filp)) { + pr_err("[FACTORY]: %s - filp_open error\n", __func__); + set_fs(old_fs); + } else { + pr_info("[FACTORY] %s : already exist\n", __func__); + ret = vfs_read(cal_filp, (char *)zero_data, + ACCEL_RAW_DATA_CNT * sizeof(int32_t), &cal_filp->f_pos); + if (ret < 0) { + pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret); + zero_data[0] = zero_data[1] = zero_data[2] = 0; + adsp_unicast(zero_data, sizeof(zero_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + filp_close(cal_filp, current->files); + set_fs(old_fs); + return; + } + adsp_unicast(zero_data, sizeof(zero_data), + MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA); + filp_close(cal_filp, current->files); + set_fs(old_fs); + } +} + +static int __init lsm6dsl_accel_factory_init(void) +{ + adsp_factory_register(MSG_ACCEL, acc_attrs); +#ifdef CONFIG_SLPI_MOTOR + pdata_motor = kzalloc(sizeof(*pdata_motor), GFP_KERNEL); + + if (pdata_motor == NULL) + return -ENOMEM; + + pdata_motor->slpi_motor_wq = + create_singlethread_workqueue("slpi_motor_wq"); + + if (pdata_motor->slpi_motor_wq == NULL) { + pr_err("[FACTORY]: %s - could not create motor wq\n", __func__); + kfree(pdata_motor); + return -ENOMEM; + } + + INIT_WORK(&pdata_motor->work_slpi_motor, slpi_motor_work_func); + + pdata_motor->motor_state = 0; +#endif + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + pdata->accel_wq = create_singlethread_workqueue("accel_wq"); + INIT_WORK(&pdata->work_accel, accel_work_func); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit lsm6dsl_accel_factory_exit(void) +{ + adsp_factory_unregister(MSG_ACCEL); +#ifdef CONFIG_SLPI_MOTOR + if (pdata_motor != NULL && pdata_motor->slpi_motor_wq != NULL) { + cancel_work_sync(&pdata_motor->work_slpi_motor); + destroy_workqueue(pdata_motor->slpi_motor_wq); + pdata_motor->slpi_motor_wq = NULL; + } +#endif + pr_info("[FACTORY] %s\n", __func__); +} +module_init(lsm6dsl_accel_factory_init); +module_exit(lsm6dsl_accel_factory_exit); diff --git a/drivers/adsp_factory/lsm6dsl_gyro.c b/drivers/adsp_factory/lsm6dsl_gyro.c new file mode 100644 index 000000000000..ed55aee97eac --- /dev/null +++ b/drivers/adsp_factory/lsm6dsl_gyro.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#define VENDOR "STM" +#define CHIP_ID "LSM6DSL" +#define ST_PASS 1 +#define ST_FAIL 0 +#define ST_ZRO_MIN (-40) +#define ST_ZRO_MAX 40 +#define SELFTEST_REVISED 1 + +static ssize_t gyro_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t gyro_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t selftest_revised_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", SELFTEST_REVISED); +} + +static ssize_t gyro_power_off(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_info("[FACTORY]: %s\n", __func__); + + return snprintf(buf, PAGE_SIZE, "%d\n", 1); +} + +static ssize_t gyro_power_on(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_info("[FACTORY]: %s\n", __func__); + + return snprintf(buf, PAGE_SIZE, "%d\n", 1); +} + +static ssize_t gyro_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + adsp_unicast(NULL, 0, MSG_GYRO_TEMP, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_GYRO_TEMP) + && cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_GYRO_TEMP); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, "-99\n"); + } + + pr_info("[FACTORY] %s: gyro_temp = %d\n", __func__, + data->msg_buf[MSG_GYRO_TEMP][0]); + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->msg_buf[MSG_GYRO_TEMP][0]); +} + +static ssize_t gyro_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int st_diff_res = ST_FAIL; + int st_zro_res = ST_FAIL; + + pr_info("[FACTORY] %s - start", __func__); + adsp_unicast(NULL, 0, MSG_GYRO, 0, MSG_TYPE_ST_SHOW_DATA); + + while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_GYRO) && + cnt++ < TIMEOUT_CNT) + msleep(20); + + data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_GYRO); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + return snprintf(buf, PAGE_SIZE, + "0,0,0,0,0,0,0,0,0,0,0,0,%d,%d\n", + ST_FAIL, ST_FAIL); + } + + if (data->msg_buf[MSG_GYRO][1] != 0) { + pr_info("[FACTORY] %s - failed(%d, %d)\n", __func__, + data->msg_buf[MSG_GYRO][1], + data->msg_buf[MSG_GYRO][5]); + + pr_info("[FACTORY]: %s - %d,%d,%d\n", __func__, + data->msg_buf[MSG_GYRO][2], + data->msg_buf[MSG_GYRO][3], + data->msg_buf[MSG_GYRO][4]); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + data->msg_buf[MSG_GYRO][2], + data->msg_buf[MSG_GYRO][3], + data->msg_buf[MSG_GYRO][4]); + } + + if (!data->msg_buf[MSG_GYRO][5]) + st_diff_res = ST_PASS; + + if((ST_ZRO_MIN <= data->msg_buf[MSG_GYRO][6]) + && (data->msg_buf[MSG_GYRO][6] <= ST_ZRO_MAX) + && (ST_ZRO_MIN <= data->msg_buf[MSG_GYRO][7]) + && (data->msg_buf[MSG_GYRO][7] <= ST_ZRO_MAX) + && (ST_ZRO_MIN <= data->msg_buf[MSG_GYRO][8]) + && (data->msg_buf[MSG_GYRO][8]<= ST_ZRO_MAX)) + st_zro_res = ST_PASS; + + pr_info("[FACTORY]: %s - %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + __func__, + data->msg_buf[MSG_GYRO][2], data->msg_buf[MSG_GYRO][3], + data->msg_buf[MSG_GYRO][4], data->msg_buf[MSG_GYRO][6], + data->msg_buf[MSG_GYRO][7], data->msg_buf[MSG_GYRO][8], + data->msg_buf[MSG_GYRO][9], data->msg_buf[MSG_GYRO][10], + data->msg_buf[MSG_GYRO][11], data->msg_buf[MSG_GYRO][12], + data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14], + st_diff_res, st_zro_res); + + return snprintf(buf, PAGE_SIZE, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_GYRO][2], data->msg_buf[MSG_GYRO][3], + data->msg_buf[MSG_GYRO][4], data->msg_buf[MSG_GYRO][6], + data->msg_buf[MSG_GYRO][7], data->msg_buf[MSG_GYRO][8], + data->msg_buf[MSG_GYRO][9], data->msg_buf[MSG_GYRO][10], + data->msg_buf[MSG_GYRO][11], data->msg_buf[MSG_GYRO][12], + data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14], + st_diff_res, st_zro_res); +} + +static DEVICE_ATTR(name, 0444, gyro_name_show, NULL); +static DEVICE_ATTR(vendor, 0444, gyro_vendor_show, NULL); +static DEVICE_ATTR(selftest, 0440, gyro_selftest_show, NULL); +static DEVICE_ATTR(power_on, 0444, gyro_power_on, NULL); +static DEVICE_ATTR(power_off, 0444, gyro_power_off, NULL); +static DEVICE_ATTR(temperature, 0440, gyro_temp_show, NULL); +static DEVICE_ATTR(selftest_revised, 0440, selftest_revised_show, NULL); + +static struct device_attribute *gyro_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_selftest, + &dev_attr_power_on, + &dev_attr_power_off, + &dev_attr_temperature, + &dev_attr_selftest_revised, + NULL, +}; + +static int __init lsm6dsl_gyro_factory_init(void) +{ + adsp_factory_register(MSG_GYRO, gyro_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit lsm6dsl_gyro_factory_exit(void) +{ + adsp_factory_unregister(MSG_GYRO); + + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(lsm6dsl_gyro_factory_init); +module_exit(lsm6dsl_gyro_factory_exit); diff --git a/drivers/adsp_factory/ssc_core.c b/drivers/adsp_factory/ssc_core.c new file mode 100644 index 000000000000..94ff88b6ac19 --- /dev/null +++ b/drivers/adsp_factory/ssc_core.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include + +#ifdef CONFIG_SUPPORT_DEVICE_MODE +#include +#endif +#include "adsp.h" + +#define SSR_REASON_LEN 128 +#ifdef CONFIG_SEC_FACTORY +#define SLPI_STUCK "SLPI_STUCK" +#define SLPI_PASS "SLPI_PASS" +#endif + +static int pid; +static char panic_msg[SSR_REASON_LEN]; +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +static char operation_mode_flag[11]; + +static ssize_t dumpstate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int32_t type[1]; + if (pid != 0) { + adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_ACCEL) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_ACCEL); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: accel dhr_sensor_info Timeout!!!\n", + __func__); + + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: light dhr_sensor_info Timeout!!!\n", + __func__); + + pr_info("[FACTORY] to take the logs\n"); + type[0] = 0; + } else { + type[0] = 2; + pr_info("[FACTORY] logging service was stopped %d\n", pid); + } + adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE); + return snprintf(buf, PAGE_SIZE, "SSC_CORE\n"); +} + +static ssize_t operation_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s", operation_mode_flag); +} + +static ssize_t operation_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int i; + + for (i = 0; i < 10 && buf[i] != '\0'; i++) + operation_mode_flag[i] = buf[i]; + operation_mode_flag[i] = '\0'; + + pr_info("[FACTORY] %s: operation_mode_flag = %s\n", __func__, + operation_mode_flag); + return size; +} + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long timeout; + unsigned long timeout_2; + int32_t type[1]; + + if (pid != 0) { + timeout = jiffies + (2 * HZ); + timeout_2 = jiffies + (10 * HZ); + type[0] = 1; + pr_info("[FACTORY] To stop logging %d\n", pid); + adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE); + + while (pid != 0) { + msleep(25); + if (time_after(jiffies, timeout)) + pr_info("[FACTORY] %s: Timeout!!!\n", __func__); + if (time_after(jiffies, timeout_2)) { +// panic("force crash : ssc core\n"); + pr_info("[FACTORY] pid %d\n", pid); + return snprintf(buf, PAGE_SIZE, "1\n"); + } + } + } + + return snprintf(buf, PAGE_SIZE, "0\n"); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int data = 0; + int32_t type[1]; + if (kstrtoint(buf, 10, &data)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return -EINVAL; + } + + if (data != 1) { + pr_err("[FACTORY] %s: data was wrong\n", __func__); + return -EINVAL; + } + + if (pid != 0) { + type[0] = 1; + adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE); + } + + return size; +} + +static ssize_t pid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", pid); +} + +static ssize_t pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int data = 0; + + if (kstrtoint(buf, 10, &data)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return -EINVAL; + } + + pid = data; + pr_info("[FACTORY] %s: pid %d\n", __func__, pid); + + return size; +} + +static ssize_t remove_sensor_sysfs_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int type = MSG_SENSOR_MAX; + struct adsp_data *data = dev_get_drvdata(dev); + + if (kstrtouint(buf, 10, &type)) { + pr_err("[FACTORY] %s: kstrtouint fail\n", __func__); + return -EINVAL; + } + + if (type > PHYSICAL_SENSOR_SYSFS) { + pr_err("[FACTORY] %s: Invalid type %u\n", __func__, type); + return size; + } + + pr_info("[FACTORY] %s: type = %u\n", __func__, type); + + mutex_lock(&data->remove_sysfs_mutex); + adsp_factory_unregister(type); + mutex_unlock(&data->remove_sysfs_mutex); + + return size; +} + +extern unsigned int sec_hw_rev(void); +int ssc_system_rev_test; +static ssize_t ssc_hw_rev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_info("[FACTORY] %s: ssc_rev:%d\n", __func__, sec_hw_rev()); + + if (!ssc_system_rev_test) + return snprintf(buf, PAGE_SIZE, "%d\n", sec_hw_rev()); + else + return snprintf(buf, PAGE_SIZE, "%d\n", ssc_system_rev_test); +} + +static ssize_t ssc_hw_rev_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + if (kstrtoint(buf, 10, &ssc_system_rev_test)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return -EINVAL; + } + + pr_info("[FACTORY] %s: system_rev_ssc %d\n", __func__, ssc_system_rev_test); + + return size; +} + +void ssr_reason_call_back(char reason[], int len) +{ + if (len <= 0) { + pr_info("[FACTORY] ssr %d\n", len); + return; + } + memset(panic_msg, 0, SSR_REASON_LEN); + strlcpy(panic_msg, reason, min(len, (int)(SSR_REASON_LEN - 1))); + + pr_info("[FACTORY] ssr %s\n", panic_msg); +} + +static ssize_t ssr_msg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef CONFIG_SEC_FACTORY + struct adsp_data *data = dev_get_drvdata(dev); + int32_t raw_data_acc[3] = {0, }; + int32_t raw_data_mag[3] = {0, }; + int ret_acc = 0; + int ret_mag = 0; + + mutex_lock(&data->accel_factory_mutex); + ret_acc = get_accel_raw_data(raw_data_acc); + mutex_unlock(&data->accel_factory_mutex); + + ret_mag = get_mag_raw_data(raw_data_mag); + + pr_info("[FACTORY] %s: accel(%d, %d, %d), mag(%d, %d, %d)\n", __func__, + raw_data_acc[0], raw_data_acc[1], raw_data_acc[2], + raw_data_mag[0], raw_data_mag[1], raw_data_mag[2]); + + if (ret_acc == -1 && ret_mag == -1) { + pr_err("[FACTORY] %s: SLPI stuck, check hal log\n", __func__); + return snprintf(buf, PAGE_SIZE, "%s\n", SLPI_STUCK); + } else { + return snprintf(buf, PAGE_SIZE, "%s\n", SLPI_PASS); + } +#else + return snprintf(buf, PAGE_SIZE, "%s\n", panic_msg); +#endif +} + +static ssize_t ssr_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int32_t type[1]; + + type[0] = 0xff; + + adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE); + pr_info("[FACTORY] %s\n", __func__); + + return snprintf(buf, PAGE_SIZE, "%s\n", "Success"); +} + +#ifdef CONFIG_SUPPORT_DEVICE_MODE +int sns_device_mode_notify(struct notifier_block *nb, + unsigned long flip_state, void *v) +{ + int state = (int)flip_state; + + pr_info("[FACTORY] %s - device mode %d", __func__, state); + if(state == 0) + adsp_unicast(NULL, 0, MSG_SSC_CORE, 0, MSG_TYPE_FACTORY_ENABLE); + else + adsp_unicast(NULL, 0, MSG_SSC_CORE, 0, MSG_TYPE_FACTORY_DISABLE); + + return 0; +} + +static struct notifier_block sns_device_mode_notifier = { + .notifier_call = sns_device_mode_notify, + .priority = 1, +}; +#endif + +static DEVICE_ATTR(dumpstate, 0440, dumpstate_show, NULL); +static DEVICE_ATTR(operation_mode, 0664, + operation_mode_show, operation_mode_store); +static DEVICE_ATTR(mode, 0660, mode_show, mode_store); +static DEVICE_ATTR(ssc_pid, 0660, pid_show, pid_store); +static DEVICE_ATTR(remove_sysfs, 0220, NULL, remove_sensor_sysfs_store); +static DEVICE_ATTR(ssc_hw_rev, 0664, ssc_hw_rev_show, ssc_hw_rev_store); +static DEVICE_ATTR(ssr_msg, 0440, ssr_msg_show, NULL); +static DEVICE_ATTR(ssr_reset, 0440, ssr_reset_show, NULL); + +static struct device_attribute *core_attrs[] = { + &dev_attr_dumpstate, + &dev_attr_operation_mode, + &dev_attr_mode, + &dev_attr_ssc_pid, + &dev_attr_remove_sysfs, + &dev_attr_ssc_hw_rev, + &dev_attr_ssr_msg, + &dev_attr_ssr_reset, + NULL, +}; + +static int __init core_factory_init(void) +{ + adsp_factory_register(MSG_SSC_CORE, core_attrs); +#ifdef CONFIG_SUPPORT_DEVICE_MODE + hall_ic_register_notify(&sns_device_mode_notifier); +#endif + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit core_factory_exit(void) +{ + adsp_factory_unregister(MSG_SSC_CORE); +#ifdef CONFIG_SUPPORT_DEVICE_MODE + hall_ic_unregister_notify(&sns_device_mode_notifier); +#endif + pr_info("[FACTORY] %s\n", __func__); +} +module_init(core_factory_init); +module_exit(core_factory_exit); diff --git a/drivers/adsp_factory/vcnl36658_light.c b/drivers/adsp_factory/vcnl36658_light.c new file mode 100755 index 000000000000..b3b3fa105b49 --- /dev/null +++ b/drivers/adsp_factory/vcnl36658_light.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include "adsp.h" +#define VENDOR "CAPELLA" +#define CHIP_ID "VCNL36658" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ +static ssize_t light_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t light_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t light_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + mutex_lock(&data->light_factory_mutex); + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + mutex_unlock(&data->light_factory_mutex); + return snprintf(buf, PAGE_SIZE, "0,0,0,0,0,0\n"); + } + + mutex_unlock(&data->light_factory_mutex); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_LIGHT][0], data->msg_buf[MSG_LIGHT][1], + data->msg_buf[MSG_LIGHT][2], data->msg_buf[MSG_LIGHT][3], + data->msg_buf[MSG_LIGHT][4], data->msg_buf[MSG_LIGHT][5]); +} + +static ssize_t light_get_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + pr_info("[FACTORY] %s: start\n", __func__); + mutex_lock(&data->light_factory_mutex); + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + mutex_unlock(&data->light_factory_mutex); + return data->msg_buf[MSG_LIGHT][0]; +} + +static DEVICE_ATTR(vendor, 0444, light_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, light_name_show, NULL); +static DEVICE_ATTR(lux, 0444, light_raw_data_show, NULL); +static DEVICE_ATTR(raw_data, 0444, light_raw_data_show, NULL); +static DEVICE_ATTR(dhr_sensor_info, 0444, light_get_dhr_sensor_info_show, NULL); + +static struct device_attribute *light_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_lux, + &dev_attr_raw_data, + &dev_attr_dhr_sensor_info, + NULL, +}; + +static int __init veml3328_light_factory_init(void) +{ + adsp_factory_register(MSG_LIGHT, light_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit veml3328_light_factory_exit(void) +{ + adsp_factory_unregister(MSG_LIGHT); + + pr_info("[FACTORY] %s\n", __func__); +} +module_init(veml3328_light_factory_init); +module_exit(veml3328_light_factory_exit); diff --git a/drivers/adsp_factory/vcnl36658_prox.c b/drivers/adsp_factory/vcnl36658_prox.c new file mode 100755 index 000000000000..68d80ffa95e5 --- /dev/null +++ b/drivers/adsp_factory/vcnl36658_prox.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#define VENDOR "CAPELLA" +#define CHIP_ID "VCNL36658" + +#define PROX_AVG_COUNT 40 +#define PROX_ALERT_THRESHOLD 200 +#define PROX_TH_READ 0 +#define PROX_TH_WRITE 1 +#define BUFFER_MAX 128 +#define PROX_REG_START 0x80 +#define PROX_DETECT_HIGH_TH 16368 +#define PROX_DETECT_LOW_TH 1000 + +extern unsigned int system_rev; + +struct vcnl36863_prox_data { + struct hrtimer prox_timer; + struct work_struct work_prox; + struct workqueue_struct *prox_wq; + int min; + int max; + int avg; + int val; + int offset; + int reg_backup[2]; + short avgwork_check; + short avgtimer_enabled; + short bBarcodeEnabled; +}; + +enum { + PRX_THRESHOLD_DETECT_H, + PRX_THRESHOLD_HIGH_DETECT_L, + PRX_THRESHOLD_HIGH_DETECT_H, + PRX_THRESHOLD_RELEASE_L, +}; + +static struct vcnl36863_prox_data *pdata; + +static ssize_t prox_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t prox_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t prox_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (pdata->avgwork_check == 0) + get_prox_raw_data(&pdata->val, &pdata->offset); + + return snprintf(buf, PAGE_SIZE, "%d\n", pdata->val); +} + +static ssize_t prox_avg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", pdata->min, + pdata->avg, pdata->max); +} + +static ssize_t prox_avg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int new_value; + + if (sysfs_streq(buf, "0")) + new_value = 0; + else + new_value = 1; + + if (new_value == pdata->avgtimer_enabled) + return size; + + if (new_value == 0) { + pdata->avgtimer_enabled = 0; + hrtimer_cancel(&pdata->prox_timer); + cancel_work_sync(&pdata->work_prox); + } else { + pdata->avgtimer_enabled = 1; + hrtimer_start(&pdata->prox_timer, + ns_to_ktime(2000 * NSEC_PER_MSEC), + HRTIMER_MODE_REL); + } + + return size; +} + +static void prox_work_func(struct work_struct *work) +{ + int min = 0, max = 0, avg = 0; + int i; + + pdata->avgwork_check = 1; + for (i = 0; i < PROX_AVG_COUNT; i++) { + msleep(20); + + get_prox_raw_data(&pdata->val, &pdata->offset); + avg += pdata->val; + + if (!i) + min = pdata->val; + else if (pdata->val < min) + min = pdata->val; + + if (pdata->val > max) + max = pdata->val; + } + avg /= PROX_AVG_COUNT; + + pdata->min = min; + pdata->avg = avg; + pdata->max = max; + pdata->avgwork_check = 0; +} + +static enum hrtimer_restart prox_timer_func(struct hrtimer *timer) +{ + queue_work(pdata->prox_wq, &pdata->work_prox); + hrtimer_forward_now(&pdata->prox_timer, + ns_to_ktime(2000 * NSEC_PER_MSEC)); + return HRTIMER_RESTART; +} + +int get_prox_threshold(struct adsp_data *data, int type) +{ + uint8_t cnt = 0; + int32_t msg_buf[2]; + int ret = 0; + + msg_buf[0] = type; + msg_buf[1] = 0; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_GET_THRESHOLD); + + while (!(data->ready_flag[MSG_TYPE_GET_THRESHOLD] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(2000, 2050); + + data->ready_flag[MSG_TYPE_GET_THRESHOLD] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + mutex_unlock(&data->prox_factory_mutex); + return ret; + } + + ret = data->msg_buf[MSG_PROX][0]; + mutex_unlock(&data->prox_factory_mutex); + + return ret; +} + +void set_prox_threshold(struct adsp_data *data, int type, int val) +{ + uint8_t cnt = 0; + int32_t msg_buf[2]; + + msg_buf[0] = type; + msg_buf[1] = val; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_SET_THRESHOLD); + + while (!(data->ready_flag[MSG_TYPE_SET_THRESHOLD] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_SET_THRESHOLD] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + mutex_unlock(&data->prox_factory_mutex); +} + +static ssize_t prox_cancel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int hi_thd, low_thd; + + hi_thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H); + low_thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L); + + if (pdata->avgwork_check == 0) + get_prox_raw_data(&pdata->val, &pdata->offset); + + pr_info("[FACTORY] %s: offset: %d, hi thd: %d, lo thd: %d\n", __func__, + pdata->offset, hi_thd, low_thd); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + pdata->offset, hi_thd, low_thd); +} + +static ssize_t prox_cancel_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + pr_info("[FACTORY] %s: skip\n", __func__); + + return size; +} + +static ssize_t prox_thresh_high_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_thresh_high_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_DETECT_H, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} + +static ssize_t prox_thresh_low_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_thresh_low_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_RELEASE_L, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} + +#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL +static ssize_t prox_high_thresh_high_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_high_thresh_high_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} + +static ssize_t prox_high_thresh_low_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_high_thresh_low_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} +#endif + +static ssize_t barcode_emul_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", pdata->bBarcodeEnabled); +} + +static ssize_t barcode_emul_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iRet; + int64_t dEnable; + + iRet = kstrtoll(buf, 10, &dEnable); + if (iRet < 0) + return iRet; + + if (dEnable) + pdata->bBarcodeEnabled = 1; + else + pdata->bBarcodeEnabled = 0; + + return size; +} + +static ssize_t prox_cancel_pass_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "1\n"); +} + +static ssize_t prox_default_trim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", pdata->offset); +} + +static ssize_t prox_alert_thresh_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", PROX_ALERT_THRESHOLD); +} + +static ssize_t prox_register_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int cnt = 0; + int32_t msg_buf[1]; + + msg_buf[0] = pdata->reg_backup[0]; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_GET_REGISTER); + + while (!(data->ready_flag[MSG_TYPE_GET_REGISTER] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_REGISTER] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pdata->reg_backup[1] = data->msg_buf[MSG_PROX][0]; + pr_info("[FACTORY] %s: [0x%x]: 0x%x\n", + __func__, pdata->reg_backup[0], pdata->reg_backup[1]); + + mutex_unlock(&data->prox_factory_mutex); + + return snprintf(buf, PAGE_SIZE, "[0x%x]: 0x%x\n", + pdata->reg_backup[0], pdata->reg_backup[1]); +} + +static ssize_t prox_register_read_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int reg = 0; + + if (sscanf(buf, "%4x", ®) != 1) { + pr_err("[FACTORY]: %s - The number of data are wrong\n", + __func__); + return -EINVAL; + } + + pdata->reg_backup[0] = reg; + pr_info("[FACTORY] %s: [0x%x]\n", __func__, pdata->reg_backup[0]); + + return size; +} + +static ssize_t prox_register_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int cnt = 0; + int32_t msg_buf[2]; + + if (sscanf(buf, "%2x,%4x", &msg_buf[0], &msg_buf[1]) != 2) { + pr_err("[FACTORY]: %s - The number of data are wrong\n", + __func__); + return -EINVAL; + } + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_SET_REGISTER); + + while (!(data->ready_flag[MSG_TYPE_SET_REGISTER] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_SET_REGISTER] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pdata->reg_backup[0] = msg_buf[0]; + pr_info("[FACTORY] %s: 0x%x - 0x%x\n", + __func__, msg_buf[0], data->msg_buf[MSG_PROX][0]); + mutex_unlock(&data->prox_factory_mutex); + + return size; +} + +static ssize_t prox_light_get_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int offset = 0; + int32_t *info = data->msg_buf[MSG_PROX]; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(NULL, 0, MSG_PROX, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pr_info("[FACTORY] %d,%d,%d,%d,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%d\n", + info[0], info[1], info[2], info[3], info[4], info[5], + info[6], info[7], info[8], info[9], info[10], info[11]); + + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"THD\":\"%d %d %d %d\",", info[0], info[1], info[2], info[3]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PDRIVE_CURRENT\":\"%02x\",", info[4]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PERSIST_TIME\":\"%02x\",", info[5]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PPULSE\":\"%02x\",", info[6]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PGAIN\":\"%02x\",", info[7]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PTIME\":\"%02x\",", info[8]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PPLUSE_LEN\":\"%02x\",", info[9]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"ATIME\":\"%02x\",", info[10]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"POFFSET\":\"%d\"\n", info[11]); + + mutex_unlock(&data->prox_factory_mutex); + return offset; +} + +static DEVICE_ATTR(vendor, 0444, prox_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, prox_name_show, NULL); +static DEVICE_ATTR(state, 0444, prox_raw_data_show, NULL); +static DEVICE_ATTR(raw_data, 0444, prox_raw_data_show, NULL); +static DEVICE_ATTR(prox_avg, 0664, + prox_avg_show, prox_avg_store); +static DEVICE_ATTR(prox_cal, 0664, + prox_cancel_show, prox_cancel_store); +static DEVICE_ATTR(thresh_high, 0664, + prox_thresh_high_show, prox_thresh_high_store); +static DEVICE_ATTR(thresh_low, 0664, + prox_thresh_low_show, prox_thresh_low_store); +#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL +static DEVICE_ATTR(high_thresh_high, 0664, + prox_high_thresh_high_show, prox_high_thresh_high_store); +static DEVICE_ATTR(high_thresh_low, 0664, + prox_high_thresh_low_show, prox_high_thresh_low_store); +#endif +static DEVICE_ATTR(register_write, 0220, + NULL, prox_register_write_store); +static DEVICE_ATTR(register_read, 0664, + prox_register_read_show, prox_register_read_store); +static DEVICE_ATTR(barcode_emul_en, 0664, + barcode_emul_enable_show, barcode_emul_enable_store); +static DEVICE_ATTR(prox_offset_pass, 0444, prox_cancel_pass_show, NULL); +static DEVICE_ATTR(prox_trim, 0444, prox_default_trim_show, NULL); + +static DEVICE_ATTR(prox_alert_thresh, 0444, prox_alert_thresh_show, NULL); +static DEVICE_ATTR(dhr_sensor_info, 0440, + prox_light_get_dhr_sensor_info_show, NULL); + +static struct device_attribute *prox_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_state, + &dev_attr_raw_data, + &dev_attr_prox_avg, + &dev_attr_prox_cal, + &dev_attr_thresh_high, + &dev_attr_thresh_low, +#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL + &dev_attr_high_thresh_high, + &dev_attr_high_thresh_low, +#endif + &dev_attr_barcode_emul_en, + &dev_attr_prox_offset_pass, + &dev_attr_prox_trim, + &dev_attr_prox_alert_thresh, + &dev_attr_dhr_sensor_info, + &dev_attr_register_write, + &dev_attr_register_read, + NULL, +}; + +static int __init vcnl36863_prox_factory_init(void) +{ + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + adsp_factory_register(MSG_PROX, prox_attrs); + pr_info("[FACTORY] %s\n", __func__); + + hrtimer_init(&pdata->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pdata->prox_timer.function = prox_timer_func; + pdata->prox_wq = create_singlethread_workqueue("prox_wq"); + + /* this is the thread function we run on the work queue */ + INIT_WORK(&pdata->work_prox, prox_work_func); + + pdata->avgwork_check = 0; + pdata->avgtimer_enabled = 0; + pdata->avg = 0; + pdata->min = 0; + pdata->max = 0; + pdata->offset = 0; + return 0; +} + +static void __exit vcnl36863_prox_factory_exit(void) +{ + if (pdata->avgtimer_enabled == 1) { + hrtimer_cancel(&pdata->prox_timer); + cancel_work_sync(&pdata->work_prox); + } + destroy_workqueue(pdata->prox_wq); + adsp_factory_unregister(MSG_PROX); + kfree(pdata); + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(vcnl36863_prox_factory_init); +module_exit(vcnl36863_prox_factory_exit); diff --git a/drivers/adsp_factory/vcnl36863_light.c b/drivers/adsp_factory/vcnl36863_light.c new file mode 100644 index 000000000000..842767a1a2b1 --- /dev/null +++ b/drivers/adsp_factory/vcnl36863_light.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include "adsp.h" +#define VENDOR "CAPELLA" +#define CHIP_ID "VCNL36863" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ +static ssize_t light_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t light_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t light_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + mutex_lock(&data->light_factory_mutex); + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + mutex_unlock(&data->light_factory_mutex); + return snprintf(buf, PAGE_SIZE, "0,0,0,0,0,0\n"); + } + + mutex_unlock(&data->light_factory_mutex); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_LIGHT][0], data->msg_buf[MSG_LIGHT][1], + data->msg_buf[MSG_LIGHT][2], data->msg_buf[MSG_LIGHT][3], + data->msg_buf[MSG_LIGHT][4], data->msg_buf[MSG_LIGHT][5]); +} + +static ssize_t light_get_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + pr_info("[FACTORY] %s: start\n", __func__); + mutex_lock(&data->light_factory_mutex); + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + mutex_unlock(&data->light_factory_mutex); + return data->msg_buf[MSG_LIGHT][0]; +} + +static DEVICE_ATTR(vendor, 0444, light_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, light_name_show, NULL); +static DEVICE_ATTR(lux, 0444, light_raw_data_show, NULL); +static DEVICE_ATTR(raw_data, 0444, light_raw_data_show, NULL); +static DEVICE_ATTR(dhr_sensor_info, 0444, light_get_dhr_sensor_info_show, NULL); + +static struct device_attribute *light_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_lux, + &dev_attr_raw_data, + &dev_attr_dhr_sensor_info, + NULL, +}; + +static int __init vcnl36863_light_factory_init(void) +{ + adsp_factory_register(MSG_LIGHT, light_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit vcnl36863_light_factory_exit(void) +{ + adsp_factory_unregister(MSG_LIGHT); + + pr_info("[FACTORY] %s\n", __func__); +} +module_init(vcnl36863_light_factory_init); +module_exit(vcnl36863_light_factory_exit); diff --git a/drivers/adsp_factory/vcnl36863_prox.c b/drivers/adsp_factory/vcnl36863_prox.c new file mode 100644 index 000000000000..96187d141139 --- /dev/null +++ b/drivers/adsp_factory/vcnl36863_prox.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include "adsp.h" +#define VENDOR "CAPELLA" +#define CHIP_ID "VCNL36863" + +#define PROX_AVG_COUNT 40 +#define PROX_ALERT_THRESHOLD 200 +#define PROX_TH_READ 0 +#define PROX_TH_WRITE 1 +#define BUFFER_MAX 128 +#define PROX_REG_START 0x80 +#define PROX_DETECT_HIGH_TH 16368 +#define PROX_DETECT_LOW_TH 1000 + +extern unsigned int system_rev; + +struct vcnl36863_prox_data { + struct hrtimer prox_timer; + struct work_struct work_prox; + struct workqueue_struct *prox_wq; + int min; + int max; + int avg; + int val; + int offset; + int reg_backup[2]; + short avgwork_check; + short avgtimer_enabled; + short bBarcodeEnabled; +}; + +enum { + PRX_THRESHOLD_DETECT_H, + PRX_THRESHOLD_HIGH_DETECT_L, + PRX_THRESHOLD_HIGH_DETECT_H, + PRX_THRESHOLD_RELEASE_L, +}; + +static struct vcnl36863_prox_data *pdata; + +static ssize_t prox_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t prox_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t prox_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (pdata->avgwork_check == 0) + get_prox_raw_data(&pdata->val, &pdata->offset); + + return snprintf(buf, PAGE_SIZE, "%d\n", pdata->val); +} + +static ssize_t prox_avg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", pdata->min, + pdata->avg, pdata->max); +} + +static ssize_t prox_avg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int new_value; + + if (sysfs_streq(buf, "0")) + new_value = 0; + else + new_value = 1; + + if (new_value == pdata->avgtimer_enabled) + return size; + + if (new_value == 0) { + pdata->avgtimer_enabled = 0; + hrtimer_cancel(&pdata->prox_timer); + cancel_work_sync(&pdata->work_prox); + } else { + pdata->avgtimer_enabled = 1; + hrtimer_start(&pdata->prox_timer, + ns_to_ktime(2000 * NSEC_PER_MSEC), + HRTIMER_MODE_REL); + } + + return size; +} + +static void prox_work_func(struct work_struct *work) +{ + int min = 0, max = 0, avg = 0; + int i; + + pdata->avgwork_check = 1; + for (i = 0; i < PROX_AVG_COUNT; i++) { + msleep(20); + + get_prox_raw_data(&pdata->val, &pdata->offset); + avg += pdata->val; + + if (!i) + min = pdata->val; + else if (pdata->val < min) + min = pdata->val; + + if (pdata->val > max) + max = pdata->val; + } + avg /= PROX_AVG_COUNT; + + pdata->min = min; + pdata->avg = avg; + pdata->max = max; + pdata->avgwork_check = 0; +} + +static enum hrtimer_restart prox_timer_func(struct hrtimer *timer) +{ + queue_work(pdata->prox_wq, &pdata->work_prox); + hrtimer_forward_now(&pdata->prox_timer, + ns_to_ktime(2000 * NSEC_PER_MSEC)); + return HRTIMER_RESTART; +} + +int get_prox_threshold(struct adsp_data *data, int type) +{ + uint8_t cnt = 0; + int32_t msg_buf[2]; + int ret = 0; + + msg_buf[0] = type; + msg_buf[1] = 0; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_GET_THRESHOLD); + + while (!(data->ready_flag[MSG_TYPE_GET_THRESHOLD] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(2000, 2050); + + data->ready_flag[MSG_TYPE_GET_THRESHOLD] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + mutex_unlock(&data->prox_factory_mutex); + return ret; + } + + ret = data->msg_buf[MSG_PROX][0]; + mutex_unlock(&data->prox_factory_mutex); + + return ret; +} + +void set_prox_threshold(struct adsp_data *data, int type, int val) +{ + uint8_t cnt = 0; + int32_t msg_buf[2]; + + msg_buf[0] = type; + msg_buf[1] = val; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_SET_THRESHOLD); + + while (!(data->ready_flag[MSG_TYPE_SET_THRESHOLD] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_SET_THRESHOLD] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + mutex_unlock(&data->prox_factory_mutex); +} + +static ssize_t prox_cancel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int hi_thd, low_thd; + + hi_thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H); + low_thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L); + + if (pdata->avgwork_check == 0) + get_prox_raw_data(&pdata->val, &pdata->offset); + + pr_info("[FACTORY] %s: offset: %d, hi thd: %d, lo thd: %d\n", __func__, + pdata->offset, hi_thd, low_thd); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + pdata->offset, hi_thd, low_thd); +} + +static ssize_t prox_cancel_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + pr_info("[FACTORY] %s: skip\n", __func__); + + return size; +} + +static ssize_t prox_thresh_high_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_thresh_high_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_DETECT_H, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} + +static ssize_t prox_thresh_low_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_thresh_low_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_RELEASE_L, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} + +#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL +static ssize_t prox_high_thresh_high_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_high_thresh_high_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} + +static ssize_t prox_high_thresh_low_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd; + + thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return snprintf(buf, PAGE_SIZE, "%d\n", thd); +} + +static ssize_t prox_high_thresh_low_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int thd = 0; + + if (kstrtoint(buf, 10, &thd)) { + pr_err("[FACTORY] %s: kstrtoint fail\n", __func__); + return size; + } + + set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L, thd); + pr_info("[FACTORY] %s: %d\n", __func__, thd); + + return size; +} +#endif + +static ssize_t barcode_emul_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", pdata->bBarcodeEnabled); +} + +static ssize_t barcode_emul_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iRet; + int64_t dEnable; + + iRet = kstrtoll(buf, 10, &dEnable); + if (iRet < 0) + return iRet; + + if (dEnable) + pdata->bBarcodeEnabled = 1; + else + pdata->bBarcodeEnabled = 0; + + return size; +} + +static ssize_t prox_cancel_pass_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "1\n"); +} + +static ssize_t prox_default_trim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", pdata->offset); +} + +static ssize_t prox_alert_thresh_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", PROX_ALERT_THRESHOLD); +} + +static ssize_t prox_register_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int cnt = 0; + int32_t msg_buf[1]; + + msg_buf[0] = pdata->reg_backup[0]; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_GET_REGISTER); + + while (!(data->ready_flag[MSG_TYPE_GET_REGISTER] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_REGISTER] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pdata->reg_backup[1] = data->msg_buf[MSG_PROX][0]; + pr_info("[FACTORY] %s: [0x%x]: 0x%x\n", + __func__, pdata->reg_backup[0], pdata->reg_backup[1]); + + mutex_unlock(&data->prox_factory_mutex); + + return snprintf(buf, PAGE_SIZE, "[0x%x]: 0x%x\n", + pdata->reg_backup[0], pdata->reg_backup[1]); +} + +static ssize_t prox_register_read_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int reg = 0; + + if (sscanf(buf, "%4x", ®) != 1) { + pr_err("[FACTORY]: %s - The number of data are wrong\n", + __func__); + return -EINVAL; + } + + pdata->reg_backup[0] = reg; + pr_info("[FACTORY] %s: [0x%x]\n", __func__, pdata->reg_backup[0]); + + return size; +} + +static ssize_t prox_register_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adsp_data *data = dev_get_drvdata(dev); + int cnt = 0; + int32_t msg_buf[2]; + + if (sscanf(buf, "%2x,%4x", &msg_buf[0], &msg_buf[1]) != 2) { + pr_err("[FACTORY]: %s - The number of data are wrong\n", + __func__); + return -EINVAL; + } + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(msg_buf, sizeof(msg_buf), + MSG_PROX, 0, MSG_TYPE_SET_REGISTER); + + while (!(data->ready_flag[MSG_TYPE_SET_REGISTER] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_SET_REGISTER] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pdata->reg_backup[0] = msg_buf[0]; + pr_info("[FACTORY] %s: 0x%x - 0x%x\n", + __func__, msg_buf[0], data->msg_buf[MSG_PROX][0]); + mutex_unlock(&data->prox_factory_mutex); + + return size; +} + +static ssize_t prox_light_get_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + int offset = 0; + int32_t *info = data->msg_buf[MSG_PROX]; + + mutex_lock(&data->prox_factory_mutex); + adsp_unicast(NULL, 0, MSG_PROX, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_PROX) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_PROX); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + pr_info("[FACTORY] %d,%d,%d,%d,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%d\n", + info[0], info[1], info[2], info[3], info[4], info[5], + info[6], info[7], info[8], info[9], info[10], info[11]); + + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"THD\":\"%d %d %d %d\",", info[0], info[1], info[2], info[3]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PDRIVE_CURRENT\":\"%02x\",", info[4]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PERSIST_TIME\":\"%02x\",", info[5]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PPULSE\":\"%02x\",", info[6]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PGAIN\":\"%02x\",", info[7]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PTIME\":\"%02x\",", info[8]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"PPLUSE_LEN\":\"%02x\",", info[9]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"ATIME\":\"%02x\",", info[10]); + offset += snprintf(buf + offset, PAGE_SIZE - offset, + "\"POFFSET\":\"%d\"\n", info[11]); + + mutex_unlock(&data->prox_factory_mutex); + return offset; +} + +static DEVICE_ATTR(vendor, 0444, prox_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, prox_name_show, NULL); +static DEVICE_ATTR(state, 0444, prox_raw_data_show, NULL); +static DEVICE_ATTR(raw_data, 0444, prox_raw_data_show, NULL); +static DEVICE_ATTR(prox_avg, 0664, + prox_avg_show, prox_avg_store); +static DEVICE_ATTR(prox_cal, 0664, + prox_cancel_show, prox_cancel_store); +static DEVICE_ATTR(thresh_high, 0664, + prox_thresh_high_show, prox_thresh_high_store); +static DEVICE_ATTR(thresh_low, 0664, + prox_thresh_low_show, prox_thresh_low_store); +#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL +static DEVICE_ATTR(high_thresh_high, 0664, + prox_high_thresh_high_show, prox_high_thresh_high_store); +static DEVICE_ATTR(high_thresh_low, 0664, + prox_high_thresh_low_show, prox_high_thresh_low_store); +#endif +static DEVICE_ATTR(register_write, 0220, + NULL, prox_register_write_store); +static DEVICE_ATTR(register_read, 0664, + prox_register_read_show, prox_register_read_store); +static DEVICE_ATTR(barcode_emul_en, 0664, + barcode_emul_enable_show, barcode_emul_enable_store); +static DEVICE_ATTR(prox_offset_pass, 0444, prox_cancel_pass_show, NULL); +static DEVICE_ATTR(prox_trim, 0444, prox_default_trim_show, NULL); + +static DEVICE_ATTR(prox_alert_thresh, 0444, prox_alert_thresh_show, NULL); +static DEVICE_ATTR(dhr_sensor_info, 0440, + prox_light_get_dhr_sensor_info_show, NULL); + +static struct device_attribute *prox_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_state, + &dev_attr_raw_data, + &dev_attr_prox_avg, + &dev_attr_prox_cal, + &dev_attr_thresh_high, + &dev_attr_thresh_low, +#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL + &dev_attr_high_thresh_high, + &dev_attr_high_thresh_low, +#endif + &dev_attr_barcode_emul_en, + &dev_attr_prox_offset_pass, + &dev_attr_prox_trim, + &dev_attr_prox_alert_thresh, + &dev_attr_dhr_sensor_info, + &dev_attr_register_write, + &dev_attr_register_read, + NULL, +}; + +static int __init vcnl36863_prox_factory_init(void) +{ + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + adsp_factory_register(MSG_PROX, prox_attrs); + pr_info("[FACTORY] %s\n", __func__); + + hrtimer_init(&pdata->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pdata->prox_timer.function = prox_timer_func; + pdata->prox_wq = create_singlethread_workqueue("prox_wq"); + + /* this is the thread function we run on the work queue */ + INIT_WORK(&pdata->work_prox, prox_work_func); + + pdata->avgwork_check = 0; + pdata->avgtimer_enabled = 0; + pdata->avg = 0; + pdata->min = 0; + pdata->max = 0; + pdata->offset = 0; + return 0; +} + +static void __exit vcnl36863_prox_factory_exit(void) +{ + if (pdata->avgtimer_enabled == 1) { + hrtimer_cancel(&pdata->prox_timer); + cancel_work_sync(&pdata->work_prox); + } + destroy_workqueue(pdata->prox_wq); + adsp_factory_unregister(MSG_PROX); + kfree(pdata); + pr_info("[FACTORY] %s\n", __func__); +} + +module_init(vcnl36863_prox_factory_init); +module_exit(vcnl36863_prox_factory_exit); diff --git a/drivers/adsp_factory/veml3328_light.c b/drivers/adsp_factory/veml3328_light.c new file mode 100644 index 000000000000..93b799581fef --- /dev/null +++ b/drivers/adsp_factory/veml3328_light.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include "adsp.h" +#define VENDOR "CAPELLA" +#define CHIP_ID "VEML3328" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ +static ssize_t light_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t light_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t light_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + mutex_lock(&data->light_factory_mutex); + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_RAW_DATA); + + while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) { + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + mutex_unlock(&data->light_factory_mutex); + return snprintf(buf, PAGE_SIZE, "0,0,0,0,0,0\n"); + } + + mutex_unlock(&data->light_factory_mutex); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n", + data->msg_buf[MSG_LIGHT][0], data->msg_buf[MSG_LIGHT][1], + data->msg_buf[MSG_LIGHT][2], data->msg_buf[MSG_LIGHT][3], + data->msg_buf[MSG_LIGHT][4], data->msg_buf[MSG_LIGHT][5]); +} + +static ssize_t light_get_dhr_sensor_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adsp_data *data = dev_get_drvdata(dev); + uint8_t cnt = 0; + + pr_info("[FACTORY] %s: start\n", __func__); + mutex_lock(&data->light_factory_mutex); + adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO); + while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) && + cnt++ < TIMEOUT_CNT) + usleep_range(500, 550); + + data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT); + + if (cnt >= TIMEOUT_CNT) + pr_err("[FACTORY] %s: Timeout!!!\n", __func__); + + mutex_unlock(&data->light_factory_mutex); + return data->msg_buf[MSG_LIGHT][0]; +} + +static DEVICE_ATTR(vendor, 0444, light_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, light_name_show, NULL); +static DEVICE_ATTR(lux, 0444, light_raw_data_show, NULL); +static DEVICE_ATTR(raw_data, 0444, light_raw_data_show, NULL); +static DEVICE_ATTR(dhr_sensor_info, 0444, light_get_dhr_sensor_info_show, NULL); + +static struct device_attribute *light_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_lux, + &dev_attr_raw_data, + &dev_attr_dhr_sensor_info, + NULL, +}; + +static int __init veml3328_light_factory_init(void) +{ + adsp_factory_register(MSG_LIGHT, light_attrs); + + pr_info("[FACTORY] %s\n", __func__); + + return 0; +} + +static void __exit veml3328_light_factory_exit(void) +{ + adsp_factory_unregister(MSG_LIGHT); + + pr_info("[FACTORY] %s\n", __func__); +} +module_init(veml3328_light_factory_init); +module_exit(veml3328_light_factory_exit); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 813a191febd8..eb8922638b02 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -297,7 +297,9 @@ static const char * const fw_path[] = { "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, - "/lib/firmware" + "/lib/firmware", + "/firmware/image", + "/firmware-modem/image" }; /* diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 39676b3f3fb9..652195196cc6 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -21,6 +21,9 @@ #include #include "power.h" +#ifdef CONFIG_SEC_PM +#include +#endif /* * If set, the suspend/hibernate code will abort transitions to a sleep state @@ -663,6 +666,12 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) ws->total_time = ktime_add(ws->total_time, duration); if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) ws->max_time = duration; +#ifdef CONFIG_SEC_PM_DEBUG + if (ktime_to_ms(duration) >= 10000LL) { + pr_info("PM: unlock %s(%lld ms)\n", + ws->name, ktime_to_ms(duration)); + } +#endif ws->last_time = now; del_timer(&ws->timer); @@ -928,8 +937,12 @@ void pm_system_irq_wakeup(unsigned int irq_number) else if (desc->action && desc->action->name) name = desc->action->name; +#ifdef CONFIG_SEC_PM + log_wakeup_reason(irq_number); +#else pr_warn("%s: %d triggered %s\n", __func__, irq_number, name); +#endif } pm_wakeup_irq = irq_number; @@ -1078,6 +1091,62 @@ static int print_wakeup_source_stats(struct seq_file *m, return 0; } +#ifdef CONFIG_SEC_PM_DEBUG +static int print_wakeup_source_active( + struct wakeup_source *ws) +{ + unsigned long flags; + ktime_t total_time; + unsigned long active_count; + ktime_t active_time; + ktime_t prevent_sleep_time; + int ret; + + spin_lock_irqsave(&ws->lock, flags); + + total_time = ws->total_time; + prevent_sleep_time = ws->prevent_sleep_time; + active_count = ws->active_count; + if (ws->active) { + ktime_t now = ktime_get(); + + active_time = ktime_sub(now, ws->last_time); + total_time = ktime_add(total_time, active_time); + + if (ws->autosleep_enabled) + prevent_sleep_time = ktime_add(prevent_sleep_time, + ktime_sub(now, ws->start_prevent_time)); + } else { + active_time = ktime_set(0, 0); + } + + ret = pr_info("<%s>\tCount(%lu) Time(%lld/%lld) Prevent(%lld)\n", + ws->name, active_count, + ktime_to_ms(active_time), ktime_to_ms(total_time), + ktime_to_ms(prevent_sleep_time)); + + spin_unlock_irqrestore(&ws->lock, flags); + + return ret; +} + +int wakeup_sources_stats_active(void) +{ + struct wakeup_source *ws; + + pr_info("Active wake lock:\n"); + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) + if (ws->active) + print_wakeup_source_active(ws); + rcu_read_unlock(); + + return 0; +} +EXPORT_SYMBOL_GPL(wakeup_sources_stats_active); +#endif + /** * wakeup_sources_stats_show - Print wakeup sources statistics information. * @m: seq_file to print the statistics into. diff --git a/drivers/battery_v2/Kconfig b/drivers/battery_v2/Kconfig new file mode 100644 index 000000000000..a357dba5278f --- /dev/null +++ b/drivers/battery_v2/Kconfig @@ -0,0 +1,282 @@ + +config BATTERY_SAMSUNG + tristate "samsung battery driver" + help + Say Y to include support for samsung battery driver + This battery driver integrated all battery-related functions + To see battery-related functions, + refer to sec_charging_common.h + +config BATTERY_SAMSUNG_V2 + tristate "samsung battery driver version 2" + default n + depends on BATTERY_SAMSUNG + help + Say Y to include support for samsung battery driver + This battery driver integrated all battery-related functions + To see battery-related functions, + refer to sec_charging_common.h + +config BATTERY_SAMSUNG_DATA_FILE + depends on BATTERY_SAMSUNG + string "samsung battery data file" + default "default_battery_data.h" + help + Path to the battery data file. + +config SLOW_CHARGING_CURRENT_STANDARD + int "slow charging" + depends on BATTERY_SAMSUNG + default "1000" + help + Value for standard of slow-charging. + +config CHARGING_VZWCONCEPT + tristate "VZW concept about the charging" + default n + depends on BATTERY_SAMSUNG + help + Say Y to include support for the VZW concepts. + +config BATTERY_SWELLING + bool "prevent battery swelling" + help + Say Y to include support for prevent battery swelling + +config BATTERY_SWELLING_SELF_DISCHARGING + bool "prevent battery swelling with self discharging" + help + Say Y to include support for prevent battery swelling with self discharging + +config CALC_TIME_TO_FULL + tristate "calculate time to full" + default n + depends on BATTERY_SAMSUNG + help + Say Y to use calc time to full function. + +config MULTI_CHARGING + bool "support for multi charger ICs" + help + Say Y to include support for multi charger ICs + +config STEP_CHARGING + bool "support for step charging" + help + Say Y to include support for step charging + +config UPDATE_BATTERY_DATA + bool "support for updating battery data" + default n + depends on BATTERY_SAMSUNG && OF + help + Say Y to include support for step charging + +config AFC_CURR_CONTROL_BY_TEMP + tristate "fast charging current control by temp" + default n + depends on BATTERY_SAMSUNG + help + Say Y to set afc current control by temp + + config BATTERY_CISD + bool "support for cisd" + help + Say Y to include support for cisd + cisd means cell internal short detection + + config DUMMY_BATTERY + bool "support for dummy battery" + help + Say Y to include support for dummy battery + for dummy battery recognition + +# Fuel Gauge +config FUELGAUGE_DUMMY + tristate "dummy fuel gauge driver" + default n + depends on BATTERY_SAMSUNG + help + Say Y to include support + for dummy fuel gauge driver. + This driver source code implemented + skeleton source code for fuel gauge functions. + +config PREVENT_SOC_JUMP + tristate "prevent soc jump at full-charged" + default n + depends on BATTERY_SAMSUNG + help + Say Y to prevent soc jump + +config FUELGAUGE_SM5705 + tristate "Siliconmitus SM5705 Fuel Gauge" + default n + depends on I2C + help + SM5705 is fuel-gauge systems for lithium-ion (Li+) batteries + in handheld and portable equipment. The SM5705 is configured + to operate with a single lithium cell + +# Charger +config CHARGER_DUMMY + tristate "dummy charger driver" + default n + depends on BATTERY_SAMSUNG + help + Say Y to include support + for dummy charger driver. + This driver source code implemented + skeleton source code for charger functions. + +config CHARGER_SM5705 + tristate "SM5705 battery charger support" + depends on BATTERY_SAMSUNG + help + Say Y here to enable support for the SM5705 charger + +config CS100_JPNCONCEPT + tristate "cs100 command support" + depends on BATTERY_SAMSUNG && I2C + help + Say Y here to support for CS100 command to stop when full-charged + on wireless charging for JPN models + +config AFC_CHARGER_MODE + bool "afc charging support in sec battery driver" + default n + help + Say Y to include support for sec afc charging support + +config SAMSUNG_LPM_MODE + bool "Off charging mode support in sec battery driver" + default n + help + Say Y to include support for sec off charging support + This value defined at bootloader. + Before enable this feature, + implemet power off charging in the bootloader. + +config SAMSUNG_BATTERY_ENG_TEST + bool "enable ENG mode for battery test" + default n + help + Say Y to include support for battery test + enable this feature only ENG mode + this featuren must disabled user binary + stability test etc.. + +config SAMSUNG_BATTERY_FACTORY + bool "enable for factory test" + default n + help + Say Y to include support for factory test + enable this feature only factory mode + this featuren must disabled user binary + stability test etc.. + +config SAMSUNG_BATTERY_DISALLOW_DEEP_SLEEP + bool "Disallow deep sleep during charging" + default n + depends on BATTERY_SAMSUNG && (ARCH_MSM8974 || ARCH_APQ8084) + help + Say Y to include support + Disallow deep sleep during charging for stablity. + +config DISABLE_SAVE_CAPACITY_MAX + bool "Disable to save capacity max in efs" + default n + depends on BATTERY_SAMSUNG + help + Say Y to include support to disable it + capacity_max is saved in /efs/Battery/batt_capacity_max + capacity_max is restored after boot up + You can find the code in healthd + +config SIOP_CHARGING_LIMIT_CURRENT + int "Siop charging limit current" + default 0 + help + set SIOP charging limit current. + +config EN_OOPS + bool "enable oops filter" + default n + help + Say Y to enable oops filter + +config USE_POGO + bool "enable pogo charging" + default n + help + Say Y to enable CONFIG_USE_POGO + +config MACH_KOR_EARJACK_WR + bool "enable earjack-noise workaround" + default n + depends on BATTERY_SAMSUNG + help + Say Y to enable earjack-noise workaround at charging + +config STORE_MODE + bool "enable store mode" + default n + help + Say Y to enable CONFIG_STORE_MODE + +config SW_SELF_DISCHARGING + bool "enable sw_self_discharging" + default n + help + Say Y to enable CONFIG_SW_SELF_DISCHARGING + +config BATTERY_AGE_FORECAST + tristate "battery age forecast" + default n + depends on BATTERY_SWELLING + help + Say Y to use calc time to full function. + +config BATTERY_AGE_FORECAST_DETACHABLE + tristate "battery age forecast for detachable" + default n + select BATTERY_AGE_FORECAST + help + Say Y to use battery age forecast for detachable + +config ENG_BATTERY_CONCEPT + bool "enable temp block" + default n + help + Say Y to enable CONFIG_ENG_BATTERY_CONCEPT + +config QH_ALGORITHM + bool "enable QH algorithm" + default n + help + Say Y to enable CONFIG_QH_ALGORITHM to measure leakges of the battery + using raw coulomb count generated by the device + +config BATTERY_NOTIFIER + bool "battery notifier" + default n + help + Say Y to enable battery notifier + +config LIMIT_CHARGING_DURING_CALL + bool "limit charging during call" + default n + help + Say Y to limit charging during call + +config ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED + bool "enable 100mA before usb configured" + default n + help + Say Y to enable 100mA before usb configured + +config TABLET_MODEL_CONCEPT + bool "tablet model concept" + default n + help + Say Y to enable tablet model concept diff --git a/drivers/battery_v2/Makefile b/drivers/battery_v2/Makefile new file mode 100644 index 000000000000..704b3b249e05 --- /dev/null +++ b/drivers/battery_v2/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_OF) += sec_adc.o +obj-$(CONFIG_MULTI_CHARGING) += sec_multi_charger.o +obj-$(CONFIG_STEP_CHARGING) += sec_step_charging.o +obj-$(CONFIG_BATTERY_CISD) += sec_cisd.o +obj-$(CONFIG_UPDATE_BATTERY_DATA) += sec_battery_data.o +obj-$(CONFIG_BATTERY_NOTIFIER) += battery_notifier.o + +obj-$(CONFIG_FUELGAUGE_SM5705) += sm5705_fuelgauge.o + +obj-$(CONFIG_CHARGER_SM5705) += sm5705_charger.o sm5705_charger_oper.o + +obj-$(CONFIG_BATTERY_SAMSUNG) += sec_battery.o +obj-$(CONFIG_BATTERY_SAMSUNG) += sec_battery_ttf.o diff --git a/drivers/battery_v2/battery_notifier.c b/drivers/battery_v2/battery_notifier.c new file mode 100644 index 000000000000..7ce30dd6c8fc --- /dev/null +++ b/drivers/battery_v2/battery_notifier.c @@ -0,0 +1,205 @@ +#include + +#include +#include +#include +//#include + +#define DEBUG +#define SET_BATTERY_NOTIFIER_BLOCK(nb, fn, dev) do { \ + (nb)->notifier_call = (fn); \ + (nb)->priority = (dev); \ + } while (0) + +#define DESTROY_BATTERY_NOTIFIER_BLOCK(nb) \ + SET_BATTERY_NOTIFIER_BLOCK(nb, NULL, -1) + +static struct charger_notifier_struct charger_notifier; +static struct pdic_notifier_struct pdic_notifier; + +struct device *charger_device; +struct device *pdic_device; + +int charger_notifier_register(struct notifier_block *nb, notifier_fn_t notifier, + charger_notifier_device_t listener) +{ + int ret = 0; + + pr_info("%s: listener=%d register\n", __func__, listener); + + /* Check if CHARGER Notifier is ready. */ + if (!charger_device) { + pr_err("%s: Not Initialized...\n", __func__); + return -1; + } + + SET_BATTERY_NOTIFIER_BLOCK(nb, notifier, listener); + ret = blocking_notifier_chain_register(&(charger_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_register error(%d)\n", + __func__, ret); + + /* current charger's attached_device status notify */ + nb->notifier_call(nb, charger_notifier.event, + &(charger_notifier)); + + return ret; +} + +int charger_notifier_unregister(struct notifier_block *nb) +{ + int ret = 0; + + pr_info("%s: listener=%d unregister\n", __func__, nb->priority); + + ret = blocking_notifier_chain_unregister(&(charger_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_BATTERY_NOTIFIER_BLOCK(nb); + + return ret; +} + +int pdic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier, + pdic_notifier_device_t listener) +{ + int ret = 0; + + pr_info("%s: listener=%d register\n", __func__, listener); + + /* Check if CHARGER Notifier is ready. */ + if (!pdic_device) { + pr_err("%s: Not Initialized...\n", __func__); + return -1; + } + + SET_BATTERY_NOTIFIER_BLOCK(nb, notifier, listener); + ret = blocking_notifier_chain_register(&(pdic_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_register error(%d)\n", + __func__, ret); + + /* current pdic's attached_device status notify */ + nb->notifier_call(nb, pdic_notifier.event, + &(pdic_notifier)); + + return ret; +} + +int pdic_notifier_unregister(struct notifier_block *nb) +{ + int ret = 0; + + pr_info("%s: listener=%d unregister\n", __func__, nb->priority); + + ret = blocking_notifier_chain_unregister(&(pdic_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_BATTERY_NOTIFIER_BLOCK(nb); + + return ret; +} + +static int battery_notifier_notify(int type) +{ + int ret = 0; + + switch (type) { + case CHARGER_NOTIFY: + ret = blocking_notifier_call_chain(&(charger_notifier.notifier_call_chain), + charger_notifier.event, &(charger_notifier)); + break; + case PDIC_NOTIFY: + ret = blocking_notifier_call_chain(&(pdic_notifier.notifier_call_chain), + pdic_notifier.event, &(pdic_notifier)); + break; + default: + pr_info("%s: notify status unknown(0x%x)\n", __func__, ret); + break; + } + + switch (ret) { + case NOTIFY_STOP_MASK: + case NOTIFY_BAD: + pr_err("%s: notify error occur(0x%x)\n", __func__, ret); + break; + case NOTIFY_DONE: + case NOTIFY_OK: + pr_info("%s: notify done(0x%x)\n", __func__, ret); + break; + default: + pr_info("%s: notify status unknown(0x%x)\n", __func__, ret); + break; + } + + return ret; +} + +static void charger_notifier_set_property(struct charger_notifier_struct * value) +{ + charger_notifier.event = value->event; + switch(value->event) { + case CHARGER_NOTIFY_EVENT_AICL: + charger_notifier.aicl_status.input_current = value->aicl_status.input_current; + break; + default: + break; + } +} + +void charger_notifier_call(struct charger_notifier_struct *value) +{ + /* charger's event broadcast */ + pr_info("%s: CHARGER_NOTIFY_EVENT :%d\n", __func__, value->event); + charger_notifier_set_property(value); + battery_notifier_notify(CHARGER_NOTIFY); +} + +static void pdic_notifier_set_property(struct pdic_notifier_struct *value) +{ + pdic_notifier.event = value->event; + switch(value->event) { + case PDIC_NOTIFY_EVENT_PD_SINK: + pdic_notifier.sink_status = value->sink_status; + break; + default: + break; + } +} + +void pdic_notifier_call(struct pdic_notifier_struct *value) +{ + /* pdic's event broadcast */ + pdic_notifier_set_property(value); + battery_notifier_notify(PDIC_NOTIFY); +} + +int battery_notifier_init(void) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + charger_device = sec_device_create(0, NULL, "charger_notifier"); + pdic_device = sec_device_create(0, NULL, "pdic_notifier"); + if (IS_ERR(charger_device)) { + pr_err("%s Failed to create device(charer_notifier)!\n", __func__); + ret = -ENODEV; + goto out; + } + if (IS_ERR(pdic_device)) { + pr_err("%s Failed to create device(pdic_notifier)!\n", __func__); + ret = -ENODEV; + goto out; + } + + BLOCKING_INIT_NOTIFIER_HEAD(&(charger_notifier.notifier_call_chain)); + BLOCKING_INIT_NOTIFIER_HEAD(&(pdic_notifier.notifier_call_chain)); + +out: + return ret; +} +device_initcall(battery_notifier_init); + diff --git a/drivers/battery_v2/include/charger/sm5705_charger.h b/drivers/battery_v2/include/charger/sm5705_charger.h new file mode 100644 index 000000000000..eb6e51772413 --- /dev/null +++ b/drivers/battery_v2/include/charger/sm5705_charger.h @@ -0,0 +1,135 @@ +/* + * sm5705_charger.h + * Samsung SM5705 Charger Header + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __SM5705_CHARGER_H +#define __SM5705_CHARGER_H __FILE__ +#include +#include +#include +#include "include/sec_charging_common.h" +/* CONFIG: Kernel Feature & Target System configuration */ +//#define SM5705_SUPPORT_AICL_CONTROL - New A series dosen't support, It's MUST be disabled +#define SM5705_SUPPORT_OTG_CONTROL //- New A series dosen't support, It's MUST be disabled +#if defined(CONFIG_USE_POGO) +#define SM5705_STATUS1_WPCINPOK (1 << 4) +#endif + +enum { + CHIP_ID = 0, + CHARGER_OP_MODE=1, + DATA, +}; +ssize_t sm5705_chg_show_attrs(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t sm5705_chg_store_attrs(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +#define SM5705_CHARGER_ATTR(_name) \ +{ \ + .attr = {.name = #_name, .mode = 0664}, \ + .show = sm5705_chg_show_attrs, \ + .store = sm5705_chg_store_attrs, \ +} +enum { + SM5705_MANUAL_RESET_TIME_7s = 0x1, + SM5705_MANUAL_RESET_TIME_8s = 0x2, + SM5705_MANUAL_RESET_TIME_9s = 0x3, +}; +enum { + SM5705_WATCHDOG_RESET_TIME_30s = 0x0, + SM5705_WATCHDOG_RESET_TIME_60s = 0x1, + SM5705_WATCHDOG_RESET_TIME_90s = 0x2, + SM5705_WATCHDOG_RESET_TIME_120s = 0x3, +}; +enum { + SM5705_TOPOFF_TIMER_10m = 0x0, + SM5705_TOPOFF_TIMER_20m = 0x1, + SM5705_TOPOFF_TIMER_30m = 0x2, + SM5705_TOPOFF_TIMER_45m = 0x3, +}; +enum { + SM5705_BUCK_BOOST_FREQ_3MHz = 0x0, + SM5705_BUCK_BOOST_FREQ_2_4MHz = 0x1, + SM5705_BUCK_BOOST_FREQ_1_5MHz = 0x2, + SM5705_BUCK_BOOST_FREQ_1_8MHz = 0x3, +}; +/* for VZW support */ +#if defined(CONFIG_TABLET_MODEL_CONCEPT) && !defined(CONFIG_SEC_FACTORY) +#define SLOW_CHARGING_CURRENT_STANDARD 1000 +#else +#define SLOW_CHARGING_CURRENT_STANDARD 400 +#endif +/* SM5705 Charger - AICL reduce current configuration */ +#define REDUCE_CURRENT_STEP 100 +#define MINIMUM_INPUT_CURRENT 300 +#define AICL_VALID_CHECK_DELAY_TIME 10 +#define SM5705_EN_DISCHG_FORCE_MASK 0x02 +#define SM5705_SBPS_MASK 0x07 +struct sm5705_charger_data { + struct device *dev; + struct i2c_client *i2c; + struct sec_charger_platform_data *pdata; + struct power_supply *psy_chg; + struct power_supply *psy_otg; + /* for IRQ-service handling */ + int irq_aicl; + int irq_vbus_pok; + int irq_wpcin_pok; +#if defined(CONFIG_USE_POGO) + int irq_wpcin_pok_pogo; + int irq_wpcin_uvlo_pogo; +#endif + int irq_topoff; + int irq_done; + int irq_otgfail; + /* for Workqueue & wake-lock, mutex process */ + struct mutex charger_mutex; + struct workqueue_struct *wqueue; + struct delayed_work wpc_work; + struct delayed_work slow_chg_work; + struct delayed_work aicl_work; +#if defined(CONFIG_USE_POGO) + struct delayed_work pogo_work; +#endif + struct delayed_work topoff_work; + // temp for rev2 SW WA + struct delayed_work op_mode_switch_work; /* for WA obnormal switch case in JIG cable */ + struct wake_lock wpc_wake_lock; + struct wake_lock afc_wake_lock; +#if defined(SM5705_SW_SOFT_START) + struct wake_lock softstart_wake_lock; +#endif + struct wake_lock check_slow_wake_lock; + struct wake_lock aicl_wake_lock; + /* for charging operation handling */ + int status; + int charge_mode; + unsigned int is_charging; + unsigned int cable_type; + unsigned int input_current; + unsigned int charging_current; + int irq_wpcin_state; + int aicl_on; + bool topoff_pending; + // temp for rev2 SW WA + bool is_rev2_wa_done; + bool slow_late_chg_mode; +}; +extern int sm5705_call_fg_device_id(void); +#if defined(SM5705_WATCHDOG_RESET_ACTIVATE) +extern void sm5705_charger_watchdog_timer_keepalive(void); +#endif +#endif /* __SM5705_CHARGER_H */ diff --git a/drivers/battery_v2/include/charger/sm5705_charger_oper.h b/drivers/battery_v2/include/charger/sm5705_charger_oper.h new file mode 100644 index 000000000000..edbe5dec13f7 --- /dev/null +++ b/drivers/battery_v2/include/charger/sm5705_charger_oper.h @@ -0,0 +1,45 @@ +/* + * drivers/battery/sm5705_charger_oper.h + * + * SM5705 Charger Operation Mode controller + * + * Copyright (C) 2015 Siliconmitus Technology Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ +#include +#include + +enum { + SM5705_CHARGER_OP_MODE_SUSPEND = 0x0, + SM5705_CHARGER_OP_MODE_FACTORY = 0x1, + SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_OFF = 0x2, + SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON = 0x3, + SM5705_CHARGER_OP_MODE_CHG_OFF = 0x4, + SM5705_CHARGER_OP_MODE_CHG_ON = 0x5, + SM5705_CHARGER_OP_MODE_FLASH_BOOST = 0x6, + SM5705_CHARGER_OP_MODE_USB_OTG = 0x7, +}; +enum SM5705_CHARGER_OP_EVENT_TYPE { + SM5705_CHARGER_OP_EVENT_SUSPEND_MODE = 0x7, + SM5705_CHARGER_OP_EVENT_VBUS = 0x5, + SM5705_CHARGER_OP_EVENT_WPC = 0x4, + SM5705_CHARGER_OP_EVENT_FLASH = 0x3, + SM5705_CHARGER_OP_EVENT_TORCH = 0x2, + SM5705_CHARGER_OP_EVENT_OTG = 0x1, + SM5705_CHARGER_OP_EVENT_PWR_SHAR = 0x0, +}; +#define make_OP_STATUS(vbus,wpc,flash,torch,otg,pwr_shar) (((vbus & 0x1) << SM5705_CHARGER_OP_EVENT_VBUS) | \ + ((wpc & 0x1) << SM5705_CHARGER_OP_EVENT_WPC) | \ + ((flash & 0x1) << SM5705_CHARGER_OP_EVENT_FLASH) | \ + ((torch & 0x1) << SM5705_CHARGER_OP_EVENT_TORCH) | \ + ((otg & 0x1) << SM5705_CHARGER_OP_EVENT_OTG) | \ + ((pwr_shar & 0x1) << SM5705_CHARGER_OP_EVENT_PWR_SHAR)) +int sm5705_charger_oper_push_event(int event_type, bool enable); +int sm5705_charger_oper_table_init(struct i2c_client *i2c); +int sm5705_charger_oper_get_current_status(void); +int sm5705_charger_oper_get_current_op_mode(void); diff --git a/drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge.h b/drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge.h new file mode 100644 index 000000000000..c3a189045a1a --- /dev/null +++ b/drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge.h @@ -0,0 +1,220 @@ +/* + * drivers/battery/sm5705_fuelgauge.h + * + * Header of SiliconMitus SM5705 Fuelgauge Driver + * + * Copyright (C) 2015 SiliconMitus + * Author: SW Jung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef SM5705_FUELGAUGE_H +#define SM5705_FUELGAUGE_H +#include +//include +#include "include/sec_charging_common.h" +#ifdef CONFIG_DEBUG_FS +#include +#endif /* #ifdef CONFIG_DEBUG_FS */ +#define FG_DRIVER_VER "0.0.0.1" +// #define ENABLE_FULL_OFFSET 1 +/*To differentiate two battery Packs: SDI & ATL*/ +#if defined(CONFIG_BATTERY_AGE_FORECAST) +#define ENABLE_BATT_LONG_LIFE 1 +#endif +enum { + SDI_BATTERY_TYPE = 0, + ATL_BATTERY_TYPE, + UNKNOWN_TYPE +}; +struct battery_data_t { + const int battery_type; /* 4200 or 4350 or 4400*/ + const int battery_table[3][16]; + const int rce_value[3]; + const int dtcd_value; + const int rs_value[4]; + const int vit_period; + const int mix_value[2]; + const int topoff_soc[2]; + const int volt_cal; + const int curr_cal; + const int temp_std; + const int temp_offset; + const int temp_offset_cal; +}; +struct sec_fg_info { + /* Device_id */ + int device_id; + /* State Of Connect */ + int online; + /* battery SOC (capacity) */ + int batt_soc; + /* battery voltage */ + int batt_voltage; + /* battery AvgVoltage */ + int batt_avgvoltage; + /* battery OCV */ + int batt_ocv; + /* Current */ + int batt_current; + /* battery Avg Current */ + int batt_avgcurrent; + /* battery SOC cycle */ + int batt_soc_cycle; + struct battery_data_t *comp_pdata; + struct mutex param_lock; + /* copy from platform data / + * DTS or update by shell script */ + struct mutex io_lock; + struct device *dev; + int32_t temperature;; /* 0.1 deg C*/ + int32_t temp_fg;; /* 0.1 deg C*/ + /* register programming */ + int reg_addr; + u8 reg_data[2]; + int battery_typ; /*SDI_BATTERY_TYPE or ATL_BATTERY_TYPE*/ + int batt_id_adc_check; + int battery_table[3][16]; +#ifdef ENABLE_BATT_LONG_LIFE +#ifdef CONFIG_BATTERY_AGE_FORECAST_DETACHABLE + int v_max_table[3]; + int q_max_table[3]; +#else + int v_max_table[5]; + int q_max_table[5]; +#endif + int v_max_now; + int q_max_now; +#endif + int rce_value[3]; + int dtcd_value; + int rs_value[5]; /*rs p_mix_factor n_mix_factor max min*/ + int vit_period; + int mix_value[2]; /*mix_rate init_blank*/ + int misc; + int enable_topoff_soc; + int topoff_soc; + int top_off; + int cycle_high_limit; + int cycle_low_limit; + int cycle_limit_cntl; + int enable_v_offset_cancel_p; + int enable_v_offset_cancel_n; + int v_offset_cancel_level; + int v_offset_cancel_mohm; + int volt_cal; + int curr_offset; + int p_curr_cal; + int n_curr_cal; + int curr_lcal_en; + int curr_lcal_0; + int curr_lcal_1; + int curr_lcal_2; + int en_auto_curr_offset; + int cntl_value; +#ifdef ENABLE_FULL_OFFSET + int full_offset_margin; + int full_extra_offset; +#endif + int temp_std; + int en_fg_temp_volcal; + int fg_temp_volcal_denom; + int fg_temp_volcal_fact; + int en_high_fg_temp_offset; + int high_fg_temp_offset_denom; + int high_fg_temp_offset_fact; + int en_low_fg_temp_offset; + int low_fg_temp_offset_denom; + int low_fg_temp_offset_fact; + int en_high_fg_temp_cal; + int high_fg_temp_p_cal_denom; + int high_fg_temp_p_cal_fact; + int high_fg_temp_n_cal_denom; + int high_fg_temp_n_cal_fact; + int en_low_fg_temp_cal; + int low_fg_temp_p_cal_denom; + int low_fg_temp_p_cal_fact; + int low_fg_temp_n_cal_denom; + int low_fg_temp_n_cal_fact; + int en_high_temp_cal; + int high_temp_p_cal_denom; + int high_temp_p_cal_fact; + int high_temp_n_cal_denom; + int high_temp_n_cal_fact; + int en_low_temp_cal; + int low_temp_p_cal_denom; + int low_temp_p_cal_fact; + int low_temp_n_cal_denom; + int low_temp_n_cal_fact; + int battery_type; /* 4200 or 4350 or 4400*/ + int data_ver; + uint32_t soc_alert_flag : 1; /* 0 : nu-occur, 1: occur */ + uint32_t volt_alert_flag : 1; /* 0 : nu-occur, 1: occur */ + uint32_t flag_full_charge : 1; /* 0 : no , 1 : yes*/ + uint32_t flag_chg_status : 1; /* 0 : discharging, 1: charging*/ + uint32_t flag_charge_health : 1; /* 0 : no , 1 : good*/ + int32_t irq_ctrl; + int value_v_alarm; + uint32_t is_FG_initialised; + int iocv_error_count; + int n_tem_poff; + int n_tem_poff_offset; + int l_tem_poff; + int l_tem_poff_offset; + /* previous battery voltage current*/ + int p_batt_voltage; + int p_batt_current; +}; +struct sec_fuelgauge_info { + struct i2c_client *client; + sec_battery_platform_data_t *pdata; + struct power_supply *psy_fg; + struct delayed_work isr_work; + int cable_type; + bool is_charging; + bool ta_exist; + /* HW-dedicated fuel guage info structure + * used in individual fuel gauge file only + * (ex. dummy_fuelgauge.c) + */ + struct sec_fg_info info; + bool is_fuel_alerted; + bool volt_alert_flag; + struct wake_lock fuel_alert_wake_lock; + unsigned int capacity_old; /* only for atomic calculation */ + unsigned int capacity_max; /* only for dynamic calculation */ + int raw_capacity; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + unsigned int chg_full_soc; /* BATTERY_AGE_FORECAST */ +#endif + bool initial_update_of_soc; + struct mutex fg_lock; + /* register programming */ + int reg_addr; + u8 reg_data[2]; + int fg_irq; +}; + +ssize_t sm5705_fg_show_attrs(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t sm5705_fg_store_attrs(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +#ifdef CONFIG_OF +extern void board_fuelgauge_init(void *fuelgauge); +extern bool sec_bat_check_jig_status(void); +#endif +#define SM5705_FG_ATTR(_name) \ +{ \ + .attr = {.name = #_name, .mode = 0664}, \ + .show = sm5705_fg_show_attrs, \ + .store = sm5705_fg_store_attrs, \ +} +enum { + FG_REG = 0, + FG_DATA, + FG_REGS, +}; +#endif // SM5705_FUELGAUGE_H diff --git a/drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge_impl.h b/drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge_impl.h new file mode 100644 index 000000000000..608834716a05 --- /dev/null +++ b/drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge_impl.h @@ -0,0 +1,118 @@ +/* + * drivers/battery/sm5705_fuelgauge-impl.h + * + * Header of SiliconMitus SM5705 Fuelgauge Driver Implementation + * + * Copyright (C) 2015 SiliconMitus + * Author: SW Jung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef SM5705_FUELGAUGE_IMPL_H +#define SM5705_FUELGAUGE_IMPL_H +/* Definitions of SM5705 Fuelgauge Registers */ +// I2C Register +#define SM5705_REG_DEVICE_ID 0x00 +#define SM5705_REG_CNTL 0x01 +#define SM5705_REG_INTFG 0x02 +#define SM5705_REG_INTFG_MASK 0x03 +#define SM5705_REG_STATUS 0x04 +#define SM5705_REG_SOC 0x05 +#define SM5705_REG_OCV 0x06 +#define SM5705_REG_VOLTAGE 0x07 +#define SM5705_REG_CURRENT 0x08 +#define SM5705_REG_TEMPERATURE 0x09 +#define SM5705_REG_SOC_CYCLE 0x0A +#define SM5705_REG_V_ALARM 0x0C +#define SM5705_REG_T_ALARM 0x0D +#define SM5705_REG_SOC_ALARM 0x0E +#define SM5705_REG_FG_OP_STATUS 0x10 +#define SM5705_REG_TOPOFFSOC 0x12 +#define SM5705_REG_PARAM_CTRL 0x13 +#define SM5705_REG_PARAM_RUN_UPDATE 0x14 +#define SM5705_REG_SOC_CYCLE_CFG 0x15 +#define SM5705_REG_VIT_PERIOD 0x1A +#define SM5705_REG_MIX_RATE 0x1B +#define SM5705_REG_MIX_INIT_BLANK 0x1C +#define SM5705_REG_RESERVED 0x1F +#define SM5705_REG_RCE0 0x20 +#define SM5705_REG_RCE1 0x21 +#define SM5705_REG_RCE2 0x22 +#define SM5705_REG_DTCD 0x23 +#define SM5705_REG_AUTO_RS_MAN 0x24 +#define SM5705_REG_RS_MIX_FACTOR 0x25 +#define SM5705_REG_RS_MAX 0x26 +#define SM5705_REG_RS_MIN 0x27 +#define SM5705_REG_RS_TUNE 0x28 +#define SM5705_REG_RS_MAN 0x29 +//for cal +#define SM5705_REG_CURR_CAL 0x2C +#define SM5705_REG_IOCV_MAN 0x2E +#define SM5705_REG_END_V_IDX 0x2F +#define SM5705_REG_VOLT_CAL 0x50 +#define SM5705_REG_CURR_OFF 0x51 +#define SM5705_REG_CURR_P_SLOPE 0x52 +#define SM5705_REG_CURR_N_SLOPE 0x53 +#define SM5705_REG_CURRLCAL_0 0x54 +#define SM5705_REG_CURRLCAL_1 0x55 +#define SM5705_REG_CURRLCAL_2 0x56 +//for debug +#define SM5705_REG_OCV_STATE 0x80 +#define SM5705_REG_CURRENT_EST 0x85 +#define SM5705_REG_CURRENT_ERR 0x86 +#define SM5705_REG_Q_EST 0x87 +#define SM5705_AUX_STAT 0x94 +//etc +#define SM5705_REG_MISC 0x90 +#define SM5705_REG_RESET 0x91 +#define SM5705_FG_INIT_MARK 0xA000 +#define SM5705_FG_PARAM_UNLOCK_CODE 0x3700 +#define SM5705_FG_PARAM_LOCK_CODE 0x0000 +#define SM5705_FG_TABLE_LEN 0xF//real table length -1 +//start reg addr for table +#define SM5705_REG_TABLE_START 0xA0 +#define SM5705_REG_IOCV_B_L_MIN 0x30 +#define SM5705_REG_IOCV_B_L_MAX 0x35 +#define SM5705_REG_IOCV_B_C_MIN 0x36 +#define SM5705_REG_IOCV_B_C_MAX 0x3B +#define SM5705_REG_IOCI_B_L_MIN 0x40 +#define SM5705_REG_IOCI_B_L_MAX 0x45 +#define SM5705_REG_IOCI_B_C_MIN 0x46 +#define SM5705_REG_IOCI_B_C_MAX 0x4B +#define SW_RESET_CODE 0x00A6 +#define SW_RESET_OTP_CODE 0x01A6 +#define RS_MAN_CNTL 0x0800 +// control register value +#define ENABLE_MIX_MODE 0x8000 +#define ENABLE_TEMP_MEASURE 0x4000 +#define ENABLE_TOPOFF_SOC 0x2000 +#define ENABLE_RS_MAN_MODE 0x0800 +#define ENABLE_MANUAL_OCV 0x0400 +#define ENABLE_MODE_nENQ4 0x0200 +#define ENABLE_SOC_ALARM 0x0008 +#define ENABLE_T_H_ALARM 0x0004 +#define ENABLE_T_L_ALARM 0x0002 +#define ENABLE_V_ALARM 0x0001 +#define CNTL_REG_DEFAULT_VALUE 0x2008 +#define INIT_CHECK_MASK 0x0010 +#define DISABLE_RE_INIT 0x0010 +#define SM5705_JIG_CONNECTED 0x0001 +#define SM5705_BATTERY_VERSION 0x00F0 +#define TOPOFF_SOC_97 0x111 +#define TOPOFF_SOC_96 0x110 +#define TOPOFF_SOC_95 0x101 +#define TOPOFF_SOC_94 0x100 +#define TOPOFF_SOC_93 0x011 +#define TOPOFF_SOC_92 0x010 +#define TOPOFF_SOC_91 0x001 +#define TOPOFF_SOC_90 0x000 +#define MASK_L_SOC_INT 0x0008 +#define MASK_H_TEM_INT 0x0004 +#define MASK_L_TEM_INT 0x0002 +#define MASK_L_VOL_INT 0x0001 +#define FULL_SOC 100 +#endif // SM5705_FUELGAUGE_IMPL_H diff --git a/drivers/battery_v2/include/sec_adc.h b/drivers/battery_v2/include/sec_adc.h new file mode 100644 index 000000000000..470235912067 --- /dev/null +++ b/drivers/battery_v2/include/sec_adc.h @@ -0,0 +1,32 @@ +/* + * sec_adc.h + * Samsung Mobile Charger Header + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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_ADC_H +#define __SEC_ADC_H __FILE__ + +#include +#include "sec_battery.h" +#include "sec_charging_common.h" + +#define VENDOR_UNKNOWN 0 +#define VENDOR_LSI 1 +#define VENDOR_QCOM 2 +#define RETRY_CNT 3 + +#endif /* __SEC_ADC_H */ + diff --git a/drivers/battery_v2/include/sec_battery.h b/drivers/battery_v2/include/sec_battery.h new file mode 100644 index 000000000000..de8cd5d14378 --- /dev/null +++ b/drivers/battery_v2/include/sec_battery.h @@ -0,0 +1,658 @@ +/* + * sec_battery.h + * Samsung Mobile Battery Header + * + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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_BATTERY_H +#define __SEC_BATTERY_H __FILE__ + +#include "sec_charging_common.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) +#include +#else +#if defined(CONFIG_CCIC_NOTIFIER) +#include +#endif /* CONFIG_CCIC_NOTIFIER */ +#if defined(CONFIG_MUIC_NOTIFIER) +#include +#include +#else +#include +#endif +#endif + +#if defined(CONFIG_BATTERY_NOTIFIER) +#include +#endif +#if defined(CONFIG_VBUS_NOTIFIER) +#include +#endif + +#if defined(CONFIG_BATTERY_CISD) +#include "sec_cisd.h" +#endif + +#include "sec_adc.h" + +#define SEC_BAT_CURRENT_EVENT_NONE 0x00000 +#define SEC_BAT_CURRENT_EVENT_AFC 0x00001 +#define SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE 0x00002 +#define SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL 0x00004 +#define SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING 0x00010 +#define SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING 0x00020 +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) +#define SEC_BAT_CURRENT_EVENT_USB_100MA 0x00040 +#else +#define SEC_BAT_CURRENT_EVENT_USB_100MA 0x00000 +#endif +#define SEC_BAT_CURRENT_EVENT_LOW_TEMP 0x00080 +#define SEC_BAT_CURRENT_EVENT_SWELLING_MODE (SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING | SEC_BAT_CURRENT_EVENT_LOW_TEMP | SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) +#define SEC_BAT_CURRENT_EVENT_USB_SUPER 0x00100 +#define SEC_BAT_CURRENT_EVENT_CHG_LIMIT 0x00200 +#define SEC_BAT_CURRENT_EVENT_CALL 0x00400 +#define SEC_BAT_CURRENT_EVENT_SLATE 0x00800 +#define SEC_BAT_CURRENT_EVENT_VBAT_OVP 0x01000 +#define SEC_BAT_CURRENT_EVENT_VSYS_OVP 0x02000 +#define SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK 0x04000 +#define SEC_BAT_CURRENT_EVENT_AICL 0x08000 +#define SEC_BAT_CURRENT_EVENT_HV_DISABLE 0x10000 +#define SEC_BAT_CURRENT_EVENT_SELECT_PDO 0x020000 + +#define SIOP_EVENT_NONE 0x0000 +#define SIOP_EVENT_WPC_CALL 0x0001 + +#if defined(CONFIG_SEC_FACTORY) // SEC_FACTORY +#define STORE_MODE_CHARGING_MAX 80 +#define STORE_MODE_CHARGING_MIN 70 +#else // !SEC_FACTORY, STORE MODE +#define STORE_MODE_CHARGING_MAX 70 +#define STORE_MODE_CHARGING_MIN 60 +#define STORE_MODE_CHARGING_MAX_VZW 35 +#define STORE_MODE_CHARGING_MIN_VZW 30 +#endif //(CONFIG_SEC_FACTORY) + +#define ADC_CH_COUNT 10 +#define ADC_SAMPLE_COUNT 10 + +#define DEFAULT_HEALTH_CHECK_COUNT 5 +#define TEMP_HIGHLIMIT_DEFAULT 2000 + +#define SIOP_INPUT_LIMIT_CURRENT 1200 +#define SIOP_CHARGING_LIMIT_CURRENT 1000 +#define SIOP_WIRELESS_INPUT_LIMIT_CURRENT 530 +#define SIOP_WIRELESS_CHARGING_LIMIT_CURRENT 780 +#define SIOP_HV_WIRELESS_INPUT_LIMIT_CURRENT 700 +#define SIOP_HV_WIRELESS_CHARGING_LIMIT_CURRENT 600 +#define SIOP_STORE_HV_WIRELESS_CHARGING_LIMIT_CURRENT 450 +#define SIOP_HV_INPUT_LIMIT_CURRENT 1200 +#define SIOP_HV_CHARGING_LIMIT_CURRENT 1000 +#define SIOP_HV_12V_INPUT_LIMIT_CURRENT 535 +#define SIOP_HV_12V_CHARGING_LIMIT_CURRENT 1000 + +#define BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE 0x00000001 +#define BATT_MISC_EVENT_WIRELESS_BACKPACK_TYPE 0x00000002 +#define BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE 0x00000004 +#define BATT_MISC_EVENT_BATT_RESET_SOC 0x00000008 +#define BATT_MISC_EVENT_HICCUP_TYPE 0x00000020 +#define BATT_MISC_EVENT_WIRELESS_FOD 0x00000100 +#define BATT_MISC_EVENT_HEALTH_OVERHEATLIMIT 0x00100000 + +#define SEC_INPUT_VOLTAGE_0V 0 +#define SEC_INPUT_VOLTAGE_5V 5 +#define SEC_INPUT_VOLTAGE_9V 9 +#define SEC_INPUT_VOLTAGE_10V 10 +#define SEC_INPUT_VOLTAGE_12V 12 + +#define HV_CHARGER_STATUS_STANDARD1 12000 /* mW */ +#define HV_CHARGER_STATUS_STANDARD2 20000 /* mW */ + +#if defined(CONFIG_CCIC_NOTIFIER) +struct sec_bat_pdic_info { + unsigned int input_voltage; + unsigned int input_current; + unsigned int pdo_index; +}; +struct sec_bat_pdic_list { + struct sec_bat_pdic_info pd_info[8]; /* 5V ~ 12V */ + unsigned int now_pd_index; + unsigned int max_pd_count; +}; +#endif +#if defined(CONFIG_BATTERY_SWELLING) +enum swelling_mode_state { + SWELLING_MODE_NONE = 0, + SWELLING_MODE_CHARGING, + SWELLING_MODE_FULL, +}; +#endif +struct adc_sample_info { + unsigned int cnt; + int total_adc; + int average_adc; + int adc_arr[ADC_SAMPLE_COUNT]; + int index; +}; + +struct sec_ttf_data; + +struct sec_battery_info { + struct device *dev; + sec_battery_platform_data_t *pdata; + struct sec_ttf_data *ttf_d; + + /* power supply used in Android */ + struct power_supply *psy_bat; + struct power_supply *psy_usb; + struct power_supply *psy_ac; + struct power_supply *psy_wireless; + struct power_supply *psy_ps; +#if defined(CONFIG_USE_POGO) + struct power_supply *psy_pogo; +#endif + unsigned int irq; + + int pd_usb_attached; +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + struct notifier_block usb_typec_nb; +#else +#if defined(CONFIG_CCIC_NOTIFIER) + struct notifier_block pdic_nb; +#endif +#if defined(CONFIG_MUIC_NOTIFIER) + struct notifier_block batt_nb; +#endif +#endif + +#if defined(CONFIG_CCIC_NOTIFIER) + bool pdic_attach; + bool pdic_ps_rdy; + struct pdic_notifier_struct pdic_info; + struct sec_bat_pdic_list pd_list; +#endif + bool update_pd_list; + +#if defined(CONFIG_VBUS_NOTIFIER) + struct notifier_block vbus_nb; + int muic_vbus_status; +#endif + + bool is_sysovlo; + bool is_vbatovlo; + + bool safety_timer_set; + bool lcd_status; + bool skip_swelling; + + int status; + int health; + bool present; + + int voltage_now; /* cell voltage (mV) */ + int voltage_avg; /* average voltage (mV) */ + int voltage_ocv; /* open circuit voltage (mV) */ + int current_now; /* current (mA) */ + int inbat_adc; /* inbat adc */ + int current_avg; /* average current (mA) */ + int current_max; /* input current limit (mA) */ + int current_adc; + + unsigned int capacity; /* SOC (%) */ + unsigned int input_voltage; /* CHGIN/WCIN input voltage (V) */ + unsigned int charge_power; /* charge power (mW) */ + unsigned int max_charge_power; /* max charge power (mW) */ + unsigned int pd_max_charge_power; /* max charge power for pd (mW) */ + unsigned int aicl_current; + + struct mutex adclock; + struct adc_sample_info adc_sample[ADC_CH_COUNT]; + + /* keep awake until monitor is done */ + struct wake_lock monitor_wake_lock; + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; +#ifdef CONFIG_SAMSUNG_BATTERY_FACTORY + struct wake_lock lpm_wake_lock; +#endif + unsigned int polling_count; + unsigned int polling_time; + bool polling_in_sleep; + bool polling_short; + + struct delayed_work polling_work; + struct alarm polling_alarm; + ktime_t last_poll_time; + +#if defined(CONFIG_BATTERY_CISD) + struct cisd cisd; + bool skip_cisd; + bool usb_overheat_check; + int prev_volt; + int prev_temp; + int prev_jig_on; + int enable_update_data; + int prev_chg_on; +#endif + + /* battery check */ + unsigned int check_count; + /* ADC check */ + unsigned int check_adc_count; + unsigned int check_adc_value; + + /* health change check*/ + bool health_change; + /* ovp-uvlo health check */ + int health_check_count; + + /* time check */ + unsigned long charging_start_time; + unsigned long charging_passed_time; + unsigned long charging_next_time; + unsigned long charging_fullcharged_time; + + unsigned long wc_heating_start_time; + unsigned long wc_heating_passed_time; + unsigned int wc_heat_limit; + + /* chg temperature check */ + unsigned int chg_limit; + unsigned int chg_limit_recovery_cable; + unsigned int vbus_chg_by_siop; + unsigned int vbus_chg_by_full; + unsigned int mix_limit; + unsigned int vbus_limit; + + /* temperature check */ + int temperature; /* battery temperature */ +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + int temperature_test_battery; + int temperature_test_usb; + int temperature_test_wpc; + int temperature_test_chg; + int temperature_test_blkt; +#endif + int temper_amb; /* target temperature */ + int usb_temp; + int chg_temp; /* charger temperature */ + int wpc_temp; + int coil_temp; + int slave_chg_temp; + int blkt_temp; /* blanket temperature(instead of batt temp in mix_temp func for tablet model) */ + + int temp_adc; + int temp_ambient_adc; + int usb_temp_adc; + int chg_temp_adc; + int wpc_temp_adc; + int coil_temp_adc; + int slave_chg_temp_adc; + int blkt_temp_adc; + + int temp_highlimit_threshold; + int temp_highlimit_recovery; + int temp_high_threshold; + int temp_high_recovery; + int temp_low_threshold; + int temp_low_recovery; + + /* charging */ + unsigned int charging_mode; + bool is_recharging; + int wdt_kick_disable; + + bool is_jig_on; + int cable_type; + int muic_cable_type; + int extended_cable_type; + + struct wake_lock cable_wake_lock; + struct delayed_work cable_work; + struct wake_lock vbus_wake_lock; + struct delayed_work siop_work; + struct wake_lock siop_wake_lock; + struct wake_lock afc_wake_lock; + struct delayed_work afc_work; +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + struct delayed_work update_work; + struct delayed_work fw_init_work; +#endif + struct delayed_work siop_event_work; + struct wake_lock siop_event_wake_lock; + struct delayed_work siop_level_work; + struct wake_lock siop_level_wake_lock; + struct delayed_work wc_headroom_work; + struct wake_lock wc_headroom_wake_lock; +#if defined(CONFIG_UPDATE_BATTERY_DATA) + struct delayed_work batt_data_work; + struct wake_lock batt_data_wake_lock; + char *data_path; +#endif +#ifdef CONFIG_OF + struct delayed_work parse_mode_dt_work; + struct wake_lock parse_mode_dt_wake_lock; +#endif + struct delayed_work init_chg_work; + + char batt_type[48]; + unsigned int full_check_cnt; + unsigned int recharge_check_cnt; + + struct mutex iolock; + int input_current; + int charging_current; + int topoff_current; + int wpc_vout_level; + unsigned int current_event; + + /* wireless charging enable */ + struct mutex wclock; + int wc_enable; + int wc_enable_cnt; + int wc_enable_cnt_value; + int led_cover; + int wc_status; + bool wc_cv_mode; + bool wc_pack_max_curr; + + int wire_status; + +#if defined(CONFIG_USE_POGO) + /* pogo status */ + int pogo_status; +#endif + /* wearable charging */ + int ps_status; + int ps_enable; + + /* test mode */ + int test_mode; + bool factory_mode; + bool store_mode; + + /* MTBF test for CMCC */ + bool is_hc_usb; + + int siop_level; + int siop_event; + int siop_prev_event; + int stability_test; + int eng_not_full_status; + + bool skip_chg_temp_check; + bool skip_wpc_temp_check; + bool wpc_temp_mode; +#if defined(CONFIG_BATTERY_SWELLING) + unsigned int swelling_mode; +#endif +#if defined(CONFIG_AFC_CHARGER_MODE) + char *hv_chg_name; +#endif +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) + struct delayed_work slowcharging_work; +#endif +#if defined(CONFIG_BATTERY_AGE_FORECAST) + int batt_cycle; +#endif +#if defined(CONFIG_STEP_CHARGING) + unsigned int step_charging_type; + unsigned int step_charging_charge_power; + int step_charging_status; + int step_charging_step; +#endif +#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY) + bool cooldown_mode; +#endif + struct mutex misclock; + unsigned int misc_event; + unsigned int prev_misc_event; + struct delayed_work misc_event_work; + struct wake_lock misc_event_wake_lock; + struct mutex batt_handlelock; + struct mutex current_eventlock; + struct mutex typec_notylock; + + unsigned int hiccup_status; + + bool stop_timer; + unsigned long prev_safety_time; + unsigned long expired_time; + unsigned long cal_safety_time; + int fg_reset; +}; +ssize_t sec_bat_show_attrs(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t sec_bat_store_attrs(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +#define SEC_BATTERY_ATTR(_name) \ +{ \ + .attr = {.name = #_name, .mode = 0664}, \ + .show = sec_bat_show_attrs, \ + .store = sec_bat_store_attrs, \ +} +/* event check */ +#define EVENT_NONE (0) +#define EVENT_2G_CALL (0x1 << 0) +#define EVENT_3G_CALL (0x1 << 1) +#define EVENT_MUSIC (0x1 << 2) +#define EVENT_VIDEO (0x1 << 3) +#define EVENT_BROWSER (0x1 << 4) +#define EVENT_HOTSPOT (0x1 << 5) +#define EVENT_CAMERA (0x1 << 6) +#define EVENT_CAMCORDER (0x1 << 7) +#define EVENT_DATA_CALL (0x1 << 8) +#define EVENT_WIFI (0x1 << 9) +#define EVENT_WIBRO (0x1 << 10) +#define EVENT_LTE (0x1 << 11) +#define EVENT_LCD (0x1 << 12) +#define EVENT_GPS (0x1 << 13) +enum { + BATT_RESET_SOC = 0, + BATT_READ_RAW_SOC, + BATT_READ_ADJ_SOC, + BATT_TYPE, + BATT_VFOCV, + BATT_VOL_ADC, + BATT_VOL_ADC_CAL, + BATT_VOL_AVER, + BATT_VOL_ADC_AVER, + BATT_CURRENT_UA_NOW, + BATT_CURRENT_UA_AVG, + BATT_FILTER_CFG, + BATT_TEMP, + BATT_TEMP_ADC, + BATT_TEMP_AVER, + BATT_TEMP_ADC_AVER, + USB_TEMP, + USB_TEMP_ADC, + BATT_CHG_TEMP, + BATT_CHG_TEMP_ADC, + SLAVE_CHG_TEMP, + SLAVE_CHG_TEMP_ADC, + BLKT_TEMP, + BLKT_TEMP_ADC, + BATT_VF_ADC, + BATT_SLATE_MODE, + BATT_LP_CHARGING, + SIOP_ACTIVATED, + SIOP_LEVEL, + SIOP_EVENT, + BATT_CHARGING_SOURCE, + FG_REG_DUMP, + FG_RESET_CAP, + FG_CAPACITY, + FG_ASOC, + AUTH, + CHG_CURRENT_ADC, + WC_ADC, + WC_STATUS, + WC_ENABLE, + WC_CONTROL, + WC_CONTROL_CNT, + LED_COVER, + HV_CHARGER_STATUS, + HV_WC_CHARGER_STATUS, + HV_CHARGER_SET, + FACTORY_MODE, + STORE_MODE, + UPDATE, + TEST_MODE, + BATT_EVENT_CALL, + BATT_EVENT_2G_CALL, + BATT_EVENT_TALK_GSM, + BATT_EVENT_3G_CALL, + BATT_EVENT_TALK_WCDMA, + BATT_EVENT_MUSIC, + BATT_EVENT_VIDEO, + BATT_EVENT_BROWSER, + BATT_EVENT_HOTSPOT, + BATT_EVENT_CAMERA, + BATT_EVENT_CAMCORDER, + BATT_EVENT_DATA_CALL, + BATT_EVENT_WIFI, + BATT_EVENT_WIBRO, + BATT_EVENT_LTE, + BATT_EVENT_LCD, + BATT_EVENT_GPS, + BATT_EVENT, + BATT_TEMP_TABLE, + BATT_HIGH_CURRENT_USB, +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + TEST_CHARGE_CURRENT, +#endif + SET_STABILITY_TEST, + BATT_CAPACITY_MAX, + BATT_INBAT_VOLTAGE, + BATT_INBAT_VOLTAGE_OCV, + CHECK_SLAVE_CHG, + BATT_INBAT_WIRELESS_CS100, + HMT_TA_CONNECTED, + HMT_TA_CHARGE, +#if defined(CONFIG_BATTERY_AGE_FORECAST) + FG_CYCLE, + FG_FULL_VOLTAGE, + FG_FULLCAPNOM, + BATTERY_CYCLE, +#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE) + BATT_AFTER_MANUFACTURED, +#endif +#endif + BATT_WPC_TEMP, + BATT_WPC_TEMP_ADC, + BATT_COIL_TEMP, + BATT_COIL_TEMP_ADC, + BATT_WIRELESS_MST_SWITCH_TEST, +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + BATT_WIRELESS_FIRMWARE_UPDATE, + OTP_FIRMWARE_RESULT, + WC_IC_GRADE, + OTP_FIRMWARE_VER_BIN, + OTP_FIRMWARE_VER, + TX_FIRMWARE_RESULT, + TX_FIRMWARE_VER, + BATT_TX_STATUS, +#endif + WC_VOUT, + WC_VRECT, +#if defined(CONFIG_WIRELESS_CHARGER_HIGH_VOLTAGE) + BATT_HV_WIRELESS_STATUS, + BATT_HV_WIRELESS_PAD_CTRL, +#endif + WC_OP_FREQ, + WC_CMD_INFO, + BATT_TUNE_FLOAT_VOLTAGE, + BATT_TUNE_INPUT_CHARGE_CURRENT, + BATT_TUNE_FAST_CHARGE_CURRENT, + BATT_TUNE_UI_TERM_CURRENT_1ST, + BATT_TUNE_UI_TERM_CURRENT_2ND, + BATT_TUNE_TEMP_HIGH_NORMAL, + BATT_TUNE_TEMP_HIGH_REC_NORMAL, + BATT_TUNE_TEMP_LOW_NORMAL, + BATT_TUNE_TEMP_LOW_REC_NORMAL, + BATT_TUNE_CHG_TEMP_HIGH, + BATT_TUNE_CHG_TEMP_REC, + BATT_TUNE_CHG_LIMMIT_CUR, + BATT_TUNE_COIL_TEMP_HIGH, + BATT_TUNE_COIL_TEMP_REC, + BATT_TUNE_COIL_LIMMIT_CUR, + +#if defined(CONFIG_UPDATE_BATTERY_DATA) + BATT_UPDATE_DATA, +#endif + + BATT_MISC_EVENT, + BATT_EXT_DEV_CHG, + BATT_WDT_CONTROL, + MODE, + CHECK_PS_READY, + BATT_CHIP_ID, + CISD_FULLCAPREP_MAX, +#if defined(CONFIG_BATTERY_CISD) + CISD_DATA, + CISD_DATA_JSON, + CISD_DATA_D_JSON, + CISD_WIRE_COUNT, + CISD_WC_DATA, + CISD_WC_DATA_JSON, + PREV_BATTERY_DATA, + PREV_BATTERY_INFO, +#endif + SAFETY_TIMER_SET, + BATT_SWELLING_CONTROL, + SAFETY_TIMER_INFO, +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + BATT_TEMP_TEST, +#endif + BATT_CURRENT_EVENT, + CHARGE_OTG_CONTROL, + CHARGE_UNO_CONTROL, +}; +enum { + EXT_DEV_NONE = 0, + EXT_DEV_GAMEPAD_CHG, + EXT_DEV_GAMEPAD_OTG, +}; + +extern unsigned int lpcharge; + +extern void select_pdo(int num); +extern int adc_read(struct sec_battery_info *battery, int channel); +extern void adc_init(struct platform_device *pdev, struct sec_battery_info *battery); +extern void adc_exit(struct sec_battery_info *battery); +extern void sec_cable_init(struct platform_device *pdev, struct sec_battery_info *battery); +extern int sec_bat_get_adc_data(struct sec_battery_info *battery, int adc_ch, int count); +extern int sec_bat_get_charger_type_adc(struct sec_battery_info *battery); +extern bool sec_bat_get_value_by_adc(struct sec_battery_info *battery, enum sec_battery_adc_channel channel, union power_supply_propval *value); +extern int sec_bat_get_adc_value(struct sec_battery_info *battery, int channel); +extern bool sec_bat_check_vf_adc(struct sec_battery_info *battery); +#if defined(CONFIG_UPDATE_BATTERY_DATA) +extern int sec_battery_update_data(const char* file_path); +#endif +#if defined(CONFIG_BATTERY_CISD) +extern bool sec_bat_cisd_check(struct sec_battery_info *battery); +extern void sec_battery_cisd_init(struct sec_battery_info *battery); +extern void set_cisd_pad_data(struct sec_battery_info *battery, const char* buf); +#endif +bool sec_bat_hv_wc_normal_mode_check(struct sec_battery_info *battery); +#endif /* __SEC_BATTERY_H */ diff --git a/drivers/battery_v2/include/sec_battery_ttf.h b/drivers/battery_v2/include/sec_battery_ttf.h new file mode 100644 index 000000000000..8e4342f2ebe4 --- /dev/null +++ b/drivers/battery_v2/include/sec_battery_ttf.h @@ -0,0 +1,67 @@ +/* + * sec_battery.h + * Samsung Mobile Battery Header + * + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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_BATTERY_TTF_H +#define __SEC_BATTERY_TTF_H __FILE__ + +struct sec_cv_slope { + int fg_current; + int soc; + int time; +}; + +struct sec_battery_info; + +struct sec_ttf_data { + void *pdev; + int timetofull; + + unsigned int ttf_hv_12v_charge_current; + unsigned int ttf_hv_charge_current; + unsigned int ttf_hv_12v_wireless_charge_current; + unsigned int ttf_hv_wireless_charge_current; + unsigned int ttf_wireless_charge_current; + unsigned int ttf_dc25_charge_current; + unsigned int ttf_dc45_charge_current; + unsigned int ttf_predict_wc20_charge_current; +#if defined(CONFIG_USE_POGO) + unsigned int ttf_pogo_charge_current; +#endif + + unsigned int max_charging_current; + unsigned int pd_charging_charge_power; + + struct sec_cv_slope *cv_data; + int cv_data_length; + unsigned int ttf_capacity; + + struct delayed_work timetofull_work; +}; + +int sec_calc_ttf(struct sec_battery_info *battery, unsigned int ttf_curr); +extern void sec_bat_calc_time_to_full(struct sec_battery_info *battery); +extern void sec_bat_time_to_full_work(struct work_struct *work); +extern void ttf_init(struct sec_battery_info *battery); +extern void ttf_work_start(struct sec_battery_info *battery); +extern int ttf_display(struct sec_battery_info *battery); +#ifdef CONFIG_OF +int sec_ttf_parse_dt(struct sec_battery_info *battery); +#endif + +#endif /* __SEC_BATTERY_H */ diff --git a/drivers/battery_v2/include/sec_charging_common.h b/drivers/battery_v2/include/sec_charging_common.h new file mode 100644 index 000000000000..6d55ae153d97 --- /dev/null +++ b/drivers/battery_v2/include/sec_charging_common.h @@ -0,0 +1,1143 @@ +/* + * sec_charging_common.h + * Samsung Mobile Charging Common Header + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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_CHARGING_COMMON_H +#define __SEC_CHARGING_COMMON_H __FILE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* definitions */ +#define SEC_BATTERY_CABLE_HV_WIRELESS_ETX 100 + +#define MFC_LDO_ON 1 +#define MFC_LDO_OFF 0 + +enum power_supply_ext_property { + POWER_SUPPLY_EXT_PROP_CHECK_SLAVE_I2C = POWER_SUPPLY_PROP_MAX, + POWER_SUPPLY_EXT_PROP_MULTI_CHARGER_MODE, + POWER_SUPPLY_EXT_PROP_WIRELESS_DET_IRQ, + POWER_SUPPLY_EXT_PROP_WIRELESS_OP_FREQ, + POWER_SUPPLY_EXT_PROP_WIRELESS_TX_CMD, + POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VAL, + POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ID, + POWER_SUPPLY_EXT_PROP_WIRELESS_TX_CHG_ERR, + POWER_SUPPLY_EXT_PROP_AICL_CURRENT, + POWER_SUPPLY_EXT_PROP_CHECK_MULTI_CHARGE, + POWER_SUPPLY_EXT_PROP_CHIP_ID, + POWER_SUPPLY_EXT_PROP_SYSOVLO, + POWER_SUPPLY_EXT_PROP_VBAT_OVP, + POWER_SUPPLY_EXT_PROP_USB_CONFIGURE, + POWER_SUPPLY_EXT_PROP_WDT_STATUS, + POWER_SUPPLY_EXT_PROP_HV_DISABLE, + POWER_SUPPLY_EXT_PROP_SUB_PBA_TEMP_REC, + POWER_SUPPLY_EXT_PROP_CURRENT_FULL, + POWER_SUPPLY_EXT_PROP_AFC_CHARGER_MODE, + POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, + POWER_SUPPLY_EXT_PROP_FILTER_CFG, + POWER_SUPPLY_EXT_PROP_CHARGE_POWERED_OTG_CONTROL, + POWER_SUPPLY_EXT_PROP_WC_CONTROL, +}; + +enum sec_battery_usb_conf { + USB_CURRENT_UNCONFIGURED = 100, + USB_CURRENT_HIGH_SPEED = 500, + USB_CURRENT_SUPER_SPEED = 900, +}; + +enum sec_battery_rp_curr { + RP_CURRENT_RP1 = 500, + RP_CURRENT_RP2 = 1500, + RP_CURRENT_RP3 = 3000, + RP_CURRENT_ABNORMAL_RP3 = 1800, +}; + +enum power_supply_ext_health { + POWER_SUPPLY_HEALTH_VSYS_OVP = POWER_SUPPLY_HEALTH_MAX, + POWER_SUPPLY_HEALTH_VBAT_OVP, +}; + +enum sec_battery_cable { + SEC_BATTERY_CABLE_UNKNOWN = 0, + SEC_BATTERY_CABLE_NONE, /* 1 */ + SEC_BATTERY_CABLE_PREPARE_TA, /* 2 */ + SEC_BATTERY_CABLE_TA, /* 3 */ + SEC_BATTERY_CABLE_USB, /* 4 */ + SEC_BATTERY_CABLE_USB_CDP, /* 5 */ + SEC_BATTERY_CABLE_9V_TA, /* 6 */ + SEC_BATTERY_CABLE_9V_ERR, /* 7 */ + SEC_BATTERY_CABLE_9V_UNKNOWN, /* 8 */ + SEC_BATTERY_CABLE_12V_TA, /* 9 */ + SEC_BATTERY_CABLE_WIRELESS, /* 10 */ + SEC_BATTERY_CABLE_HV_WIRELESS, /* 11 */ + SEC_BATTERY_CABLE_PMA_WIRELESS, /* 12 */ + SEC_BATTERY_CABLE_WIRELESS_PACK, /* 13 */ + SEC_BATTERY_CABLE_WIRELESS_PACK_TA, /* 14 */ + SEC_BATTERY_CABLE_WIRELESS_STAND, /* 15 */ + SEC_BATTERY_CABLE_WIRELESS_HV_STAND, /* 16 */ + SEC_BATTERY_CABLE_QC20, /* 17 */ + SEC_BATTERY_CABLE_QC30, /* 18 */ + SEC_BATTERY_CABLE_PDIC, /* 19 */ + SEC_BATTERY_CABLE_UARTOFF, /* 20 */ + SEC_BATTERY_CABLE_OTG, /* 21 */ + SEC_BATTERY_CABLE_LAN_HUB, /* 22 */ + SEC_BATTERY_CABLE_POWER_SHARING, /* 23 */ + SEC_BATTERY_CABLE_HMT_CONNECTED, /* 24 */ + SEC_BATTERY_CABLE_HMT_CHARGE, /* 25 */ + SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT, /* 26 */ + SEC_BATTERY_CABLE_WIRELESS_VEHICLE, /* 27 */ + SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE, /* 28 */ + SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV, /* 29 */ + SEC_BATTERY_CABLE_TIMEOUT, /* 30 */ + SEC_BATTERY_CABLE_SMART_OTG, /* 31 */ + SEC_BATTERY_CABLE_SMART_NOTG, /* 32 */ +#if defined(CONFIG_USE_POGO) + SEC_BATTERY_CABLE_POGO, /* 33 POGO */ +#endif + SEC_BATTERY_CABLE_MAX, /* 34 */ +}; + +enum sec_battery_voltage_mode { + /* average voltage */ + SEC_BATTERY_VOLTAGE_AVERAGE = 0, + /* open circuit voltage */ + SEC_BATTERY_VOLTAGE_OCV, +}; + +enum sec_battery_current_mode { + /* uA */ + SEC_BATTERY_CURRENT_UA = 0, + /* mA */ + SEC_BATTERY_CURRENT_MA, +}; + +enum sec_battery_capacity_mode { + /* designed capacity */ + SEC_BATTERY_CAPACITY_DESIGNED = 0, + /* absolute capacity by fuel gauge */ + SEC_BATTERY_CAPACITY_ABSOLUTE, + /* temperary capacity in the time */ + SEC_BATTERY_CAPACITY_TEMPERARY, + /* current capacity now */ + SEC_BATTERY_CAPACITY_CURRENT, + /* cell aging information */ + SEC_BATTERY_CAPACITY_AGEDCELL, + /* charge count */ + SEC_BATTERY_CAPACITY_CYCLE, + /* full capacity rep */ + SEC_BATTERY_CAPACITY_FULL, + /* QH capacity */ + SEC_BATTERY_CAPACITY_QH, + /* vfsoc */ + SEC_BATTERY_CAPACITY_VFSOC, +}; + +enum sec_wireless_info_mode { + SEC_WIRELESS_OTP_FIRM_RESULT = 0, + SEC_WIRELESS_IC_REVISION, + SEC_WIRELESS_IC_GRADE, + SEC_WIRELESS_OTP_FIRM_VER_BIN, + SEC_WIRELESS_OTP_FIRM_VER, + SEC_WIRELESS_TX_FIRM_RESULT, + SEC_WIRELESS_TX_FIRM_VER, + SEC_TX_FIRMWARE, + SEC_WIRELESS_OTP_FIRM_VERIFY, + SEC_WIRELESS_MST_SWITCH_VERIFY, +}; + +enum sec_wireless_firm_update_mode { + SEC_WIRELESS_RX_SDCARD_MODE = 0, + SEC_WIRELESS_RX_BUILT_IN_MODE, + SEC_WIRELESS_TX_ON_MODE, + SEC_WIRELESS_TX_OFF_MODE, + SEC_WIRELESS_RX_INIT, +}; + +enum sec_tx_firmware_mode { + SEC_TX_OFF = 0, + SEC_TX_STANDBY, + SEC_TX_POWER_TRANSFER, + SEC_TX_ERROR, +}; + +enum sec_wireless_control_mode { + WIRELESS_VOUT_OFF = 0, + WIRELESS_VOUT_NORMAL_VOLTAGE, /* 5V , reserved by factory */ + WIRELESS_VOUT_RESERVED, /* 6V */ + WIRELESS_VOUT_HIGH_VOLTAGE, /* 9V , reserved by factory */ + WIRELESS_VOUT_CC_CV_VOUT, + WIRELESS_VOUT_CV_CALL, + WIRELESS_VOUT_CC_CALL, + WIRELESS_VOUT_5V, + WIRELESS_VOUT_9V, + WIRELESS_VOUT_10V, + WIRELESS_VOUT_5V_STEP, + WIRELESS_VOUT_9V_STEP, + WIRELESS_VOUT_10V_STEP, + WIRELESS_PAD_FAN_OFF, + WIRELESS_PAD_FAN_ON, + WIRELESS_PAD_LED_OFF, + WIRELESS_PAD_LED_ON, + WIRELESS_VRECT_ADJ_ON, + WIRELESS_VRECT_ADJ_OFF, + WIRELESS_VRECT_ADJ_ROOM_0, + WIRELESS_VRECT_ADJ_ROOM_1, + WIRELESS_VRECT_ADJ_ROOM_2, + WIRELESS_VRECT_ADJ_ROOM_3, + WIRELESS_VRECT_ADJ_ROOM_4, + WIRELESS_VRECT_ADJ_ROOM_5, + WIRELESS_CLAMP_ENABLE, +}; + +enum sec_siop_event_mode { + SIOP_EVENT_IDLE = 0, + SIOP_EVENT_WPC_CALL_START, /* 5V wireless charging + Call */ + SIOP_EVENT_WPC_CALL_END, /* 5V wireless charging + Call */ + SIOP_EVENT_MAX, /* end */ +}; + +enum sec_wireless_pad_mode { + SEC_WIRELESS_PAD_NONE = 0, + SEC_WIRELESS_PAD_WPC, + SEC_WIRELESS_PAD_WPC_HV, + SEC_WIRELESS_PAD_WPC_PACK, + SEC_WIRELESS_PAD_WPC_PACK_TA, + SEC_WIRELESS_PAD_WPC_STAND, + SEC_WIRELESS_PAD_WPC_STAND_HV, + SEC_WIRELESS_PAD_PMA, + SEC_WIRELESS_PAD_VEHICLE, + SEC_WIRELESS_PAD_VEHICLE_HV, + SEC_WIRELESS_PAD_PREPARE_HV, + SEC_WIRELESS_PAD_A4WP, +}; + +enum sec_wireless_pad_id { + WC_PAD_ID_UNKNOWN = 0x00, + /* 0x01~1F : Single Port */ + WC_PAD_ID_SNGL_NOBLE = 0x10, + WC_PAD_ID_SNGL_VEHICLE, + WC_PAD_ID_SNGL_MINI, + WC_PAD_ID_SNGL_ZERO, + WC_PAD_ID_SNGL_DREAM, + /* 0x20~2F : Multi Port */ + /* 0x30~3F : Stand Type */ + WC_PAD_ID_STAND_HERO = 0x30, + WC_PAD_ID_STAND_DREAM, + /* 0x40~4F : External Battery Pack */ + WC_PAD_ID_EXT_BATT_PACK = 0x40, + WC_PAD_ID_EXT_BATT_PACK_TA, + /* 0x50~6F : Reserved */ + WC_PAD_ID_MAX = 0x6F, +}; + +enum sec_battery_temp_control_source { + TEMP_CONTROL_SOURCE_NONE = 0, + TEMP_CONTROL_SOURCE_BAT_THM, + TEMP_CONTROL_SOURCE_CHG_THM, + TEMP_CONTROL_SOURCE_WPC_THM, + TEMP_CONTROL_SOURCE_USB_THM, + TEMP_CONTROL_SOURCE_BLKT_THM, +}; + +/* ADC type */ +enum sec_battery_adc_type { + /* NOT using this ADC channel */ + SEC_BATTERY_ADC_TYPE_NONE = 0, + /* ADC in AP */ + SEC_BATTERY_ADC_TYPE_AP, + /* ADC by additional IC */ + SEC_BATTERY_ADC_TYPE_IC, + SEC_BATTERY_ADC_TYPE_NUM +}; + +enum sec_battery_adc_channel { + SEC_BAT_ADC_CHANNEL_CABLE_CHECK = 0, + SEC_BAT_ADC_CHANNEL_BAT_CHECK, + SEC_BAT_ADC_CHANNEL_TEMP, + SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT, + SEC_BAT_ADC_CHANNEL_FULL_CHECK, + SEC_BAT_ADC_CHANNEL_VOLTAGE_NOW, + SEC_BAT_ADC_CHANNEL_CHG_TEMP, + SEC_BAT_ADC_CHANNEL_INBAT_VOLTAGE, + SEC_BAT_ADC_CHANNEL_DISCHARGING_CHECK, + SEC_BAT_ADC_CHANNEL_DISCHARGING_NTC, + SEC_BAT_ADC_CHANNEL_WPC_TEMP, + SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP, + SEC_BAT_ADC_CHANNEL_USB_TEMP, + SEC_BAT_ADC_CHANNEL_BLKT_TEMP, + SEC_BAT_ADC_CHANNEL_NUM, +}; + +enum sec_battery_charge_mode { + SEC_BAT_CHG_MODE_CHARGING = 0, + SEC_BAT_CHG_MODE_CHARGING_OFF, + SEC_BAT_CHG_MODE_BUCK_OFF, +}; + +/* charging mode */ +enum sec_battery_charging_mode { + /* buckoff */ + SEC_BATTERY_BUCKOFF = 0, + /* no charging */ + SEC_BATTERY_CHARGING_NONE, + /* 1st charging */ + SEC_BATTERY_CHARGING_1ST, + /* 2nd charging */ + SEC_BATTERY_CHARGING_2ND, +}; + +struct sec_bat_adc_api { + bool (*init)(struct platform_device *); + bool (*exit)(void); + int (*read)(unsigned int); +}; +#define sec_bat_adc_api_t struct sec_bat_adc_api + +/* monitor activation */ +enum sec_battery_polling_time_type { + /* same order with power supply status */ + SEC_BATTERY_POLLING_TIME_BASIC = 0, + SEC_BATTERY_POLLING_TIME_CHARGING, + SEC_BATTERY_POLLING_TIME_DISCHARGING, + SEC_BATTERY_POLLING_TIME_NOT_CHARGING, + SEC_BATTERY_POLLING_TIME_SLEEP, +}; + +enum sec_battery_monitor_polling { + /* polling work queue */ + SEC_BATTERY_MONITOR_WORKQUEUE, + /* alarm polling */ + SEC_BATTERY_MONITOR_ALARM, + /* timer polling (NOT USE) */ + SEC_BATTERY_MONITOR_TIMER, +}; +#define sec_battery_monitor_polling_t \ + enum sec_battery_monitor_polling + +/* full charged check : POWER_SUPPLY_PROP_STATUS */ +enum sec_battery_full_charged { + SEC_BATTERY_FULLCHARGED_NONE = 0, + /* current check by ADC */ + SEC_BATTERY_FULLCHARGED_ADC, + /* fuel gauge current check */ + SEC_BATTERY_FULLCHARGED_FG_CURRENT, + /* time check */ + SEC_BATTERY_FULLCHARGED_TIME, + /* SOC check */ + SEC_BATTERY_FULLCHARGED_SOC, + /* charger GPIO, NO additional full condition */ + SEC_BATTERY_FULLCHARGED_CHGGPIO, + /* charger interrupt, NO additional full condition */ + SEC_BATTERY_FULLCHARGED_CHGINT, + /* charger power supply property, NO additional full condition */ + SEC_BATTERY_FULLCHARGED_CHGPSY, +}; + +#define sec_battery_full_charged_t \ + enum sec_battery_full_charged + +/* full check condition type (can be used overlapped) */ +#define sec_battery_full_condition_t unsigned int +/* SEC_BATTERY_FULL_CONDITION_NOTIMEFULL + * full-charged by absolute-timer only in high voltage + */ +#define SEC_BATTERY_FULL_CONDITION_NOTIMEFULL 1 +/* SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL + * do not set polling time as sleep polling time in full-charged + */ +#define SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL 2 +/* SEC_BATTERY_FULL_CONDITION_SOC + * use capacity for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_SOC 4 +/* SEC_BATTERY_FULL_CONDITION_VCELL + * use VCELL for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_VCELL 8 +/* SEC_BATTERY_FULL_CONDITION_AVGVCELL + * use average VCELL for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_AVGVCELL 16 +/* SEC_BATTERY_FULL_CONDITION_OCV + * use OCV for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_OCV 32 + +/* recharge check condition type (can be used overlapped) */ +#define sec_battery_recharge_condition_t unsigned int +/* SEC_BATTERY_RECHARGE_CONDITION_SOC + * use capacity for recharging check + */ +#define SEC_BATTERY_RECHARGE_CONDITION_SOC 1 +/* SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL + * use average VCELL for recharging check + */ +#define SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL 2 +/* SEC_BATTERY_RECHARGE_CONDITION_VCELL + * use VCELL for recharging check + */ +#define SEC_BATTERY_RECHARGE_CONDITION_VCELL 4 + +/* battery check : POWER_SUPPLY_PROP_PRESENT */ +enum sec_battery_check { + /* No Check for internal battery */ + SEC_BATTERY_CHECK_NONE, + /* by ADC */ + SEC_BATTERY_CHECK_ADC, + /* by callback function (battery certification by 1 wired)*/ + SEC_BATTERY_CHECK_CALLBACK, + /* by PMIC */ + SEC_BATTERY_CHECK_PMIC, + /* by fuel gauge */ + SEC_BATTERY_CHECK_FUELGAUGE, + /* by charger */ + SEC_BATTERY_CHECK_CHARGER, + /* by interrupt (use check_battery_callback() to check battery) */ + SEC_BATTERY_CHECK_INT, +}; +#define sec_battery_check_t \ + enum sec_battery_check + +/* OVP, UVLO check : POWER_SUPPLY_PROP_HEALTH */ +enum sec_battery_ovp_uvlo { + /* by callback function */ + SEC_BATTERY_OVP_UVLO_CALLBACK, + /* by PMIC polling */ + SEC_BATTERY_OVP_UVLO_PMICPOLLING, + /* by PMIC interrupt */ + SEC_BATTERY_OVP_UVLO_PMICINT, + /* by charger polling */ + SEC_BATTERY_OVP_UVLO_CHGPOLLING, + /* by charger interrupt */ + SEC_BATTERY_OVP_UVLO_CHGINT, +}; +#define sec_battery_ovp_uvlo_t \ + enum sec_battery_ovp_uvlo + +/* thermal source */ +enum sec_battery_thermal_source { + /* by fuel gauge */ + SEC_BATTERY_THERMAL_SOURCE_FG, + /* by external source */ + SEC_BATTERY_THERMAL_SOURCE_CALLBACK, + /* by ADC */ + SEC_BATTERY_THERMAL_SOURCE_ADC, +}; +#define sec_battery_thermal_source_t \ + enum sec_battery_thermal_source + +/* temperature check type */ +enum sec_battery_temp_check { + SEC_BATTERY_TEMP_CHECK_NONE = 0, /* no temperature check */ + SEC_BATTERY_TEMP_CHECK_ADC, /* by ADC value */ + SEC_BATTERY_TEMP_CHECK_TEMP, /* by temperature */ +}; +#define sec_battery_temp_check_t \ + enum sec_battery_temp_check + +/* cable check (can be used overlapped) */ +#define sec_battery_cable_check_t unsigned int +/* SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE + * for USB cable in tablet model, + * status is stuck into discharging, + * but internal charging logic is working + */ +#define SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE 1 +/* SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE + * for incompatible charger + * (Not compliant to USB specification, + * cable type is SEC_BATTERY_CABLE_UNKNOWN), + * do NOT charge and show message to user + * (only for VZW) + */ +#define SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE 2 +/* SEC_BATTERY_CABLE_CHECK_PSY + * check cable by power supply set_property + */ +#define SEC_BATTERY_CABLE_CHECK_PSY 4 +/* SEC_BATTERY_CABLE_CHECK_INT + * check cable by interrupt + */ +#define SEC_BATTERY_CABLE_CHECK_INT 8 +/* SEC_BATTERY_CABLE_CHECK_CHGINT + * check cable by charger interrupt + */ +#define SEC_BATTERY_CABLE_CHECK_CHGINT 16 +/* SEC_BATTERY_CABLE_CHECK_POLLING + * check cable by GPIO polling + */ +#define SEC_BATTERY_CABLE_CHECK_POLLING 32 + +/* check cable source (can be used overlapped) */ +#define sec_battery_cable_source_t unsigned int +/* SEC_BATTERY_CABLE_SOURCE_EXTERNAL + * already given by external argument + */ +#define SEC_BATTERY_CABLE_SOURCE_EXTERNAL 1 +/* SEC_BATTERY_CABLE_SOURCE_CALLBACK + * by callback (MUIC, USB switch) + */ +#define SEC_BATTERY_CABLE_SOURCE_CALLBACK 2 +/* SEC_BATTERY_CABLE_SOURCE_ADC + * by ADC + */ +#define SEC_BATTERY_CABLE_SOURCE_ADC 4 + +/* capacity calculation type (can be used overlapped) */ +#define sec_fuelgauge_capacity_type_t int +/* SEC_FUELGAUGE_CAPACITY_TYPE_RESET + * use capacity information to reset fuel gauge + * (only for driver algorithm, can NOT be set by user) + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_RESET (-1) +/* SEC_FUELGAUGE_CAPACITY_TYPE_RAW + * use capacity information from fuel gauge directly + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_RAW 1 +/* SEC_FUELGAUGE_CAPACITY_TYPE_SCALE + * rescale capacity by scaling, need min and max value for scaling + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_SCALE 2 +/* SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE + * change only maximum capacity dynamically + * to keep time for every SOC unit + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE 4 +/* SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC + * change capacity value by only -1 or +1 + * no sudden change of capacity + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC 8 +/* SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL + * skip current capacity value + * if it is abnormal value + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL 16 + +/* charger function settings (can be used overlapped) */ +#define sec_charger_functions_t unsigned int +/* SEC_CHARGER_NO_GRADUAL_CHARGING_CURRENT + * disable gradual charging current setting + * SUMMIT:AICL, MAXIM:regulation loop + */ +#define SEC_CHARGER_NO_GRADUAL_CHARGING_CURRENT 1 + +/* SEC_CHARGER_MINIMUM_SIOP_CHARGING_CURRENT + * charging current should be over than USB charging current + */ +#define SEC_CHARGER_MINIMUM_SIOP_CHARGING_CURRENT 2 + +/** + * struct sec_bat_adc_table_data - adc to temperature table for sec battery + * driver + * @adc: adc value + * @temperature: temperature(C) * 10 + */ +struct sec_bat_adc_table_data { + int adc; + int data; +}; +#define sec_bat_adc_table_data_t \ + struct sec_bat_adc_table_data + +struct sec_bat_adc_region { + int min; + int max; +}; +#define sec_bat_adc_region_t \ + struct sec_bat_adc_region + +struct sec_charging_current { + unsigned int input_current_limit; + unsigned int fast_charging_current; +}; + +#define sec_charging_current_t \ + struct sec_charging_current + +#if defined(CONFIG_BATTERY_AGE_FORECAST) +struct sec_age_data { + unsigned int cycle; + unsigned int float_voltage; + unsigned int recharge_condition_vcell; + unsigned int full_condition_vcell; + unsigned int full_condition_soc; +}; + +#define sec_age_data_t \ + struct sec_age_data +#endif + +struct sec_battery_platform_data { + /* NO NEED TO BE CHANGED */ + /* callback functions */ + void (*initial_check)(void); + void (*monitor_additional_check)(void); + bool (*bat_gpio_init)(void); + bool (*fg_gpio_init)(void); + bool (*is_lpm)(void); + bool (*check_jig_status)(void); + bool (*is_interrupt_cable_check_possible)(int); + int (*check_cable_callback)(void); + int (*get_cable_from_extended_cable_type)(int); + bool (*cable_switch_check)(void); + bool (*cable_switch_normal)(void); + bool (*check_cable_result_callback)(int); + bool (*check_battery_callback)(void); + bool (*check_battery_result_callback)(void); + int (*ovp_uvlo_callback)(void); + bool (*ovp_uvlo_result_callback)(int); + bool (*fuelalert_process)(bool); + bool (*get_temperature_callback)( + enum power_supply_property, + union power_supply_propval*); + + /* ADC API for each ADC type */ + sec_bat_adc_api_t adc_api[SEC_BATTERY_ADC_TYPE_NUM]; + /* ADC region by power supply type + * ADC region should be exclusive + */ + sec_bat_adc_region_t *cable_adc_value; + /* charging current for type (0: not use) */ + sec_charging_current_t *charging_current; + unsigned int *polling_time; + char *chip_vendor; + unsigned int temp_adc_type; + int temp_adc_channel; + int chg_temp_adc_channel; + int usb_temp_adc_channel; + int blkt_temp_adc_channel; + /* NO NEED TO BE CHANGED */ + unsigned int pre_afc_input_current; + unsigned int pre_wc_afc_input_current; + unsigned int store_mode_afc_input_current; + unsigned int store_mode_hv_wireless_input_current; + + char *pmic_name; + + /* battery */ + char *vendor; + int technology; + int battery_type; + void *battery_data; + + int bat_gpio_ta_nconnected; + /* 1 : active high, 0 : active low */ + int bat_polarity_ta_nconnected; + int bat_irq; + int bat_irq_gpio; /* BATT_INT(BAT_ID detecting) */ + unsigned long bat_irq_attr; + int jig_irq; + unsigned long jig_irq_attr; + sec_battery_cable_check_t cable_check_type; + sec_battery_cable_source_t cable_source_type; + + bool use_LED; /* use charging LED */ + + /* flag for skipping the swelling mode */ + bool swelling_mode_skip_in_high_temp; + /* sustaining event after deactivated (second) */ + unsigned int event_waiting_time; + + /* battery swelling */ + int swelling_high_temp_block; + int swelling_high_temp_recov; + int swelling_wc_high_temp_recov; + int swelling_low_temp_block_1st; + int swelling_low_temp_recov_1st; + int swelling_low_temp_block_2nd; + int swelling_low_temp_recov_2nd; + unsigned int swelling_low_temp_current; + unsigned int swelling_low_temp_topoff; + unsigned int swelling_high_temp_current; + unsigned int swelling_high_temp_topoff; + unsigned int swelling_wc_high_temp_current; + unsigned int swelling_wc_low_temp_current; + + unsigned int swelling_normal_float_voltage; + unsigned int swelling_drop_float_voltage; + unsigned int swelling_high_rechg_voltage; + unsigned int swelling_low_rechg_voltage; + unsigned int swelling_drop_voltage_condition; + +#if defined(CONFIG_CALC_TIME_TO_FULL) + unsigned int ttf_hv_12v_charge_current; + unsigned int ttf_hv_charge_current; + unsigned int ttf_hv_wireless_charge_current; + unsigned int ttf_wireless_charge_current; +#endif + +#if defined(CONFIG_STEP_CHARGING) + /* step charging */ + unsigned int *step_charging_condition; + unsigned int *step_charging_current; +#endif + + /* Monitor setting */ + sec_battery_monitor_polling_t polling_type; + /* for initial check */ + unsigned int monitor_initial_count; + + /* Battery check */ + sec_battery_check_t battery_check_type; + /* how many times do we need to check battery */ + unsigned int check_count; + /* ADC */ + /* battery check ADC maximum value */ + unsigned int check_adc_max; + /* battery check ADC minimum value */ + unsigned int check_adc_min; + + /* OVP/UVLO check */ + sec_battery_ovp_uvlo_t ovp_uvlo_check_type; + + sec_battery_thermal_source_t thermal_source; + + /* + * inbat_adc_table + * in-battery voltage check for table models: + * To read real battery voltage with Jig cable attached, + * dedicated hw pin & conversion table of adc-voltage are required + */ + sec_bat_adc_table_data_t *temp_adc_table; + sec_bat_adc_table_data_t *temp_amb_adc_table; + sec_bat_adc_table_data_t *usb_temp_adc_table; + sec_bat_adc_table_data_t *chg_temp_adc_table; + sec_bat_adc_table_data_t *wpc_temp_adc_table; + sec_bat_adc_table_data_t *slave_chg_temp_adc_table; + sec_bat_adc_table_data_t *blkt_temp_adc_table; + sec_bat_adc_table_data_t *inbat_adc_table; + + unsigned int temp_adc_table_size; + unsigned int temp_amb_adc_table_size; + unsigned int usb_temp_adc_table_size; + unsigned int chg_temp_adc_table_size; + unsigned int wpc_temp_adc_table_size; + unsigned int slave_chg_temp_adc_table_size; + unsigned int blkt_temp_adc_table_size; + unsigned int inbat_adc_table_size; + + sec_battery_temp_check_t temp_check_type; + unsigned int temp_check_count; + unsigned int usb_thermal_source; /* To confirm the usb temperature */ + unsigned int chg_temp_check; /* Control the charging current depending on the chg_thm */ + unsigned int chg_thermal_source; /* To confirm the charger temperature */ + unsigned int wpc_temp_check; + unsigned int wpc_thermal_source; /* To confirm the wpc temperature */ + unsigned int coil_temp_check; + unsigned int coil_thermal_source; /* To confirm the coil temperature */ + unsigned int slave_chg_temp_check; + unsigned int slave_thermal_source; /* To confirm the slave charger temperature */ + unsigned int blkt_temp_check; + unsigned int blkt_thermal_source; /* To confirm the blanket temperature */ + unsigned int inbat_voltage; + + /* + * limit can be ADC value or Temperature + * depending on temp_check_type + * temperature should be temp x 10 (0.1 degree) + */ + int temp_highlimit_threshold_normal; + int temp_highlimit_recovery_normal; + int temp_high_threshold_normal; + int temp_high_recovery_normal; + int temp_low_threshold_normal; + int temp_low_recovery_normal; + int temp_highlimit_threshold_lpm; + int temp_highlimit_recovery_lpm; + int temp_high_threshold_lpm; + int temp_high_recovery_lpm; + int temp_low_threshold_lpm; + int temp_low_recovery_lpm; + int wpc_high_threshold_normal; + int wpc_high_recovery_normal; + int wpc_low_threshold_normal; + int wpc_low_recovery_normal; + int chg_12v_high_temp; + int chg_high_temp; + int chg_high_temp_recovery; + unsigned int chg_charging_limit_current; + unsigned int chg_input_limit_current; + unsigned int wpc_temp_control_source; + unsigned int wpc_temp_lcd_on_control_source; + int wpc_high_temp; + int wpc_high_temp_recovery; + unsigned int wpc_charging_limit_current; + int wpc_lcd_on_high_temp; + int wpc_lcd_on_high_temp_rec; + unsigned int wpc_lcd_on_charging_limit_current; + unsigned int sleep_mode_limit_current; + unsigned int wc_full_input_limit_current; + unsigned int wc_cv_current; + unsigned int wc_cv_pack_current; + unsigned int max_charging_current; + int mix_high_temp; + int mix_high_chg_temp; + int mix_high_temp_recovery; + + /* If these is NOT full check type or NONE full check type, + * it is skipped + */ + /* 1st full check */ + sec_battery_full_charged_t full_check_type; + /* 2nd full check */ + sec_battery_full_charged_t full_check_type_2nd; + unsigned int full_check_count; + int chg_gpio_full_check; + /* 1 : active high, 0 : active low */ + int chg_polarity_full_check; + sec_battery_full_condition_t full_condition_type; + unsigned int full_condition_soc; + unsigned int full_condition_vcell; + unsigned int full_condition_avgvcell; + unsigned int full_condition_ocv; + + unsigned int recharge_check_count; + sec_battery_recharge_condition_t recharge_condition_type; + unsigned int recharge_condition_soc; + unsigned int recharge_condition_avgvcell; + unsigned int recharge_condition_vcell; + + /* reset charging for abnormal malfunction (0: not use) */ + unsigned long charging_reset_time; + + /* fuel gauge */ + char *fuelgauge_name; + int fg_irq; + unsigned long fg_irq_attr; + /* fuel alert SOC (-1: not use) */ + int fuel_alert_soc; + /* fuel alert can be repeated */ + bool repeated_fuelalert; + sec_fuelgauge_capacity_type_t capacity_calculation_type; + /* soc should be soc x 10 (0.1% degree) + * only for scaling + */ + int capacity_max; + int capacity_max_hv; + + int capacity_max_margin; + int capacity_min; + + unsigned int store_mode_charging_max; + unsigned int store_mode_charging_min; + /* charger */ + char *charger_name; + char *fgsrc_switch_name; + bool support_fgsrc_change; + + /* wireless charger */ + char *wireless_charger_name; + int wireless_cc_cv; + int set_cv_vout_in_low_capacity; + int wpc_det; + int wpc_en; + + int chg_gpio_en; + int chg_irq; + unsigned long chg_irq_attr; + /* float voltage (mV) */ + unsigned int chg_float_voltage; + unsigned int chg_float_voltage_conv; + bool is_b2b_model; + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + int num_age_step; + int age_step; + int age_data_length; + sec_age_data_t* age_data; +#endif + unsigned int siop_event_check_type; + unsigned int siop_call_cc_current; + unsigned int siop_call_cv_current; + + int siop_input_limit_current; + int siop_charging_limit_current; + int siop_hv_input_limit_current; + int siop_hv_charging_limit_current; + int siop_hv_12v_input_limit_current; + int siop_hv_12v_charging_limit_current; + + int siop_wireless_input_limit_current; + int siop_wireless_charging_limit_current; + int siop_hv_wireless_input_limit_current; + int siop_hv_wireless_charging_limit_current; + int wc_hero_stand_cc_cv; + int wc_hero_stand_cv_current; + int wc_hero_stand_hv_cv_current; + int siop_max_charging_current_0; + int siop_max_charging_current_10; + int siop_max_charging_current_70; + int siop_max_input_power; + + int default_input_current; + int default_charging_current; + int default_usb_input_current; + int default_usb_charging_current; + int max_input_voltage; + int max_input_current; + int pre_afc_work_delay; + int pre_wc_afc_work_delay; + + sec_charger_functions_t chg_functions_setting; + + bool fake_capacity; + int batt_channel; + + unsigned int battery_full_capacity; +#if defined(CONFIG_BATTERY_CISD) + unsigned int cisd_cap_high_thr; + unsigned int cisd_cap_low_thr; + unsigned int cisd_cap_limit; + unsigned int max_voltage_thr; + unsigned int cisd_alg_index; + unsigned int *ignore_cisd_index; + unsigned int *ignore_cisd_index_d; +#if defined(CONFIG_QH_ALGORITHM) + int cisd_qh_current_high_thr; + int cisd_qh_current_low_thr; + int cisd_qh_vfsoc_thr; +#endif +#endif + + /* ADC setting */ + unsigned int adc_check_count; + + unsigned int full_check_current_1st; + unsigned int full_check_current_2nd; + + unsigned int pd_charging_charge_power; + unsigned int nv_charge_power; + + unsigned int expired_time; + unsigned int recharging_expired_time; + int standard_curr; + + /* ADC type for each channel */ + unsigned int adc_type[]; +}; + +struct sec_charger_platform_data { + bool (*chg_gpio_init)(void); + + /* charging current for type (0: not use) */ + sec_charging_current_t *charging_current; + + /* wirelss charger */ + char *wireless_charger_name; + int wireless_cc_cv; + + int vbus_ctrl_gpio; + int chg_gpio_en; + /* float voltage (mV) */ + int chg_float_voltage; + int irq_gpio; + int chg_irq; + unsigned long chg_irq_attr; + + /* otg_en setting */ + int otg_en; + + /* OVP/UVLO check */ + sec_battery_ovp_uvlo_t ovp_uvlo_check_type; + /* 1st full check */ + sec_battery_full_charged_t full_check_type; + /* 2nd full check */ + sec_battery_full_charged_t full_check_type_2nd; + + sec_charger_functions_t chg_functions_setting; +}; + +struct sec_fuelgauge_platform_data { + bool (*fg_gpio_init)(void); + bool (*check_jig_status)(void); + int (*check_cable_callback)(void); + bool (*fuelalert_process)(bool); + + /* charging current for type (0: not use) */ + unsigned int full_check_current_1st; + unsigned int full_check_current_2nd; + + int jig_irq; + int jig_gpio; + int jig_low_active; + unsigned long jig_irq_attr; + + sec_battery_thermal_source_t thermal_source; + + int fg_irq; + unsigned long fg_irq_attr; + /* fuel alert SOC (-1: not use) */ + int fuel_alert_soc; + int fuel_alert_vol; + /* fuel alert can be repeated */ + bool repeated_fuelalert; + sec_fuelgauge_capacity_type_t capacity_calculation_type; + /* soc should be soc x 10 (0.1% degree) + * only for scaling + */ + int capacity_max; + int capacity_max_hv; + int capacity_max_margin; + int capacity_min; + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + int num_age_step; + int age_step; + int age_data_length; + sec_age_data_t* age_data; + unsigned int full_condition_soc; +#endif +}; + +#define sec_battery_platform_data_t \ + struct sec_battery_platform_data + +#define sec_charger_platform_data_t \ + struct sec_charger_platform_data + +#define sec_fuelgauge_platform_data_t \ + struct sec_fuelgauge_platform_data + +static inline struct power_supply *get_power_supply_by_name(char *name) +{ + if (!name) + return (struct power_supply *)NULL; + else + return power_supply_get_by_name(name); +} + +#define psy_do_property(name, function, property, value) \ +{ \ + struct power_supply *psy; \ + int ret; \ + psy = get_power_supply_by_name((name)); \ + if (!psy) { \ + pr_err("%s: Fail to "#function" psy (%s)\n", \ + __func__, (name)); \ + value.intval = 0; \ + } else { \ + if (psy->desc->function##_property != NULL) { \ + ret = psy->desc->function##_property(psy, (property), &(value)); \ + if (ret < 0) { \ + pr_err("%s: Fail to %s "#function" (%d=>%d)\n", \ + __func__, name, (property), ret); \ + value.intval = 0; \ + } \ + } \ + power_supply_put(psy); \ + } \ +} + +#ifndef CONFIG_OF +#define adc_init(pdev, pdata, channel) \ + (((pdata)->adc_api)[((((pdata)->adc_type[(channel)]) < \ + SEC_BATTERY_ADC_TYPE_NUM) ? ((pdata)->adc_type[(channel)]) : \ + SEC_BATTERY_ADC_TYPE_NONE)].init((pdev))) + +#define adc_exit(pdata, channel) \ + (((pdata)->adc_api)[((pdata)->adc_type[(channel)])].exit()) + +#define adc_read(pdata, channel) \ + (((pdata)->adc_api)[((pdata)->adc_type[(channel)])].read((channel))) +#endif + +#define get_battery_data(driver) \ + (((struct battery_data_t *)(driver)->pdata->battery_data) \ + [(driver)->pdata->battery_type]) + +#define is_hv_wireless_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_HV_WIRELESS || \ + cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_ETX || \ + cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_STAND || \ + cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE) + +#define is_nv_wireless_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_WIRELESS || \ + cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS || \ + cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK || \ + cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA || \ + cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND || \ + cable_type == SEC_BATTERY_CABLE_WIRELESS_VEHICLE || \ + cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) + +#define is_wireless_type(cable_type) \ + (is_hv_wireless_type(cable_type) || is_nv_wireless_type(cable_type)) + +#define is_not_wireless_type(cable_type) ( \ + cable_type != SEC_BATTERY_CABLE_WIRELESS && \ + cable_type != SEC_BATTERY_CABLE_PMA_WIRELESS && \ + cable_type != SEC_BATTERY_CABLE_WIRELESS_PACK && \ + cable_type != SEC_BATTERY_CABLE_WIRELESS_PACK_TA && \ + cable_type != SEC_BATTERY_CABLE_WIRELESS_STAND && \ + cable_type != SEC_BATTERY_CABLE_HV_WIRELESS && \ + cable_type != SEC_BATTERY_CABLE_HV_WIRELESS_ETX && \ + cable_type != SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV && \ + cable_type != SEC_BATTERY_CABLE_WIRELESS_HV_STAND && \ + cable_type != SEC_BATTERY_CABLE_WIRELESS_VEHICLE && \ + cable_type != SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE) + +#define is_wired_type(cable_type) \ + (is_not_wireless_type(cable_type) && (cable_type != SEC_BATTERY_CABLE_NONE)) + +#define is_hv_qc_wire_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_QC20 || \ + cable_type == SEC_BATTERY_CABLE_QC30) + +#define is_hv_afc_wire_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_9V_ERR || \ + cable_type == SEC_BATTERY_CABLE_9V_TA || \ + cable_type == SEC_BATTERY_CABLE_9V_UNKNOWN || \ + cable_type == SEC_BATTERY_CABLE_12V_TA) + +#define is_hv_wire_9v_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_9V_ERR || \ + cable_type == SEC_BATTERY_CABLE_9V_TA || \ + cable_type == SEC_BATTERY_CABLE_9V_UNKNOWN || \ + cable_type == SEC_BATTERY_CABLE_QC20) + +#define is_hv_wire_12v_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_12V_TA || \ + cable_type == SEC_BATTERY_CABLE_QC30) + +#define is_hv_wire_type(cable_type) ( \ + is_hv_afc_wire_type(cable_type) || is_hv_qc_wire_type(cable_type)) + +#define is_nocharge_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_NONE || \ + cable_type == SEC_BATTERY_CABLE_OTG || \ + cable_type == SEC_BATTERY_CABLE_POWER_SHARING) + +#define is_pd_wire_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_PDIC) + +#define is_slate_mode(battery) ((battery->current_event & SEC_BAT_CURRENT_EVENT_SLATE) \ + == SEC_BAT_CURRENT_EVENT_SLATE) +#endif /* __SEC_CHARGING_COMMON_H */ diff --git a/drivers/battery_v2/include/sec_cisd.h b/drivers/battery_v2/include/sec_cisd.h new file mode 100644 index 000000000000..b75412045726 --- /dev/null +++ b/drivers/battery_v2/include/sec_cisd.h @@ -0,0 +1,314 @@ +/* + * sec_cisd.h + * Samsung Mobile Charger Header + * + * Copyright (C) 2015 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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_CISD_H +#define __SEC_CISD_H __FILE__ + +#define CISD_STATE_NONE 0x00 +#define CISD_STATE_CAP_OVERFLOW 0x01 +#define CISD_STATE_VOLT_DROP 0x02 +#define CISD_STATE_SOC_DROP 0x04 +#define CISD_STATE_RESET 0x08 +#define CISD_STATE_LEAK_A 0x10 +#define CISD_STATE_LEAK_B 0x20 +#define CISD_STATE_LEAK_C 0x40 +#define CISD_STATE_LEAK_D 0x80 +#define CISD_STATE_OVER_VOLTAGE 0x100 +#define CISD_STATE_LEAK_E 0x200 +#define CISD_STATE_LEAK_F 0x400 +#define CISD_STATE_LEAK_G 0x800 + +#define is_cisd_check_type(cable_type) ( \ + cable_type == SEC_BATTERY_CABLE_TA || \ + cable_type == SEC_BATTERY_CABLE_9V_TA || \ + cable_type == SEC_BATTERY_CABLE_9V_UNKNOWN || \ + cable_type == SEC_BATTERY_CABLE_9V_ERR || \ + cable_type == SEC_BATTERY_CABLE_PDIC) + +#if 0 +enum cisd_data { + CISD_DATA_FULL_COUNT = 0, + CISD_DATA_CAP_MAX, + CISD_DATA_CAP_MIN, + CISD_DATA_CAP_ONCE, + CISD_DATA_LEAKAGE_A, + CISD_DATA_LEAKAGE_B, + CISD_DATA_LEAKAGE_C, + CISD_DATA_LEAKAGE_D, + CISD_DATA_CAP_PER_TIME, + CISD_DATA_ERRCAP_LOW, + CISD_DATA_ERRCAP_HIGH, + + CISD_DATA_OVER_VOLTAGE, + CISD_DATA_LEAKAGE_E, + CISD_DATA_LEAKAGE_F, + CISD_DATA_LEAKAGE_G, + CISD_DATA_RECHARGING_TIME, + CISD_DATA_VALERT_COUNT, + CISD_DATA_CYCLE, + CISD_DATA_WIRE_COUNT, + CISD_DATA_WIRELESS_COUNT, + CISD_DATA_HIGH_TEMP_SWELLING, + + CISD_DATA_LOW_TEMP_SWELLING, + CISD_DATA_SWELLING_CHARGING_COUNT, + CISD_DATA_SAFETY_TIMER_3, + CISD_DATA_SAFETY_TIMER_5, + CISD_DATA_SAFETY_TIMER_10, + CISD_DATA_AICL_COUNT, + CISD_DATA_BATT_TEMP_MAX, + CISD_DATA_BATT_TEMP_MIN, + CISD_DATA_CHG_TEMP_MAX, + CISD_DATA_CHG_TEMP_MIN, + + CISD_DATA_WPC_TEMP_MAX, + CISD_DATA_WPC_TEMP_MIN, + CISD_UNSAFE_VOLTAGE, + CISD_UNSAFE_TEMPERATURE, + CISD_SAFETY_TIMER, + CISD_VSYS_OVP, + CISD_VBAT_OVP, + CISD_WATER_DETECT, + CISD_AFC_FAIL, + + CISD_DATA_MAX, +}; +#endif + +enum cisd_data { + CISD_DATA_RESET_ALG = 0, + + CISD_DATA_ALG_INDEX, + CISD_DATA_FULL_COUNT, + CISD_DATA_CAP_MAX, + CISD_DATA_CAP_MIN, + CISD_DATA_RECHARGING_COUNT, + CISD_DATA_VALERT_COUNT, + CISD_DATA_CYCLE, + CISD_DATA_WIRE_COUNT, + CISD_DATA_WIRELESS_COUNT, + CISD_DATA_HIGH_TEMP_SWELLING, + + CISD_DATA_LOW_TEMP_SWELLING, + CISD_DATA_SWELLING_CHARGING_COUNT, + CISD_DATA_SWELLING_FULL_CNT, + CISD_DATA_SWELLING_RECOVERY_CNT, + CISD_DATA_AICL_COUNT, + CISD_DATA_BATT_TEMP_MAX, + CISD_DATA_BATT_TEMP_MIN, + CISD_DATA_CHG_TEMP_MAX, + CISD_DATA_CHG_TEMP_MIN, + CISD_DATA_WPC_TEMP_MAX, + + CISD_DATA_WPC_TEMP_MIN, + CISD_DATA_USB_TEMP_MAX, + CISD_DATA_USB_TEMP_MIN, + CISD_DATA_CHG_BATT_TEMP_MAX, + CISD_DATA_CHG_BATT_TEMP_MIN, + CISD_DATA_CHG_CHG_TEMP_MAX, + CISD_DATA_CHG_CHG_TEMP_MIN, + CISD_DATA_CHG_WPC_TEMP_MAX, + CISD_DATA_CHG_WPC_TEMP_MIN, + CISD_DATA_CHG_USB_TEMP_MAX, + + CISD_DATA_CHG_USB_TEMP_MIN, + CISD_DATA_USB_OVERHEAT_CHARGING, /* 32 */ + CISD_DATA_UNSAFETY_VOLTAGE, + CISD_DATA_UNSAFETY_TEMPERATURE, + CISD_DATA_SAFETY_TIMER, + CISD_DATA_VSYS_OVP, + CISD_DATA_VBAT_OVP, + CISD_DATA_AFC_FAIL, + CISD_DATA_BUCK_OFF, + CISD_DATA_WATER_DETECT, + + CISD_DATA_DROP_VALUE, + + CISD_DATA_MAX, +}; + +enum cisd_data_per_day { + CISD_DATA_FULL_COUNT_PER_DAY = CISD_DATA_MAX, + + CISD_DATA_CAP_MAX_PER_DAY, + CISD_DATA_CAP_MIN_PER_DAY, + CISD_DATA_RECHARGING_COUNT_PER_DAY, + CISD_DATA_VALERT_COUNT_PER_DAY, + CISD_DATA_WIRE_COUNT_PER_DAY, + CISD_DATA_WIRELESS_COUNT_PER_DAY, + CISD_DATA_HIGH_TEMP_SWELLING_PER_DAY, + CISD_DATA_LOW_TEMP_SWELLING_PER_DAY, + CISD_DATA_SWELLING_CHARGING_COUNT_PER_DAY, + CISD_DATA_SWELLING_FULL_CNT_PER_DAY, + + CISD_DATA_SWELLING_RECOVERY_CNT_PER_DAY, + CISD_DATA_AICL_COUNT_PER_DAY, + CISD_DATA_BATT_TEMP_MAX_PER_DAY, + CISD_DATA_BATT_TEMP_MIN_PER_DAY, + CISD_DATA_CHG_TEMP_MAX_PER_DAY, + CISD_DATA_CHG_TEMP_MIN_PER_DAY, + CISD_DATA_WPC_TEMP_MAX_PER_DAY, + CISD_DATA_WPC_TEMP_MIN_PER_DAY, + CISD_DATA_USB_TEMP_MAX_PER_DAY, + CISD_DATA_USB_TEMP_MIN_PER_DAY, + + CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY, + CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY, + CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY, + CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY, + CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY, + CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY, + CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY, + CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY, + CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY, + CISD_DATA_UNSAFE_VOLTAGE_PER_DAY, + + CISD_DATA_UNSAFE_TEMPERATURE_PER_DAY, + CISD_DATA_SAFETY_TIMER_PER_DAY, /* 32 */ + CISD_DATA_VSYS_OVP_PER_DAY, + CISD_DATA_VBAT_OVP_PER_DAY, + CISD_DATA_AFC_FAIL_PER_DAY, + CISD_DATA_BUCK_OFF_PER_DAY, + CISD_DATA_WATER_DETECT_PER_DAY, + CISD_DATA_DROP_VALUE_PER_DAY, + + CISD_DATA_MAX_PER_DAY, +}; + +enum { + WC_DATA_INDEX = 0, + WC_SNGL_NOBLE, + WC_SNGL_VEHICLE, + WC_SNGL_MINI, + WC_SNGL_ZERO, + WC_SNGL_DREAM, + WC_STAND_HERO, + WC_STAND_DREAM, + WC_EXT_PACK, + WC_EXT_PACK_TA, + + WC_DATA_MAX, +}; + +enum { + CISD_CABLE_INDEX = 0, + CISD_CABLE_TA, + CISD_CABLE_AFC, + CISD_CABLE_AFC_FAIL, + CISD_CABLE_QC, + CISD_CABLE_QC_FAIL, + CISD_CABLE_PD, + CISD_CABLE_PD_HIGH, + + CISD_CABLE_TYPE_MAX, +}; + +extern const char *cisd_data_str[]; +extern const char *cisd_data_str_d[]; + +#define PAD_INDEX_STRING "INDEX" +#define PAD_INDEX_VALUE 1 +#define PAD_JSON_STRING "PAD_0x" +#define MAX_PAD_ID 0xFF + +struct pad_data { + unsigned int id; + unsigned int count; + + struct pad_data* prev; + struct pad_data* next; +}; + +struct cisd { + unsigned int cisd_alg_index; + unsigned int state; + + unsigned int delay_time; + int diff_volt_now; + int diff_cap_now; + int curr_cap_max; + int err_cap_max_thrs; + int err_cap_high_thr; + int err_cap_low_thr; + int overflow_cap_thr; + unsigned int cc_delay_time; + unsigned int full_delay_time; + unsigned int lcd_off_delay_time; + unsigned int recharge_delay_time; + unsigned int diff_time; + unsigned long cc_start_time; + unsigned long full_start_time; + unsigned long lcd_off_start_time; + unsigned long overflow_start_time; + unsigned long charging_end_time; + unsigned long charging_end_time_2; + unsigned int recharge_count; + unsigned int recharge_count_2; + unsigned int recharge_count_thres; + unsigned long leakage_e_time; + unsigned long leakage_f_time; + unsigned long leakage_g_time; + int current_max_thres; + int charging_current_thres; + int current_avg_thres; + + unsigned int ab_vbat_max_count; + unsigned int ab_vbat_check_count; + unsigned int max_voltage_thr; + + /* Big Data Field */ + int capacity_now; + int data[CISD_DATA_MAX_PER_DAY]; + int cable_data[CISD_CABLE_TYPE_MAX]; + + struct mutex padlock; + struct pad_data* pad_array; + unsigned int pad_count; + +#if defined(CONFIG_QH_ALGORITHM) + unsigned long prev_time; + unsigned long qh_valid_time; + int prev_qh_value; + int prev_qh_vfsoc; + int qh_value_now; + int qh_vfsoc_now; +#endif +}; + +extern struct cisd *gcisd; +static inline void set_cisd_data(int type, int value) +{ + if (gcisd && (type >= CISD_DATA_RESET_ALG && type < CISD_DATA_MAX_PER_DAY)) + gcisd->data[type] = value; +} +static inline int get_cisd_data(int type) +{ + if (!gcisd || (type < CISD_DATA_RESET_ALG || type >= CISD_DATA_MAX_PER_DAY)) + return -1; + + return gcisd->data[type]; +} +static inline void increase_cisd_count(int type) +{ + if (gcisd && (type >= CISD_DATA_RESET_ALG && type < CISD_DATA_MAX_PER_DAY)) + gcisd->data[type]++; +} + +void init_cisd_pad_data(struct cisd *cisd); +void count_cisd_pad_data(struct cisd *cisd, unsigned int pad_id); + +#endif /* __SEC_CISD_H */ diff --git a/drivers/battery_v2/sec_adc.c b/drivers/battery_v2/sec_adc.c new file mode 100644 index 000000000000..891b5e20d876 --- /dev/null +++ b/drivers/battery_v2/sec_adc.c @@ -0,0 +1,462 @@ +/* + * sec_adc.c + * Samsung Mobile Battery Driver + * + * Copyright (C) 2012 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 "include/sec_adc.h" + +#define DEBUG + +static struct qpnp_vadc_chip *adc_client; + +struct adc_list { + const char* name; + struct iio_channel *channel; + struct qpnp_vadc_result prev_value; + //bool is_used; +}; + +static struct adc_list batt_adc_list[SEC_BAT_ADC_CHANNEL_NUM] = { + {.name = "adc-cable"}, + {.name = "adc-bat"}, + {.name = "adc-temp"}, + {.name = "adc-temp"}, + {.name = "adc-full"}, + {.name = "adc-volt"}, + {.name = "adc-chg-temp"}, + {.name = "adc-in-bat"}, + {.name = "adc-dischg"}, + {.name = "adc-dischg-ntc"}, + {.name = "adc-wpc-temp"}, + {.name = "adc-slave-chg-temp"}, + {.name = "adc-usb-temp"}, +}; +static void sec_bat_adc_ap_init(struct platform_device *pdev, + struct sec_battery_info *battery) +{ + adc_client = qpnp_get_vadc(battery->dev, "sec-battery"); + + if (IS_ERR(adc_client)) { + int rc; + rc = PTR_ERR(adc_client); + if (rc != -EPROBE_DEFER) + pr_err("%s: Fail to get vadc %d\n", __func__, rc); + } +} + +static void sec_bat_read_adc(struct qpnp_vadc_chip *vadc, int channel, + struct qpnp_vadc_result *result, int adc_channel) +{ + int ret = 0; + int retry_cnt = RETRY_CNT; + + do { + ret = qpnp_vadc_read(vadc, channel, result); + retry_cnt--; + } while ((retry_cnt > 0) && ret); + + if (retry_cnt <= 0) { + pr_err("%s: Error in ADC(%d) retry_cnt(%d)\n", __func__, adc_channel, retry_cnt); + result->adc_code = batt_adc_list[adc_channel].prev_value.adc_code; + result->physical = batt_adc_list[adc_channel].prev_value.physical; + } else { + batt_adc_list[adc_channel].prev_value.adc_code = result->adc_code; + batt_adc_list[adc_channel].prev_value.physical = result->physical; + } + pr_debug("%s, channel: 0x%x, adc: %d, physical: %lld\n", __func__, channel, result->adc_code, result->physical); +} + +static int sec_bat_adc_ap_read(struct sec_battery_info *battery, int channel) +{ + struct qpnp_vadc_result results; + int data = -1; + + switch (channel) + { + case SEC_BAT_ADC_CHANNEL_TEMP: + sec_bat_read_adc(adc_client, battery->pdata->temp_adc_channel, &results, channel); + data = results.adc_code; + break; + case SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT: + data = 33000; + break; + case SEC_BAT_ADC_CHANNEL_BAT_CHECK: + sec_bat_read_adc(adc_client, battery->pdata->batt_channel, &results, channel); + data = results.adc_code; + break; + case SEC_BAT_ADC_CHANNEL_USB_TEMP: + sec_bat_read_adc(adc_client, battery->pdata->usb_temp_adc_channel, &results, channel); + data = results.adc_code; + break; + case SEC_BAT_ADC_CHANNEL_CHG_TEMP: + sec_bat_read_adc(adc_client, battery->pdata->chg_temp_adc_channel, &results, channel); + data = results.adc_code; + break; + case SEC_BAT_ADC_CHANNEL_WPC_TEMP: + sec_bat_read_adc(adc_client, battery->pdata->temp_adc_channel, &results, channel); + data = results.adc_code; + break; + case SEC_BAT_ADC_CHANNEL_INBAT_VOLTAGE: + sec_bat_read_adc(adc_client, VBAT_SNS, &results, channel); + data = ((int)results.physical)/1000; + break; + case SEC_BAT_ADC_CHANNEL_BLKT_TEMP: + sec_bat_read_adc(adc_client, battery->pdata->blkt_temp_adc_channel, &results, channel); + data = results.adc_code; + break; + default : + break; + } + + return data; +} + +static void sec_bat_adc_ap_exit(void) +{ + return; +} + +static void sec_bat_adc_none_init(struct platform_device *pdev) +{ +} + +static int sec_bat_adc_none_read(int channel) +{ + return 0; +} + +static void sec_bat_adc_none_exit(void) +{ +} + +static void sec_bat_adc_ic_init(struct platform_device *pdev) +{ +} + +static int sec_bat_adc_ic_read(int channel) +{ + return 0; +} + +static void sec_bat_adc_ic_exit(void) +{ +} +static int adc_read_type(struct sec_battery_info *battery, int channel) +{ + int adc = 0; + + switch (battery->pdata->temp_adc_type) + { + case SEC_BATTERY_ADC_TYPE_NONE : + adc = sec_bat_adc_none_read(channel); + break; + case SEC_BATTERY_ADC_TYPE_AP : + adc = sec_bat_adc_ap_read(battery, channel); + break; + case SEC_BATTERY_ADC_TYPE_IC : + adc = sec_bat_adc_ic_read(channel); + break; + case SEC_BATTERY_ADC_TYPE_NUM : + break; + default : + break; + } + pr_debug("[%s] [%d] ADC = %d\n", __func__, channel, adc); + return adc; + +} + +static void adc_init_type(struct platform_device *pdev, + struct sec_battery_info *battery) +{ + switch (battery->pdata->temp_adc_type) + { + case SEC_BATTERY_ADC_TYPE_NONE : + sec_bat_adc_none_init(pdev); + break; + case SEC_BATTERY_ADC_TYPE_AP : + sec_bat_adc_ap_init(pdev, battery); + break; + case SEC_BATTERY_ADC_TYPE_IC : + sec_bat_adc_ic_init(pdev); + break; + case SEC_BATTERY_ADC_TYPE_NUM : + break; + default : + break; + } +} + +static void adc_exit_type(struct sec_battery_info *battery) +{ + switch (battery->pdata->temp_adc_type) + { + case SEC_BATTERY_ADC_TYPE_NONE : + sec_bat_adc_none_exit(); + break; + case SEC_BATTERY_ADC_TYPE_AP : + sec_bat_adc_ap_exit(); + break; + case SEC_BATTERY_ADC_TYPE_IC : + sec_bat_adc_ic_exit(); + break; + case SEC_BATTERY_ADC_TYPE_NUM : + break; + default : + break; + } +} + +int sec_bat_get_adc_data(struct sec_battery_info *battery, + int adc_ch, int count) +{ + int adc_data = 0; + int adc_max = 0; + int adc_min = 0; + int adc_total = 0; + int i = 0; + + for (i = 0; i < count; i++) { + mutex_lock(&battery->adclock); +#ifdef CONFIG_OF + adc_data = adc_read_type(battery, adc_ch); +#else + adc_data = adc_read(battery->pdata, adc_ch); +#endif + mutex_unlock(&battery->adclock); + + if (adc_data < 0) + goto err; + + if (i != 0) { + if (adc_data > adc_max) + adc_max = adc_data; + else if (adc_data < adc_min) + adc_min = adc_data; + } else { + adc_max = adc_data; + adc_min = adc_data; + } + adc_total += adc_data; + } + + return (adc_total - adc_max - adc_min) / (count - 2); +err: + return adc_data; +} + +int adc_read(struct sec_battery_info *battery, int channel) +{ + int adc = 0; + + adc = adc_read_type(battery, channel); + + dev_dbg(battery->dev, "[%s]adc = %d\n", __func__, adc); + + return adc; +} + +int sec_bat_get_adc_value( + struct sec_battery_info *battery, int channel) +{ + int adc = 0; + + adc = sec_bat_get_adc_data(battery, channel, + battery->pdata->adc_check_count); + + if (adc < 0) { + dev_err(battery->dev, + "%s: Error in ADC\n", __func__); + return adc; + } + + return adc; +} + +int sec_bat_get_charger_type_adc + (struct sec_battery_info *battery) +{ + /* It is true something valid is + connected to the device for charging. + By default this something is considered to be USB.*/ + int result = SEC_BATTERY_CABLE_USB; + + int adc = 0; + int i = 0; + + /* Do NOT check cable type when cable_switch_check() returns false + * and keep current cable type + */ + if (battery->pdata->cable_switch_check && + !battery->pdata->cable_switch_check()) + return battery->cable_type; + + adc = sec_bat_get_adc_value(battery, + SEC_BAT_ADC_CHANNEL_CABLE_CHECK); + + /* Do NOT check cable type when cable_switch_normal() returns false + * and keep current cable type + */ + if (battery->pdata->cable_switch_normal && + !battery->pdata->cable_switch_normal()) + return battery->cable_type; + + for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++) + if ((adc > battery->pdata->cable_adc_value[i].min) && + (adc < battery->pdata->cable_adc_value[i].max)) + break; + if (i >= SEC_BATTERY_CABLE_MAX) + dev_err(battery->dev, + "%s : default USB\n", __func__); + else + result = i; + + dev_dbg(battery->dev, "%s : result(%d), adc(%d)\n", + __func__, result, adc); + + return result; +} + +bool sec_bat_get_value_by_adc( + struct sec_battery_info *battery, + enum sec_battery_adc_channel channel, + union power_supply_propval *value) +{ + int temp = 0; + int temp_adc; + int low = 0; + int high = 0; + int mid = 0; + const sec_bat_adc_table_data_t *temp_adc_table = {0 , }; + unsigned int temp_adc_table_size = 0; + + temp_adc = sec_bat_get_adc_value(battery, channel); + if (temp_adc < 0) + return true; + + switch (channel) { + case SEC_BAT_ADC_CHANNEL_TEMP: + temp_adc_table = battery->pdata->temp_adc_table; + temp_adc_table_size = + battery->pdata->temp_adc_table_size; + battery->temp_adc = temp_adc; + break; + case SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT: + temp_adc_table = battery->pdata->temp_amb_adc_table; + temp_adc_table_size = + battery->pdata->temp_amb_adc_table_size; + battery->temp_ambient_adc = temp_adc; + break; + case SEC_BAT_ADC_CHANNEL_USB_TEMP: + temp_adc_table = battery->pdata->usb_temp_adc_table; + temp_adc_table_size = + battery->pdata->usb_temp_adc_table_size; + battery->usb_temp_adc = temp_adc; + break; + case SEC_BAT_ADC_CHANNEL_CHG_TEMP: + temp_adc_table = battery->pdata->chg_temp_adc_table; + temp_adc_table_size = + battery->pdata->chg_temp_adc_table_size; + battery->chg_temp_adc = temp_adc; + break; + case SEC_BAT_ADC_CHANNEL_WPC_TEMP: /* Coil Therm */ + temp_adc_table = battery->pdata->wpc_temp_adc_table; + temp_adc_table_size = + battery->pdata->wpc_temp_adc_table_size; + battery->wpc_temp_adc = temp_adc; + battery->coil_temp_adc = temp_adc; + break; + case SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP: + temp_adc_table = battery->pdata->slave_chg_temp_adc_table; + temp_adc_table_size = + battery->pdata->slave_chg_temp_adc_table_size; + battery->slave_chg_temp_adc = temp_adc; + break; + case SEC_BAT_ADC_CHANNEL_BLKT_TEMP: + temp_adc_table = battery->pdata->blkt_temp_adc_table; + temp_adc_table_size = + battery->pdata->blkt_temp_adc_table_size; + battery->blkt_temp_adc = temp_adc; + break; + default: + dev_err(battery->dev, + "%s: Invalid Property\n", __func__); + return false; + } + + if (temp_adc_table[0].adc >= temp_adc) { + temp = temp_adc_table[0].data; + goto temp_by_adc_goto; + } else if (temp_adc_table[temp_adc_table_size-1].adc <= temp_adc) { + temp = temp_adc_table[temp_adc_table_size-1].data; + goto temp_by_adc_goto; + } + + high = temp_adc_table_size - 1; + + while (low <= high) { + mid = (low + high) / 2; + if (temp_adc_table[mid].adc > temp_adc) + high = mid - 1; + else if (temp_adc_table[mid].adc < temp_adc) + low = mid + 1; + else { + temp = temp_adc_table[mid].data; + goto temp_by_adc_goto; + } + } + + temp = temp_adc_table[high].data; + temp += ((temp_adc_table[low].data - temp_adc_table[high].data) * + (temp_adc - temp_adc_table[high].adc)) / + (temp_adc_table[low].adc - temp_adc_table[high].adc); + +temp_by_adc_goto: + value->intval = temp; + + dev_info(battery->dev, + "%s:[%d] Temp(%d), Temp-ADC(%d)\n", + __func__,channel, temp, temp_adc); + + return true; +} + +bool sec_bat_check_vf_adc(struct sec_battery_info *battery) +{ + int adc = 0; + + adc = sec_bat_get_adc_data(battery, + SEC_BAT_ADC_CHANNEL_BAT_CHECK, + battery->pdata->adc_check_count); + if (adc < 0) { + dev_err(battery->dev, "%s: VF ADC error\n", __func__); + adc = battery->check_adc_value; + } else + battery->check_adc_value = adc; + + dev_info(battery->dev, "%s: adc (%d)\n", __func__, battery->check_adc_value); + if ((battery->check_adc_value <= battery->pdata->check_adc_max) && + (battery->check_adc_value >= battery->pdata->check_adc_min)) { + return true; + } else { + return false; + } +} + +void adc_init(struct platform_device *pdev, struct sec_battery_info *battery) +{ + adc_init_type(pdev, battery); +} + +void adc_exit(struct sec_battery_info *battery) +{ + adc_exit_type(battery); +} + diff --git a/drivers/battery_v2/sec_battery.c b/drivers/battery_v2/sec_battery.c new file mode 100644 index 000000000000..4278efa0d65e --- /dev/null +++ b/drivers/battery_v2/sec_battery.c @@ -0,0 +1,10051 @@ +/* + * sec_battery.c + * Samsung Mobile Battery Driver + * + * Copyright (C) 2012 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 "include/sec_battery.h" +#include +#include "include/sec_battery_ttf.h" +#if defined(CONFIG_SEC_ABC) +#include +#endif +#ifdef CONFIG_SAMSUNG_BATTERY_DISALLOW_DEEP_SLEEP +#include +struct clk * xo_chr = NULL; +#endif + +static struct device_attribute sec_battery_attrs[] = { + SEC_BATTERY_ATTR(batt_reset_soc), + SEC_BATTERY_ATTR(batt_read_raw_soc), + SEC_BATTERY_ATTR(batt_read_adj_soc), + SEC_BATTERY_ATTR(batt_type), + SEC_BATTERY_ATTR(batt_vfocv), + SEC_BATTERY_ATTR(batt_vol_adc), + SEC_BATTERY_ATTR(batt_vol_adc_cal), + SEC_BATTERY_ATTR(batt_vol_aver), + SEC_BATTERY_ATTR(batt_vol_adc_aver), + SEC_BATTERY_ATTR(batt_current_ua_now), + SEC_BATTERY_ATTR(batt_current_ua_avg), + SEC_BATTERY_ATTR(batt_filter_cfg), + SEC_BATTERY_ATTR(batt_temp), + SEC_BATTERY_ATTR(batt_temp_adc), + SEC_BATTERY_ATTR(batt_temp_aver), + SEC_BATTERY_ATTR(batt_temp_adc_aver), + SEC_BATTERY_ATTR(usb_temp), + SEC_BATTERY_ATTR(usb_temp_adc), + SEC_BATTERY_ATTR(chg_temp), + SEC_BATTERY_ATTR(chg_temp_adc), + SEC_BATTERY_ATTR(slave_chg_temp), + SEC_BATTERY_ATTR(slave_chg_temp_adc), + SEC_BATTERY_ATTR(blkt_temp), + SEC_BATTERY_ATTR(blkt_temp_adc), + SEC_BATTERY_ATTR(batt_vf_adc), + SEC_BATTERY_ATTR(batt_slate_mode), + SEC_BATTERY_ATTR(batt_lp_charging), + SEC_BATTERY_ATTR(siop_activated), + SEC_BATTERY_ATTR(siop_level), + SEC_BATTERY_ATTR(siop_event), + SEC_BATTERY_ATTR(batt_charging_source), + SEC_BATTERY_ATTR(fg_reg_dump), + SEC_BATTERY_ATTR(fg_reset_cap), + SEC_BATTERY_ATTR(fg_capacity), + SEC_BATTERY_ATTR(fg_asoc), + SEC_BATTERY_ATTR(auth), + SEC_BATTERY_ATTR(chg_current_adc), + SEC_BATTERY_ATTR(wc_adc), + SEC_BATTERY_ATTR(wc_status), + SEC_BATTERY_ATTR(wc_enable), + SEC_BATTERY_ATTR(wc_control), + SEC_BATTERY_ATTR(wc_control_cnt), + SEC_BATTERY_ATTR(led_cover), + SEC_BATTERY_ATTR(hv_charger_status), + SEC_BATTERY_ATTR(hv_wc_charger_status), + SEC_BATTERY_ATTR(hv_charger_set), + SEC_BATTERY_ATTR(factory_mode), + SEC_BATTERY_ATTR(store_mode), + SEC_BATTERY_ATTR(update), + SEC_BATTERY_ATTR(test_mode), + SEC_BATTERY_ATTR(call), + SEC_BATTERY_ATTR(2g_call), + SEC_BATTERY_ATTR(talk_gsm), + SEC_BATTERY_ATTR(3g_call), + SEC_BATTERY_ATTR(talk_wcdma), + SEC_BATTERY_ATTR(music), + SEC_BATTERY_ATTR(video), + SEC_BATTERY_ATTR(browser), + SEC_BATTERY_ATTR(hotspot), + SEC_BATTERY_ATTR(camera), + SEC_BATTERY_ATTR(camcorder), + SEC_BATTERY_ATTR(data_call), + SEC_BATTERY_ATTR(wifi), + SEC_BATTERY_ATTR(wibro), + SEC_BATTERY_ATTR(lte), + SEC_BATTERY_ATTR(lcd), + SEC_BATTERY_ATTR(gps), + SEC_BATTERY_ATTR(event), + SEC_BATTERY_ATTR(batt_temp_table), + SEC_BATTERY_ATTR(batt_high_current_usb), +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + SEC_BATTERY_ATTR(test_charge_current), +#endif + SEC_BATTERY_ATTR(set_stability_test), + SEC_BATTERY_ATTR(batt_capacity_max), + SEC_BATTERY_ATTR(batt_inbat_voltage), + SEC_BATTERY_ATTR(batt_inbat_voltage_ocv), + SEC_BATTERY_ATTR(check_slave_chg), + SEC_BATTERY_ATTR(batt_inbat_wireless_cs100), + SEC_BATTERY_ATTR(hmt_ta_connected), + SEC_BATTERY_ATTR(hmt_ta_charge), +#if defined(CONFIG_BATTERY_AGE_FORECAST) + SEC_BATTERY_ATTR(fg_cycle), + SEC_BATTERY_ATTR(fg_full_voltage), + SEC_BATTERY_ATTR(fg_fullcapnom), + SEC_BATTERY_ATTR(battery_cycle), +#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE) + SEC_BATTERY_ATTR(batt_after_manufactured), +#endif +#endif + SEC_BATTERY_ATTR(batt_wpc_temp), + SEC_BATTERY_ATTR(batt_wpc_temp_adc), + SEC_BATTERY_ATTR(batt_coil_temp), /* Wireless Coil therm */ + SEC_BATTERY_ATTR(batt_coil_temp_adc), /* Wireless Coil therm */ + SEC_BATTERY_ATTR(mst_switch_test), /* MFC MST switch test */ +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + SEC_BATTERY_ATTR(batt_wireless_firmware_update), + SEC_BATTERY_ATTR(otp_firmware_result), + SEC_BATTERY_ATTR(wc_ic_grade), + SEC_BATTERY_ATTR(otp_firmware_ver_bin), + SEC_BATTERY_ATTR(otp_firmware_ver), + SEC_BATTERY_ATTR(tx_firmware_result), + SEC_BATTERY_ATTR(tx_firmware_ver), + SEC_BATTERY_ATTR(batt_tx_status), +#endif + SEC_BATTERY_ATTR(wc_vout), + SEC_BATTERY_ATTR(wc_vrect), +#if defined(CONFIG_WIRELESS_CHARGER_HIGH_VOLTAGE) + SEC_BATTERY_ATTR(batt_hv_wireless_status), + SEC_BATTERY_ATTR(batt_hv_wireless_pad_ctrl), +#endif + SEC_BATTERY_ATTR(wc_op_freq), + SEC_BATTERY_ATTR(wc_cmd_info), + SEC_BATTERY_ATTR(batt_tune_float_voltage), + SEC_BATTERY_ATTR(batt_tune_input_charge_current), + SEC_BATTERY_ATTR(batt_tune_fast_charge_current), + SEC_BATTERY_ATTR(batt_tune_ui_term_cur_1st), + SEC_BATTERY_ATTR(batt_tune_ui_term_cur_2nd), + SEC_BATTERY_ATTR(batt_tune_temp_high_normal), + SEC_BATTERY_ATTR(batt_tune_temp_high_rec_normal), + SEC_BATTERY_ATTR(batt_tune_temp_low_normal), + SEC_BATTERY_ATTR(batt_tune_temp_low_rec_normal), + SEC_BATTERY_ATTR(batt_tune_chg_temp_high), + SEC_BATTERY_ATTR(batt_tune_chg_temp_rec), + SEC_BATTERY_ATTR(batt_tune_chg_limit_cur), + SEC_BATTERY_ATTR(batt_tune_coil_temp_high), + SEC_BATTERY_ATTR(batt_tune_coil_temp_rec), + SEC_BATTERY_ATTR(batt_tune_coil_limit_cur), +#if defined(CONFIG_UPDATE_BATTERY_DATA) + SEC_BATTERY_ATTR(batt_update_data), +#endif + SEC_BATTERY_ATTR(batt_misc_event), + SEC_BATTERY_ATTR(batt_ext_dev_chg), + SEC_BATTERY_ATTR(batt_wdt_control), + SEC_BATTERY_ATTR(mode), + SEC_BATTERY_ATTR(check_ps_ready), + SEC_BATTERY_ATTR(batt_chip_id), + SEC_BATTERY_ATTR(cisd_fullcaprep_max), +#if defined(CONFIG_BATTERY_CISD) + SEC_BATTERY_ATTR(cisd_data), + SEC_BATTERY_ATTR(cisd_data_json), + SEC_BATTERY_ATTR(cisd_data_d_json), + SEC_BATTERY_ATTR(cisd_wire_count), + SEC_BATTERY_ATTR(cisd_wc_data), + SEC_BATTERY_ATTR(cisd_wc_data_json), + SEC_BATTERY_ATTR(prev_battery_data), + SEC_BATTERY_ATTR(prev_battery_info), +#endif + SEC_BATTERY_ATTR(safety_timer_set), + SEC_BATTERY_ATTR(batt_swelling_control), + SEC_BATTERY_ATTR(safety_timer_info), +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + SEC_BATTERY_ATTR(batt_temp_test), +#endif + SEC_BATTERY_ATTR(batt_current_event), + SEC_BATTERY_ATTR(charge_otg_control), + SEC_BATTERY_ATTR(charge_uno_control), +}; + +static enum power_supply_property sec_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_AMBIENT, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, +}; + +static enum power_supply_property sec_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property sec_wireless_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, +}; + +static enum power_supply_property sec_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_TEMP, +}; + +static enum power_supply_property sec_ps_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +char *sec_cable_type[SEC_BATTERY_CABLE_MAX] = { + "UNKNOWN", /* 0 */ + "NONE", /* 1 */ + "PREAPARE_TA", /* 2 */ + "TA", /* 3 */ + "USB", /* 4 */ + "USB_CDP", /* 5 */ + "9V_TA", /* 6 */ + "9V_ERR", /* 7 */ + "9V_UNKNOWN", /* 8 */ + "12V_TA", /* 9 */ + "WIRELESS", /* 10 */ + "HV_WIRELESS", /* 11 */ + "PMA_WIRELESS", /* 12 */ + "WIRELESS_PACK", /* 13 */ + "WIRELESS_PACK_TA", /* 14 */ + "WIRELESS_STAND", /* 15 */ + "WIRELESS_HV_STAND", /* 16 */ + "OC20", /* 17 */ + "QC30", /* 18 */ + "PDIC", /* 19 */ + "UARTOFF", /* 20 */ + "OTG", /* 21 */ + "LAN_HUB", /* 22 */ + "POWER_SHARGING", /* 23 */ + "HMT_CONNECTED", /* 24 */ + "HMT_CHARGE", /* 25 */ + "HV_TA_CHG_LIMIT", /* 26 */ + "WIRELESS_VEHICLE", /* 27 */ + "WIRELESS_HV_VEHICLE", /* 28 */ + "WIRELESS_HV_PREPARE", /* 29 */ + "TIMEOUT", /* 30 */ + "SMART_OTG", /* 31 */ + "SMART_NOTG", /* 32 */ +#if defined(CONFIG_USE_POGO) + "POGO", /* 33 */ +#endif +}; + +char *sec_bat_charging_mode_str[] = { + "Buckoff", + "None", + "Normal", + "Additional", +}; + +char *sec_bat_status_str[] = { + "Unknown", + "Charging", + "Discharging", + "Not-charging", + "Full" +}; + +char *sec_bat_health_str[] = { + "Unknown", + "Good", + "Overheat", + "Dead", + "OverVoltage", + "UnspecFailure", + "Cold", + "WatchdogTimerExpire", + "SafetyTimerExpire", + "Warm", + "Cool", + "Hot", + "UnderVoltage", + "OverheatLimit", + "VsysOVP", + "VbatOVP", +}; + +bool sleep_mode = false; +static int fg_reset = 1; +unsigned int lpcharge; +// check bulid error +unsigned int poweroff_charging; +int charging_night_mode; +extern unsigned int is_boot_recovery(void); + +EXPORT_SYMBOL(poweroff_charging); +EXPORT_SYMBOL(lpcharge); +#if defined(CONFIG_SEC_FACTORY) || defined(CONFIG_DUMMY_BATTERY) +int factory_mode; +EXPORT_SYMBOL(factory_mode); +#endif + +static int sec_bat_is_lpm_check(char *str) +{ + if (strncmp(str, "charger", 7) == 0) + lpcharge = 1; + else + lpcharge = 0; + pr_info("%s: Low power charging mode: %d\n", __func__, lpcharge); + + poweroff_charging = lpcharge; + return lpcharge; +} +__setup("androidboot.mode=", sec_bat_is_lpm_check); + +static int __init charging_mode(char *str) +{ + int mode; + + /* + * Only update loglevel value when a correct setting was passed, + * to prevent blind crashes (when loglevel being set to 0) that + * are quite hard to debug + */ + if (get_option(&str, &mode)) { + charging_night_mode = mode & 0x000000FF; + + printk(KERN_ERR "charging_mode() : 0x%x(%d)\n", charging_night_mode, charging_night_mode); + + return 0; + } + + printk(KERN_ERR "charging_mode() : %d\n", -EINVAL); + + return -EINVAL; +} +early_param("charging_mode", charging_mode); + +static int sec_bat_get_fg_reset(char *val) +{ + fg_reset = strncmp(val, "1", 1) ? 0 : 1; + pr_info("%s, fg_reset:%d\n", __func__, fg_reset); + return 1; +} +__setup("fg_reset=", sec_bat_get_fg_reset); + +#if defined(CONFIG_SEC_FACTORY) || defined(CONFIG_DUMMY_BATTERY) +static int sec_bat_get_factory_mode(char *val) +{ + factory_mode = strncmp(val, "2", 1) ? (strncmp(val, "1", 1) ? 0 : 1):2; + + pr_info("%s, factory_mode:%d\n", __func__, factory_mode); + return 1; +} +__setup("factory_mode=", sec_bat_get_factory_mode); +#endif + +static void sec_bat_set_misc_event(struct sec_battery_info *battery, + const int misc_event_type, bool do_clear) { + + mutex_lock(&battery->misclock); + pr_info("%s: %s misc event(now=0x%x, value=0x%x)\n", + __func__, ((do_clear) ? "clear" : "set"), battery->misc_event, misc_event_type); + if (do_clear) { + battery->misc_event &= ~misc_event_type; + } else { + battery->misc_event |= misc_event_type; + } + mutex_unlock(&battery->misclock); + + if (battery->prev_misc_event != battery->misc_event) { + cancel_delayed_work(&battery->misc_event_work); + wake_lock(&battery->misc_event_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->misc_event_work, 0); + } +} + +static void sec_bat_set_current_event(struct sec_battery_info *battery, + unsigned int current_event_val, unsigned int current_event_mask) +{ + unsigned int temp = battery->current_event; + + mutex_lock(&battery->current_eventlock); + + battery->current_event &= ~current_event_mask; + battery->current_event |= current_event_val; + + pr_info("%s: current event before(0x%x), after(0x%x)\n", + __func__, temp, battery->current_event); + + mutex_unlock(&battery->current_eventlock); +} + +static void sec_bat_change_default_current(struct sec_battery_info *battery, + int cable_type, int input, int output) +{ + battery->pdata->charging_current[cable_type].input_current_limit = input; + battery->pdata->charging_current[cable_type].fast_charging_current = output; + pr_info("%s: cable_type: %d input: %d output: %d\n",__func__, cable_type, input, output); +} + +static int sec_bat_get_wireless_current(struct sec_battery_info *battery, int incurr) +{ + /* 1. SIOP EVENT */ + if (battery->siop_event & SIOP_EVENT_WPC_CALL && + (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA)) { + if (battery->capacity >= battery->pdata->wireless_cc_cv) { + if (incurr > battery->pdata->siop_call_cv_current) + incurr = battery->pdata->siop_call_cv_current; + } else { + if (incurr > battery->pdata->siop_call_cc_current) + incurr = battery->pdata->siop_call_cc_current; + } + } + + /* 2. WPC_SLEEP_MODE */ + if (is_hv_wireless_type(battery->cable_type) && sleep_mode) { + if(incurr > battery->pdata->sleep_mode_limit_current) + incurr = battery->pdata->sleep_mode_limit_current; + pr_info("%s sleep_mode =%d, chg_limit =%d, in_curr = %d \n", __func__, + sleep_mode, battery->chg_limit, incurr); + } + + /* 3. WPC_TEMP_MODE */ + if (is_wireless_type(battery->cable_type) && battery->chg_limit) { + if ((battery->siop_level >= 100) && + (incurr > battery->pdata->wpc_charging_limit_current)) + incurr = battery->pdata->wpc_charging_limit_current; + else if ((battery->siop_level < 100) && + (incurr > battery->pdata->wpc_lcd_on_charging_limit_current)) + incurr = battery->pdata->wpc_lcd_on_charging_limit_current; + } + + /* 4. WPC_CV_MODE */ + if (battery->pdata->set_cv_vout_in_low_capacity && + is_nv_wireless_type(battery->cable_type)) { + union power_supply_propval value = {0,}; + + if (battery->capacity <= battery->pdata->set_cv_vout_in_low_capacity && + !battery->wc_cv_mode) { + battery->wc_cv_mode = true; + value.intval = WIRELESS_VOUT_CC_CV_VOUT; // 5.5V + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } else if (battery->capacity > battery->pdata->set_cv_vout_in_low_capacity && + battery->capacity < battery->pdata->wireless_cc_cv && + battery->wc_cv_mode) { + battery->wc_cv_mode = false; + value.intval = WIRELESS_VOUT_5V; // 5V + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } + } + + if (battery->wc_cv_mode) { + if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA) { + if (incurr > battery->pdata->wc_cv_current) + incurr = battery->pdata->wc_cv_current; + } else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK) { + if (incurr > battery->pdata->wc_cv_pack_current) + incurr = battery->pdata->wc_cv_pack_current; + } + } + + /* 5. Full-Additional state */ + if (battery->status == POWER_SUPPLY_STATUS_FULL && battery->charging_mode == SEC_BATTERY_CHARGING_2ND) { + if (incurr > battery->pdata->siop_hv_wireless_input_limit_current) + incurr = battery->pdata->siop_hv_wireless_input_limit_current; + } + + /* 6. Hero Stand Pad CV */ + if (battery->capacity >= battery->pdata->wc_hero_stand_cc_cv) { + if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND) { + if (incurr > battery->pdata->wc_hero_stand_cv_current) + incurr = battery->pdata->wc_hero_stand_cv_current; + } else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_STAND) { + if (battery->chg_limit && + incurr > battery->pdata->wc_hero_stand_cv_current) { + incurr = battery->pdata->wc_hero_stand_cv_current; + } else if (!battery->chg_limit && + incurr > battery->pdata->wc_hero_stand_hv_cv_current) { + incurr = battery->pdata->wc_hero_stand_hv_cv_current; + } + } + } + + /* 7. Full-None state && SIOP_LEVEL 100 */ + if (battery->siop_level == 100 && + battery->status == POWER_SUPPLY_STATUS_FULL && battery->charging_mode == SEC_BATTERY_CHARGING_NONE) { + incurr = battery->pdata->wc_full_input_limit_current; + } + + return incurr; +} + +static void sec_bat_get_charging_current_by_siop(struct sec_battery_info *battery, + int *input_current, int *charging_current) { + if (battery->siop_level == 3) { + /* side sync scenario : siop_level 3 */ + if (is_nv_wireless_type(battery->cable_type)) { + if (*input_current > battery->pdata->siop_wireless_input_limit_current) + *input_current = battery->pdata->siop_wireless_input_limit_current; + *charging_current = battery->pdata->siop_wireless_charging_limit_current; + } else if (is_hv_wireless_type(battery->cable_type)) { + if (*input_current > battery->pdata->siop_hv_wireless_input_limit_current) + *input_current = battery->pdata->siop_hv_wireless_input_limit_current; + *charging_current = battery->pdata->siop_hv_wireless_charging_limit_current; + } else if (is_hv_wire_type(battery->cable_type)) { + if (*input_current > 450) + *input_current = 450; + *charging_current = battery->pdata->siop_hv_charging_limit_current; +#if defined(CONFIG_CCIC_NOTIFIER) + } else if (battery->cable_type == SEC_BATTERY_CABLE_PDIC) { + if (*input_current > (4000 / battery->input_voltage)) + *input_current = 4000 / battery->input_voltage; + *charging_current = battery->pdata->siop_hv_charging_limit_current; +#endif + } else { + if (*input_current > 800) + *input_current = 800; + *charging_current = battery->pdata->charging_current[ + battery->cable_type].fast_charging_current; + if (*charging_current > battery->pdata->siop_charging_limit_current) + *charging_current = battery->pdata->siop_charging_limit_current; + } + } else if (battery->siop_level == 5) { + /* special senario : calling or browsing during wired charging */ + if (is_hv_wire_type(battery->cable_type) && + !(battery->current_event & SEC_BAT_CURRENT_EVENT_CHG_LIMIT)) { + if (battery->cable_type == SEC_BATTERY_CABLE_12V_TA) + *input_current = 440; + else + *input_current = 600; + *charging_current = 900; + } else if (battery->cable_type == SEC_BATTERY_CABLE_TA || + (battery->current_event & SEC_BAT_CURRENT_EVENT_CHG_LIMIT)) { + *input_current = 1000; + *charging_current = 900; + } + } else if (battery->siop_level < 100) { + int max_charging_current; + + if (is_wireless_type(battery->cable_type)) { + if (battery->siop_level == 0) { /* 3 step(0) */ + max_charging_current = 0; + } else if (battery->siop_level <= 10) { /* 2 step(10) */ + max_charging_current = 500; + } else { + max_charging_current = 1000; /* 1 step(70) */ + } + } else { + if (battery->siop_level == 0) { /* 3 step(0) */ + max_charging_current = battery->pdata->siop_max_charging_current_0; + } else if (battery->siop_level <= 10) { /* 2 step(10) */ + max_charging_current = battery->pdata->siop_max_charging_current_10; + } else { + max_charging_current = battery->pdata->siop_max_charging_current_70; /* 1 step(70) */ + } + } + /* do forced set charging current */ + if (*charging_current > max_charging_current) + *charging_current = max_charging_current; + + if (is_nv_wireless_type(battery->cable_type)) { + if (*input_current > battery->pdata->siop_wireless_input_limit_current) + *input_current = battery->pdata->siop_wireless_input_limit_current; + if (*charging_current > battery->pdata->siop_wireless_charging_limit_current) + *charging_current = battery->pdata->siop_wireless_charging_limit_current; + } else if (is_hv_wireless_type(battery->cable_type)) { + if (*input_current > battery->pdata->siop_hv_wireless_input_limit_current) + *input_current = battery->pdata->siop_hv_wireless_input_limit_current; + if (*charging_current > battery->pdata->siop_hv_wireless_charging_limit_current) + *charging_current = battery->pdata->siop_hv_wireless_charging_limit_current; + } else if (is_hv_wire_type(battery->cable_type) && is_hv_wire_type(battery->wire_status)) { + if (is_hv_wire_12v_type(battery->cable_type)) { + if (*input_current > battery->pdata->siop_hv_12v_input_limit_current) + *input_current = battery->pdata->siop_hv_12v_input_limit_current; + } else { + if (*input_current > battery->pdata->siop_hv_input_limit_current) + *input_current = battery->pdata->siop_hv_input_limit_current; + } +#if defined(CONFIG_CCIC_NOTIFIER) + } else if (battery->cable_type == SEC_BATTERY_CABLE_PDIC) { + if (*input_current > (battery->pdata->siop_max_input_power / battery->input_voltage)) + *input_current = battery->pdata->siop_max_input_power / battery->input_voltage; +#endif + } else { + if (*input_current > battery->pdata->siop_input_limit_current) + *input_current = battery->pdata->siop_input_limit_current; + } + } + + pr_info("%s: incurr(%d), chgcurr(%d)\n", __func__, *input_current, *charging_current); +} + +#if defined(CONFIG_MUIC_HV) || defined(CONFIG_SUPPORT_QC30) +extern int muic_afc_set_voltage(int vol); +#endif +#if !defined(CONFIG_SEC_FACTORY) +static int sec_bat_get_temp_by_temp_control_source(struct sec_battery_info *battery, + enum sec_battery_temp_control_source tcs) +{ + switch (tcs) { + case TEMP_CONTROL_SOURCE_CHG_THM: + return battery->chg_temp; + case TEMP_CONTROL_SOURCE_USB_THM: + return battery->usb_temp; + case TEMP_CONTROL_SOURCE_WPC_THM: + return battery->wpc_temp; + case TEMP_CONTROL_SOURCE_BLKT_THM: + return battery->blkt_temp; + case TEMP_CONTROL_SOURCE_NONE: + case TEMP_CONTROL_SOURCE_BAT_THM: + default: + return battery->temperature; + } +} + +static int sec_bat_check_mix_temp(struct sec_battery_info *battery, int input_current) +{ + int batt_temp = battery->pdata->blkt_temp_check ? battery->blkt_temp : battery->temperature; + + if (battery->pdata->chg_temp_check && battery->siop_level >= 100 && is_not_wireless_type(battery->cable_type)) { + if ((!battery->mix_limit && + (batt_temp >= battery->pdata->mix_high_temp) && + (battery->chg_temp >= battery->pdata->mix_high_chg_temp)) || + (battery->mix_limit && + (batt_temp> battery->pdata->mix_high_temp_recovery))) { + int max_input_current = + battery->pdata->full_check_current_1st + 50; + + /* inpu current = float voltage * (topoff_current_1st + 50mA(margin)) / (vbus_level * 0.9) */ + input_current = ((battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv) * max_input_current) / + (battery->input_voltage * 90) / 10; + if (input_current > max_input_current) + input_current = max_input_current; + + battery->mix_limit = true; + /* skip other heating control */ + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL, + SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL); + } else if (battery->mix_limit) { + battery->mix_limit = false; + } + + pr_info("%s: mix_limit(%d), temp(%d), chg_temp(%d), input_current(%d)\n", + __func__, battery->mix_limit, batt_temp, battery->chg_temp, input_current); + } else { + battery->mix_limit = false; + } + return input_current; +} + +static int sec_bat_check_wpc_temp(struct sec_battery_info *battery, int input_current) +{ + if (is_wireless_type(battery->cable_type)) { + int wpc_vout_level = WIRELESS_VOUT_10V; + + if (battery->siop_level >= 100) { + int temp_val = sec_bat_get_temp_by_temp_control_source(battery, + battery->pdata->wpc_temp_control_source); + + if ((!battery->chg_limit && temp_val >= battery->pdata->wpc_high_temp) || + (battery->chg_limit && temp_val > battery->pdata->wpc_high_temp_recovery)) { + battery->chg_limit = true; + input_current = battery->pdata->wpc_charging_limit_current; + wpc_vout_level = WIRELESS_VOUT_5V_STEP; + } else if (battery->chg_limit) { + battery->chg_limit = false; + } + } else { + if ((is_hv_wireless_type(battery->cable_type) && + battery->cable_type != SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE) || + battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) { + int temp_val = sec_bat_get_temp_by_temp_control_source(battery, + battery->pdata->wpc_temp_lcd_on_control_source); + + if ((!battery->chg_limit && + temp_val >= battery->pdata->wpc_lcd_on_high_temp) || + (battery->chg_limit && + temp_val > battery->pdata->wpc_lcd_on_high_temp_rec)) { + input_current = battery->pdata->wpc_lcd_on_charging_limit_current; + battery->chg_limit = true; + wpc_vout_level = (battery->capacity < 95) ? + WIRELESS_VOUT_5V_STEP : WIRELESS_VOUT_10V; + } else if (battery->chg_limit) { + battery->chg_limit = false; + } + } else if (battery->chg_limit) { + battery->chg_limit = false; + } + } + + if (is_hv_wireless_type(battery->cable_type)) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_SWELLING_MODE) + wpc_vout_level = WIRELESS_VOUT_5V_STEP; + + if (wpc_vout_level != battery->wpc_vout_level) { + battery->wpc_vout_level = wpc_vout_level; + if (battery->current_event & SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK) { + pr_info("%s: block to set wpc vout level(%d) because otg on\n", + __func__, wpc_vout_level); + } else { + union power_supply_propval value = {0, }; + + value.intval = wpc_vout_level; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + pr_info("%s: change vout level(%d)", + __func__, battery->wpc_vout_level); + battery->aicl_current = 0; /* reset aicl current */ + } + } + } + pr_info("%s: change input_current(%d), vout_level(%d), chg_limit(%d)\n", + __func__, input_current, battery->wpc_vout_level, battery->chg_limit); + } + + return input_current; +} + +static void sec_bat_check_afc_temp(struct sec_battery_info *battery, int *input_current, int *charging_current) +{ +#if defined(CONFIG_MUIC_HV) || defined(CONFIG_SUPPORT_QC30) + if (battery->siop_level >= 100) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_CHG_LIMIT) { + battery->chg_limit = battery->vbus_chg_by_siop = false; + *input_current = battery->pdata->pre_afc_input_current; + { + /* change input current */ + union power_supply_propval value; + battery->charge_power = battery->input_voltage * (*input_current); + value.intval = *input_current; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + battery->input_current = *input_current; + } + /* set current event */ + cancel_delayed_work(&battery->afc_work); + wake_unlock(&battery->afc_wake_lock); + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_AFC, + (SEC_BAT_CURRENT_EVENT_CHG_LIMIT | SEC_BAT_CURRENT_EVENT_AFC)); + + if(!battery->vbus_chg_by_full) { + /* vbus level : 5V --> 9V */ + if (battery->chg_limit_recovery_cable == SEC_BATTERY_CABLE_12V_TA) { + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_12V); + } else if (battery->chg_limit_recovery_cable == SEC_BATTERY_CABLE_9V_TA) { + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_9V); + } else + pr_info("%s: cable_type(%d), chg_limit_recovery_cable(%d) vbus_by_siop(%d)\n", __func__, + battery->cable_type, battery->chg_limit_recovery_cable, battery->vbus_chg_by_siop); + } + } else if (!battery->chg_limit && is_hv_wire_type(battery->cable_type) && (battery->chg_temp >= battery->pdata->chg_high_temp)) { + *input_current = battery->pdata->chg_input_limit_current; + *charging_current = battery->pdata->chg_charging_limit_current; + battery->chg_limit = true; + } else if (!battery->chg_limit && battery->max_charge_power >= (battery->pdata->pd_charging_charge_power - 500) && (battery->chg_temp >= battery->pdata->chg_high_temp)) { + *input_current = battery->pdata->default_input_current; + *charging_current = battery->pdata->default_charging_current; + battery->chg_limit = true; + } else if (battery->chg_limit && is_hv_wire_type(battery->cable_type)) { + if (battery->chg_temp <= battery->pdata->chg_high_temp_recovery) { + *input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit; + *charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current; + battery->chg_limit = false; + } else { + *input_current = battery->pdata->chg_input_limit_current; + *charging_current = battery->pdata->chg_charging_limit_current; + battery->chg_limit = true; + } + } else if (battery->chg_limit && battery->max_charge_power >= (battery->pdata->pd_charging_charge_power - 500)) { + if (battery->chg_temp <= battery->pdata->chg_high_temp_recovery) { + *input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit; + *charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current; + battery->chg_limit = false; + } else { + *input_current = battery->pdata->chg_input_limit_current; + *charging_current = battery->pdata->chg_charging_limit_current; + battery->chg_limit = true; + } + } + pr_info("%s: cable_type(%d), chg_limit(%d) vbus_by_siop(%d)\n", __func__, + battery->cable_type, battery->chg_limit, battery->vbus_chg_by_siop); + } else if (is_hv_wire_type(battery->cable_type) && is_hv_wire_type(battery->wire_status) && + !battery->store_mode && (battery->cable_type != SEC_BATTERY_CABLE_QC30) && + (battery->status == POWER_SUPPLY_STATUS_CHARGING) && !battery->vbus_chg_by_siop) { + battery->chg_limit_recovery_cable = battery->cable_type; + battery->vbus_chg_by_siop = true; + battery->chg_limit = false; + /* vbus level : 9V --> 5V */ + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V); + pr_info("%s: vbus set 5V by siop(recovery cable: %d)\n", __func__,battery->chg_limit_recovery_cable); + } +#else + if (!battery->chg_limit && is_hv_wire_type(battery->cable_type) && (battery->chg_temp >= battery->pdata->chg_high_temp)) { + *input_current = battery->pdata->chg_input_limit_current; + *charging_current = battery->pdata->chg_charging_limit_current; + battery->chg_limit = true; + } else if (battery->chg_limit && is_hv_wire_type(battery->cable_type) && (battery->chg_temp <= battery->pdata->chg_high_temp_recovery)) { + *input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit; + *charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current; + battery->chg_limit = false; + } +#endif +} + +#if defined(CONFIG_CCIC_NOTIFIER) +extern void select_pdo(int num); + +static bool sec_bat_change_vbus_pd(struct sec_battery_info *battery, int *input_current) +{ +#if defined(CONFIG_SUPPORT_HV_CTRL) + unsigned int target_pd_index = 0; + + if (battery->store_mode) + return false; + + if (battery->cable_type == SEC_BATTERY_CABLE_PDIC) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_SELECT_PDO) { + pr_info("%s: skip during current_event(0x%x)\n", + __func__, battery->current_event); + return false; + } + + if (battery->siop_level >= 100) { + /* select PDO greater than 5V */ + target_pd_index = battery->pd_list.max_pd_count - 1; + } else { + /* select 5V PDO */ + target_pd_index = 0; + } + pr_info("%s: target_pd_index: %d, now_pd_index: %d\n", __func__, + target_pd_index, battery->pd_list.now_pd_index); + + if (target_pd_index != battery->pd_list.now_pd_index) { + /* change input current before request new pdo if new pdo's input current is less than now */ +#if defined(CONFIG_PDIC_PD30) + if (battery->pd_list.pd_info[target_pd_index].max_current < battery->input_current) { +#else + if (battery->pd_list.pd_info[target_pd_index].input_current < battery->input_current) { +#endif + union power_supply_propval value = {0, }; +#if defined(CONFIG_PDIC_PD30) + *input_current = battery->pd_list.pd_info[target_pd_index].max_current; +#else + *input_current = battery->pd_list.pd_info[target_pd_index].input_current; +#endif + + value.intval = *input_current; + battery->input_current = *input_current; + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO, + SEC_BAT_CURRENT_EVENT_SELECT_PDO); + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + } + battery->pdic_ps_rdy = false; + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO, + SEC_BAT_CURRENT_EVENT_SELECT_PDO); + select_pdo(battery->pd_list.pd_info[target_pd_index].pdo_index); + return true; + } + } +#endif + return false; +} + +static void sec_bat_check_pdic_temp(struct sec_battery_info *battery, int *input_current, int *charging_current) +{ + if (battery->pdic_ps_rdy && battery->siop_level >= 100 && !battery->lcd_status) { + if ((!battery->chg_limit && (battery->chg_temp >= battery->pdata->chg_high_temp)) || + (battery->chg_limit && (battery->chg_temp >= battery->pdata->chg_high_temp_recovery))) { + *input_current = + (battery->pdata->chg_input_limit_current * SEC_INPUT_VOLTAGE_9V) / battery->input_voltage; + *charging_current = battery->pdata->chg_charging_limit_current; + battery->chg_limit = true; + } else if (battery->chg_limit && battery->chg_temp <= battery->pdata->chg_high_temp_recovery) { + *input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit; + *charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current; + battery->chg_limit = false; + } + pr_info("%s: cable_type(%d), chg_limit(%d)\n", __func__, + battery->cable_type, battery->chg_limit); + } +} + +static int sec_bat_check_pd_input_current(struct sec_battery_info *battery, int input_current) +{ + if (battery->current_event & SEC_BAT_CURRENT_EVENT_SELECT_PDO) { + input_current = battery->input_current; + pr_info("%s: change input_current(%d), cable_type(%d)\n", __func__, input_current, battery->cable_type); + } + + return input_current; +} +#endif +#endif + +static int sec_bat_check_afc_input_current(struct sec_battery_info *battery, int input_current) +{ + if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) { + int work_delay = 0; + + if (!is_wireless_type(battery->cable_type)) { + input_current = battery->pdata->pre_afc_input_current; // 1000mA + work_delay = battery->pdata->pre_afc_work_delay; + } else { + input_current = battery->pdata->pre_wc_afc_input_current; + /* do not reduce this time, this is for noble pad */ + work_delay = battery->pdata->pre_wc_afc_work_delay; + } + + wake_lock(&battery->afc_wake_lock); + if (!delayed_work_pending(&battery->afc_work)) + queue_delayed_work(battery->monitor_wqueue, + &battery->afc_work , msecs_to_jiffies(work_delay)); + + pr_info("%s: change input_current(%d), cable_type(%d)\n", __func__, input_current, battery->cable_type); + } + + return input_current; +} + +#if defined(CONFIG_CCIC_NOTIFIER) +static void sec_bat_get_input_current_in_power_list(struct sec_battery_info *battery) +{ + int pdo_num = battery->pdic_info.sink_status.current_pdo_num; + int max_input_current = 0; + + max_input_current = battery->pdata->charging_current[SEC_BATTERY_CABLE_PDIC].input_current_limit = + battery->pdic_info.sink_status.power_list[pdo_num].max_current; + + pr_info("%s:max_input_current : %dmA\n", __func__, max_input_current); +} + +static void sec_bat_get_charging_current_in_power_list(struct sec_battery_info *battery) +{ + int max_charging_current = 0; + int pdo_num = battery->pdic_info.sink_status.current_pdo_num; + int pd_power = (battery->pdic_info.sink_status.power_list[pdo_num].max_voltage * + battery->pdic_info.sink_status.power_list[pdo_num].max_current); + + /* We assume that output voltage to float voltage */ + max_charging_current = pd_power / (battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv); + max_charging_current = max_charging_current > battery->pdata->max_charging_current ? + battery->pdata->max_charging_current : max_charging_current; + battery->pdata->charging_current[SEC_BATTERY_CABLE_PDIC].fast_charging_current = max_charging_current; + battery->charge_power = pd_power; + + pr_info("%s:pd_charge_power : %dmW, max_charging_current : %dmA\n", __func__, + battery->charge_power, max_charging_current); +} +#endif + +static int sec_bat_set_charging_current(struct sec_battery_info *battery, int skip_afc_temp) +{ + static int afc_init = false; + union power_supply_propval value = {0, }; + int input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit, + charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current, + topoff_current = battery->pdata->full_check_current_1st; +#if !defined(CONFIG_SEC_FACTORY) + int temp = 0; +#endif + if (is_hv_wire_type(battery->cable_type)) { + input_current = battery->pdata->charging_current[battery->wire_status].input_current_limit; + charging_current = battery->pdata->charging_current[battery->wire_status].fast_charging_current; + } + if (battery->aicl_current) + input_current = battery->aicl_current; + mutex_lock(&battery->iolock); + if (is_nocharge_type(battery->cable_type)) { + } else { +#if !defined(CONFIG_SEC_FACTORY) + if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL)) { + input_current = sec_bat_check_mix_temp(battery, input_current); + } +#endif + + /* check input current */ +#if !defined(CONFIG_SEC_FACTORY) + if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL)) { + if (battery->pdata->wpc_temp_check) { + temp = sec_bat_check_wpc_temp(battery, input_current); + if (input_current > temp) + input_current = temp; + } + if (battery->pdata->chg_temp_check && !skip_afc_temp) + sec_bat_check_afc_temp(battery, &input_current, &charging_current); +#if defined(CONFIG_CCIC_NOTIFIER) + else if (battery->cable_type == SEC_BATTERY_CABLE_PDIC && battery->pdata->chg_temp_check) { + if (!sec_bat_change_vbus_pd(battery, &input_current)) { + sec_bat_check_pdic_temp(battery, &input_current, &charging_current); + input_current = sec_bat_check_pd_input_current(battery, input_current); + } + } +#endif + } +#endif + + input_current = sec_bat_check_afc_input_current(battery, input_current); + /* Set limited max current with hv wire cable when store mode is set and LDU + Limited max current should be set with over 5% capacity since target could be turned off during boot up */ + if (battery->store_mode && is_hv_wire_type(battery->wire_status) && (battery->capacity >= 5)) { + input_current = battery->pdata->store_mode_afc_input_current; + } + + sec_bat_get_charging_current_by_siop(battery, &input_current, &charging_current); + + /* Calculate wireless input current under the specific conditions (siop_event, wpc_sleep_mode, chg_limit)*/ + if (battery->wc_status != SEC_WIRELESS_PAD_NONE) { + input_current = sec_bat_get_wireless_current(battery, input_current); + } + + /* check topoff current */ + if ((battery->charging_mode == SEC_BATTERY_CHARGING_2ND) && + (battery->pdata->full_check_type_2nd == SEC_BATTERY_FULLCHARGED_CHGPSY)) { + topoff_current = battery->pdata->full_check_current_2nd; + } + + /* check swelling state */ + if (is_wireless_type(battery->cable_type)) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) { + charging_current = (charging_current > battery->pdata->swelling_wc_low_temp_current) ? + battery->pdata->swelling_wc_low_temp_current : charging_current; + topoff_current = (topoff_current > battery->pdata->swelling_low_temp_topoff) ? + battery->pdata->swelling_low_temp_topoff : topoff_current; + } else if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) { + charging_current = (charging_current > battery->pdata->swelling_wc_high_temp_current) ? + battery->pdata->swelling_wc_high_temp_current : charging_current; + topoff_current = (topoff_current > battery->pdata->swelling_high_temp_topoff) ? + battery->pdata->swelling_high_temp_topoff : topoff_current; + } else if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP) { + charging_current = (charging_current > battery->pdata->swelling_wc_low_temp_current) ? + battery->pdata->swelling_wc_low_temp_current : charging_current; + } + } else { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) { + charging_current = (charging_current > battery->pdata->swelling_low_temp_current) ? + battery->pdata->swelling_low_temp_current : charging_current; + topoff_current = (topoff_current > battery->pdata->swelling_low_temp_topoff) ? + battery->pdata->swelling_low_temp_topoff : topoff_current; + } else if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) { + charging_current = (charging_current > battery->pdata->swelling_high_temp_current) ? + battery->pdata->swelling_high_temp_current : charging_current; + topoff_current = (topoff_current > battery->pdata->swelling_high_temp_topoff) ? + battery->pdata->swelling_high_temp_topoff : topoff_current; + } else if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP) { + charging_current = (charging_current > battery->pdata->swelling_low_temp_current) ? + battery->pdata->swelling_low_temp_current : charging_current; + } +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) + /* usb unconfigured or suspend*/ + if ((battery->cable_type == SEC_BATTERY_CABLE_USB) && !lpcharge) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_USB_100MA) { + pr_info("%s: usb unconfigured\n", __func__); + input_current = USB_CURRENT_UNCONFIGURED; + charging_current = USB_CURRENT_UNCONFIGURED; + } + } +#endif + } + } + + /* In wireless charging, must be set charging current before input current. */ + if (is_wireless_type(battery->cable_type) && + battery->charging_current != charging_current) { + value.intval = charging_current; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_AVG, value); + battery->charging_current = charging_current; + } + /* set input current, charging current */ + if ((battery->input_current != input_current) || + (battery->charging_current != charging_current)) { + /* update charge power */ + battery->charge_power = battery->input_voltage * input_current; + if (battery->charge_power > battery->max_charge_power) + battery->max_charge_power = battery->charge_power; + + value.intval = input_current; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + battery->input_current = input_current; + + value.intval = charging_current; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_NOW, value); + + if (charging_current <= 100) + battery->charging_current = 100; + else + battery->charging_current = charging_current; + pr_info("%s: power(%d), input(%d), charge(%d)\n", __func__, + battery->charge_power, battery->input_current, battery->charging_current); + } + + /* set topoff current */ + if (battery->topoff_current != topoff_current) { + value.intval = topoff_current; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CURRENT_FULL, value); + battery->topoff_current = topoff_current; + } + if (!afc_init) { + afc_init = true; +#if defined(CONFIG_AFC_CHARGER_MODE) + value.intval = 1; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_AFC_CHARGER_MODE, + value); +#endif + } + mutex_unlock(&battery->iolock); + return 0; +} + +static int sec_bat_set_charge( + struct sec_battery_info *battery, + enum sec_battery_charging_mode chg_mode) +{ + union power_supply_propval val = {0, }; + ktime_t current_time = {0, }; + struct timespec ts = {0, }; + + if (chg_mode < SEC_BATTERY_BUCKOFF) + dev_info(battery->dev, "invalid chg_mode: %d", chg_mode); + if ((battery->status == POWER_SUPPLY_STATUS_CHARGING || battery->status == POWER_SUPPLY_STATUS_FULL) && + chg_mode == SEC_BATTERY_BUCKOFF) { + dev_info(battery->dev, "invalid status: %d and chg_mode: %d", battery->status, chg_mode); + } + if ((battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING || + battery->status == POWER_SUPPLY_STATUS_DISCHARGING) && + chg_mode > SEC_BATTERY_CHARGING_NONE) { + dev_info(battery->dev, "invalid status: %d and chg_mode: %d", battery->status, chg_mode); + } + + if (battery->cable_type == SEC_BATTERY_CABLE_HMT_CONNECTED) + return 0; + + if ((battery->current_event & SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE) && + (chg_mode > SEC_BATTERY_CHARGING_NONE)) { + dev_info(battery->dev, "%s: charge disable by HMT\n", __func__); + chg_mode = SEC_BATTERY_CHARGING_NONE; + } + + val.intval = battery->status; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_STATUS, val); + current_time = ktime_get_boottime(); + ts = ktime_to_timespec(current_time); + + if (chg_mode > SEC_BATTERY_CHARGING_NONE) { + /*Reset charging start time only in initial charging start */ + if (battery->charging_start_time == 0) { + if (ts.tv_sec < 1) + ts.tv_sec = 1; + battery->charging_start_time = ts.tv_sec; + battery->charging_next_time = + battery->pdata->charging_reset_time; + } + } else { + battery->charging_start_time = 0; + battery->charging_passed_time = 0; + battery->charging_next_time = 0; + battery->charging_fullcharged_time = 0; + battery->full_check_cnt = 0; +#if defined(CONFIG_BATTERY_CISD) + battery->usb_overheat_check = false; + battery->cisd.ab_vbat_check_count = 0; + if (chg_mode == SEC_BATTERY_BUCKOFF) { + battery->cisd.data[CISD_DATA_BUCK_OFF]++; + battery->cisd.data[CISD_DATA_BUCK_OFF_PER_DAY]++; + } +#endif + } + battery->charging_mode = chg_mode; + switch (chg_mode){ + case SEC_BATTERY_BUCKOFF: + val.intval = SEC_BAT_CHG_MODE_BUCK_OFF; + break; + case SEC_BATTERY_CHARGING_NONE: + val.intval = SEC_BAT_CHG_MODE_CHARGING_OFF; + break; + case SEC_BATTERY_CHARGING_1ST: + case SEC_BATTERY_CHARGING_2ND: + val.intval = SEC_BAT_CHG_MODE_CHARGING; + break; + default: + panic("invalid charging mode"); + break; + } + + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CHARGING_ENABLED, val); + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_CHARGING_ENABLED, val); + + return 0; +} + +static bool sec_bat_check_by_psy(struct sec_battery_info *battery) +{ + char *psy_name = NULL; + union power_supply_propval value = {0, }; + bool ret = true; + + switch (battery->pdata->battery_check_type) { + case SEC_BATTERY_CHECK_PMIC: + psy_name = battery->pdata->pmic_name; + break; + case SEC_BATTERY_CHECK_FUELGAUGE: + psy_name = battery->pdata->fuelgauge_name; + break; + case SEC_BATTERY_CHECK_CHARGER: + psy_name = battery->pdata->charger_name; + break; + default: + dev_err(battery->dev, + "%s: Invalid Battery Check Type\n", __func__); + ret = false; + goto battery_check_error; + break; + } + + psy_do_property(psy_name, get, + POWER_SUPPLY_PROP_PRESENT, value); + ret = (bool)value.intval; + +battery_check_error: + return ret; +} + +static bool sec_bat_check(struct sec_battery_info *battery) +{ + bool ret = true; + + if (battery->factory_mode || battery->is_jig_on) { + dev_dbg(battery->dev, "%s: No need to check in factory mode\n", + __func__); + return ret; + } + + if (battery->health != POWER_SUPPLY_HEALTH_GOOD && + battery->health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { + dev_dbg(battery->dev, "%s: No need to check\n", __func__); + return ret; + } + + switch (battery->pdata->battery_check_type) { + case SEC_BATTERY_CHECK_ADC: + if(is_nocharge_type(battery->cable_type)) + ret = battery->present; + else + ret = sec_bat_check_vf_adc(battery); + break; + case SEC_BATTERY_CHECK_INT: + case SEC_BATTERY_CHECK_CALLBACK: + if(is_nocharge_type(battery->cable_type)) { + ret = battery->present; + } else { + if (battery->pdata->check_battery_callback) + ret = battery->pdata->check_battery_callback(); + } + break; + case SEC_BATTERY_CHECK_PMIC: + case SEC_BATTERY_CHECK_FUELGAUGE: + case SEC_BATTERY_CHECK_CHARGER: + ret = sec_bat_check_by_psy(battery); + break; + case SEC_BATTERY_CHECK_NONE: + dev_dbg(battery->dev, "%s: No Check\n", __func__); + default: + break; + } + + return ret; +} + +static bool sec_bat_get_cable_type( + struct sec_battery_info *battery, + int cable_source_type) +{ + bool ret = false; + int cable_type = battery->cable_type; + + if (cable_source_type & SEC_BATTERY_CABLE_SOURCE_CALLBACK) { + if (battery->pdata->check_cable_callback) + cable_type = + battery->pdata->check_cable_callback(); + } + + if (cable_source_type & SEC_BATTERY_CABLE_SOURCE_ADC) { + if (gpio_get_value_cansleep( + battery->pdata->bat_gpio_ta_nconnected) ^ + battery->pdata->bat_polarity_ta_nconnected) + cable_type = SEC_BATTERY_CABLE_NONE; + else + cable_type = + sec_bat_get_charger_type_adc(battery); + } + + if (battery->cable_type == cable_type) { + dev_dbg(battery->dev, + "%s: No need to change cable status\n", __func__); + } else { + if (cable_type < SEC_BATTERY_CABLE_NONE || + cable_type >= SEC_BATTERY_CABLE_MAX) { + dev_err(battery->dev, + "%s: Invalid cable type\n", __func__); + } else { + battery->cable_type = cable_type; + if (battery->pdata->check_cable_result_callback) + battery->pdata->check_cable_result_callback( + battery->cable_type); + + ret = true; + + dev_dbg(battery->dev, "%s: Cable Changed (%d)\n", + __func__, battery->cable_type); + } + } + + return ret; +} + +static void sec_bat_set_charging_status(struct sec_battery_info *battery, + int status) { + union power_supply_propval value = {0, }; + + switch (status) { + case POWER_SUPPLY_STATUS_CHARGING: + if (battery->siop_level != 100) + battery->stop_timer = true; + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + case POWER_SUPPLY_STATUS_DISCHARGING: + if ((battery->status == POWER_SUPPLY_STATUS_FULL || + (battery->capacity == 100 && !is_slate_mode(battery))) && + !battery->store_mode) { + value.intval = 100; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_CHARGE_FULL, value); + /* To get SOC value (NOT raw SOC), need to reset value */ + value.intval = 0; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + battery->capacity = value.intval; + } + battery->expired_time = battery->pdata->expired_time; + battery->prev_safety_time = 0; + break; + case POWER_SUPPLY_STATUS_FULL: + if (is_wireless_type(battery->cable_type)) { + bool send_cs100_cmd = true; + +#ifdef CONFIG_CS100_JPNCONCEPT + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ID, value); + + /* In case of the JPN PAD, this pad block the charge after give the cs100 command. */ + send_cs100_cmd = (battery->charging_mode == SEC_BATTERY_CHARGING_2ND || value.intval); +#endif + if (send_cs100_cmd) { + value.intval = POWER_SUPPLY_STATUS_FULL; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_STATUS, value); + } + } + break; + default: + break; + } + battery->status = status; +} + +static bool sec_bat_battery_cable_check(struct sec_battery_info *battery) +{ + if (!sec_bat_check(battery)) { + if (battery->check_count < battery->pdata->check_count) + battery->check_count++; + else { + dev_err(battery->dev, + "%s: Battery Disconnected\n", __func__); + battery->present = false; + battery->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + + if (battery->status != + POWER_SUPPLY_STATUS_DISCHARGING) { + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_NOT_CHARGING); + sec_bat_set_charge(battery, SEC_BATTERY_BUCKOFF); + } + + if (battery->pdata->check_battery_result_callback) + battery->pdata-> + check_battery_result_callback(); + return false; + } + } else + battery->check_count = 0; + + battery->present = true; + + if (battery->health == POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { + battery->health = POWER_SUPPLY_HEALTH_GOOD; + + if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) { + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_CHARGING); +#if defined(CONFIG_BATTERY_SWELLING) + if (!battery->swelling_mode) +#endif + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + } + } + + dev_dbg(battery->dev, "%s: Battery Connected\n", __func__); + + if (battery->pdata->cable_check_type & + SEC_BATTERY_CABLE_CHECK_POLLING) { + if (sec_bat_get_cable_type(battery, + battery->pdata->cable_source_type)) { + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + } + } + return true; +} + +static int sec_bat_ovp_uvlo_by_psy(struct sec_battery_info *battery) +{ + char *psy_name = NULL; + union power_supply_propval value = {0, }; + + value.intval = POWER_SUPPLY_HEALTH_GOOD; + + switch (battery->pdata->ovp_uvlo_check_type) { + case SEC_BATTERY_OVP_UVLO_PMICPOLLING: + psy_name = battery->pdata->pmic_name; + break; + case SEC_BATTERY_OVP_UVLO_CHGPOLLING: + psy_name = battery->pdata->charger_name; + break; + default: + dev_err(battery->dev, + "%s: Invalid OVP/UVLO Check Type\n", __func__); + goto ovp_uvlo_check_error; + break; + } + + psy_do_property(psy_name, get, + POWER_SUPPLY_PROP_HEALTH, value); + +ovp_uvlo_check_error: + return value.intval; +} + +static bool sec_bat_ovp_uvlo_result( + struct sec_battery_info *battery, int health) +{ + if (battery->health != health) { + battery->health = health; + switch (health) { + case POWER_SUPPLY_HEALTH_GOOD: + dev_info(battery->dev, "%s: Safe voltage\n", __func__); + dev_info(battery->dev, "%s: is_recharging : %d\n", __func__, battery->is_recharging); + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_CHARGING); +#if defined(CONFIG_BATTERY_SWELLING) + if (!battery->swelling_mode) +#endif + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + battery->health_check_count = 0; + break; + case POWER_SUPPLY_HEALTH_OVERVOLTAGE: + case POWER_SUPPLY_HEALTH_UNDERVOLTAGE: + dev_info(battery->dev, + "%s: Unsafe voltage (%d)\n", + __func__, health); + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_NOT_CHARGING); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + battery->is_recharging = false; + battery->health_check_count = DEFAULT_HEALTH_CHECK_COUNT; +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_UNSAFETY_VOLTAGE]++; + battery->cisd.data[CISD_DATA_UNSAFE_VOLTAGE_PER_DAY]++; +#endif + /* Take the wakelock during 10 seconds + when over-voltage status is detected */ + wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10); + break; + } + power_supply_changed(battery->psy_bat); + return true; + } + + return false; +} + +static bool sec_bat_ovp_uvlo(struct sec_battery_info *battery) +{ + int health = POWER_SUPPLY_HEALTH_GOOD; + + if (battery->wdt_kick_disable) { + dev_dbg(battery->dev, + "%s: No need to check in wdt test\n", + __func__); + return false; + } else if ((battery->status == POWER_SUPPLY_STATUS_FULL) && + (battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) { + dev_dbg(battery->dev, "%s: No need to check in Full status", __func__); + return false; + } + + if (battery->health != POWER_SUPPLY_HEALTH_GOOD && + battery->health != POWER_SUPPLY_HEALTH_OVERVOLTAGE && + battery->health != POWER_SUPPLY_HEALTH_UNDERVOLTAGE) { + dev_dbg(battery->dev, "%s: No need to check\n", __func__); + return false; + } + + health = battery->health; + + switch (battery->pdata->ovp_uvlo_check_type) { + case SEC_BATTERY_OVP_UVLO_CALLBACK: + if (battery->pdata->ovp_uvlo_callback) + health = battery->pdata->ovp_uvlo_callback(); + break; + case SEC_BATTERY_OVP_UVLO_PMICPOLLING: + case SEC_BATTERY_OVP_UVLO_CHGPOLLING: + health = sec_bat_ovp_uvlo_by_psy(battery); + break; + case SEC_BATTERY_OVP_UVLO_PMICINT: + case SEC_BATTERY_OVP_UVLO_CHGINT: + /* nothing for interrupt check */ + default: + break; + } + + /* Move the location for calling the get_health + in case of attaching the jig */ + if (battery->factory_mode || battery->is_jig_on) { + dev_dbg(battery->dev, + "%s: No need to check in factory mode\n", + __func__); + return false; + } + + return sec_bat_ovp_uvlo_result(battery, health); +} + +static bool sec_bat_check_recharge(struct sec_battery_info *battery) +{ + if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING || + battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) { + dev_dbg(battery->dev, + "%s: No need to check recharge\n", __func__); + return false; + } +#if defined(CONFIG_BATTERY_SWELLING) + if (battery->swelling_mode == SWELLING_MODE_CHARGING || + battery->swelling_mode == SWELLING_MODE_FULL) { + int swelling_rechg_voltage = battery->pdata->swelling_high_rechg_voltage; + if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) { + swelling_rechg_voltage = battery->pdata->swelling_low_rechg_voltage; + } + if (battery->voltage_now < swelling_rechg_voltage && + battery->charging_mode == SEC_BATTERY_CHARGING_NONE) { + battery->expired_time = battery->pdata->recharging_expired_time; + battery->prev_safety_time = 0; + pr_info("%s: swelling mode recharging start. Vbatt(%d)\n", + __func__, battery->voltage_now); +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_SWELLING_CHARGING_COUNT]++; + battery->cisd.data[CISD_DATA_SWELLING_CHARGING_COUNT_PER_DAY]++; +#endif + return true; + } + pr_info("%s: Skip normal recharge check routine for swelling mode\n", + __func__); + return false; + } +#endif + if ((battery->status == POWER_SUPPLY_STATUS_CHARGING) && + (battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) && + (battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) { + dev_info(battery->dev, + "%s: Re-charging by NOTIMEFULL (%d)\n", + __func__, battery->capacity); + goto check_recharge_check_count; + } + + if (battery->status == POWER_SUPPLY_STATUS_FULL && + battery->charging_mode == SEC_BATTERY_CHARGING_NONE) { + int recharging_voltage = battery->pdata->recharge_condition_vcell; + if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP) { + /* float voltage - 150mV */ + recharging_voltage =\ + (battery->pdata->chg_float_voltage /\ + battery->pdata->chg_float_voltage_conv) - 150; + if (battery->pdata->is_b2b_model) /*for B2B Model , spec is float voltage - 200mV */ + recharging_voltage -= 50; + dev_info(battery->dev, "%s: recharging voltage changed by low temp(%d)\n", + __func__, recharging_voltage); + } + dev_info(battery->dev, "%s: recharging voltage (%d)\n", + __func__, recharging_voltage); + + if ((battery->pdata->recharge_condition_type & + SEC_BATTERY_RECHARGE_CONDITION_SOC) && + (battery->capacity <= + battery->pdata->recharge_condition_soc)) { + battery->expired_time = battery->pdata->recharging_expired_time; + battery->prev_safety_time = 0; + dev_info(battery->dev, + "%s: Re-charging by SOC (%d)\n", + __func__, battery->capacity); + goto check_recharge_check_count; + } + + if ((battery->pdata->recharge_condition_type & + SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL) && + (battery->voltage_avg <= recharging_voltage)) { + battery->expired_time = battery->pdata->recharging_expired_time; + battery->prev_safety_time = 0; + dev_info(battery->dev, + "%s: Re-charging by average VCELL (%d)\n", + __func__, battery->voltage_avg); + goto check_recharge_check_count; + } + + if ((battery->pdata->recharge_condition_type & + SEC_BATTERY_RECHARGE_CONDITION_VCELL) && + (battery->voltage_now <= recharging_voltage)) { + battery->expired_time = battery->pdata->recharging_expired_time; + battery->prev_safety_time = 0; + dev_info(battery->dev, + "%s: Re-charging by VCELL (%d)\n", + __func__, battery->voltage_now); + goto check_recharge_check_count; + } + } + + battery->recharge_check_cnt = 0; + return false; + +check_recharge_check_count: + if (battery->recharge_check_cnt < + battery->pdata->recharge_check_count) + battery->recharge_check_cnt++; + dev_dbg(battery->dev, + "%s: recharge count = %d\n", + __func__, battery->recharge_check_cnt); + + if (battery->recharge_check_cnt >= + battery->pdata->recharge_check_count) { +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.recharge_count++; + battery->cisd.recharge_count_2++; + battery->cisd.data[CISD_DATA_RECHARGING_COUNT]++; + battery->cisd.data[CISD_DATA_RECHARGING_COUNT_PER_DAY]++; +#endif + return true; + } else { + return false; + } +} + +static bool sec_bat_voltage_check(struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING) { + dev_dbg(battery->dev, + "%s: Charging Disabled\n", __func__); + return true; + } + + /* OVP/UVLO check */ + if (sec_bat_ovp_uvlo(battery)) { + if (battery->pdata->ovp_uvlo_result_callback) + battery->pdata-> + ovp_uvlo_result_callback(battery->health); + return false; + } + + if ((battery->status == POWER_SUPPLY_STATUS_FULL) && +#if defined(CONFIG_BATTERY_SWELLING) + (battery->charging_mode == SEC_BATTERY_CHARGING_2ND || + battery->is_recharging || battery->swelling_mode)) { +#else + (battery->charging_mode == SEC_BATTERY_CHARGING_2ND || + battery->is_recharging)) { +#endif + value.intval = 0; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + if (value.intval < + battery->pdata->full_condition_soc && + battery->voltage_now < + (battery->pdata->recharge_condition_vcell - 50)) { + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_CHARGING); + dev_info(battery->dev, + "%s: battery status full -> charging, RepSOC(%d)\n", __func__, value.intval); + return false; + } + } + + /* Re-Charging check */ + if (sec_bat_check_recharge(battery)) { + battery->is_recharging = true; + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + return false; + } + + return true; +} + +#if defined(CONFIG_BATTERY_AGE_FORECAST) +static bool sec_bat_set_aging_step(struct sec_battery_info *battery, int step) +{ + union power_supply_propval value = {0, }; + + if (battery->pdata->num_age_step <= 0 || step < 0 || step >= battery->pdata->num_age_step) { + pr_info("%s: [AGE] abnormal age step : %d/%d\n", + __func__, step, battery->pdata->num_age_step-1); + return false; + } + + battery->pdata->age_step = step; + + /* float voltage */ + battery->pdata->chg_float_voltage = + battery->pdata->age_data[battery->pdata->age_step].float_voltage; + battery->pdata->swelling_normal_float_voltage = + battery->pdata->chg_float_voltage; + if (!battery->swelling_mode) { + value.intval = battery->pdata->chg_float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, value); + } + + /* full/recharge condition */ + battery->pdata->recharge_condition_vcell = + battery->pdata->age_data[battery->pdata->age_step].recharge_condition_vcell; + battery->pdata->full_condition_soc = + battery->pdata->age_data[battery->pdata->age_step].full_condition_soc; + battery->pdata->full_condition_vcell = + battery->pdata->age_data[battery->pdata->age_step].full_condition_vcell; + + value.intval = battery->pdata->full_condition_soc; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, value); + + dev_info(battery->dev, + "%s: Step(%d/%d), Cycle(%d), float_v(%d), r_v(%d), f_s(%d), f_vl(%d)\n", + __func__, + battery->pdata->age_step, battery->pdata->num_age_step-1, battery->batt_cycle, + battery->pdata->chg_float_voltage, + battery->pdata->recharge_condition_vcell, + battery->pdata->full_condition_soc, + battery->pdata->full_condition_vcell); + + return true; +} + +static void sec_bat_aging_check(struct sec_battery_info *battery) +{ + int prev_step = battery->pdata->age_step; + int calc_step = -1; + bool ret = 0; + + if (battery->pdata->num_age_step <= 0 || battery->batt_cycle < 0) + return; + + if (battery->temperature < 50) { + pr_info("%s: [AGE] skip (temperature:%d)\n", __func__, battery->temperature); + return; + } + + for (calc_step = battery->pdata->num_age_step - 1; calc_step >= 0; calc_step--) { + if (battery->pdata->age_data[calc_step].cycle <= battery->batt_cycle) + break; + } + + if (calc_step == prev_step) + return; + + ret = sec_bat_set_aging_step(battery, calc_step); + dev_info(battery->dev, + "%s: %s change step (%d->%d), Cycle(%d)\n", + __func__, ret ? "Succeed in" : "Fail to", + prev_step, battery->pdata->age_step, battery->batt_cycle); +} +#endif + +static bool sec_bat_temperature( + struct sec_battery_info *battery) +{ + bool ret; + ret = true; + + if (is_wireless_type(battery->cable_type)) { + battery->temp_highlimit_threshold = + battery->pdata->temp_highlimit_threshold_normal; + battery->temp_highlimit_recovery = + battery->pdata->temp_highlimit_recovery_normal; + battery->temp_high_threshold = + battery->pdata->wpc_high_threshold_normal; + battery->temp_high_recovery = + battery->pdata->wpc_high_recovery_normal; + battery->temp_low_recovery = + battery->pdata->wpc_low_recovery_normal; + battery->temp_low_threshold = + battery->pdata->wpc_low_threshold_normal; + } else { + if (lpcharge) { + battery->temp_highlimit_threshold = + battery->pdata->temp_highlimit_threshold_lpm; + battery->temp_highlimit_recovery = + battery->pdata->temp_highlimit_recovery_lpm; + battery->temp_high_threshold = + battery->pdata->temp_high_threshold_lpm; + battery->temp_high_recovery = + battery->pdata->temp_high_recovery_lpm; + battery->temp_low_recovery = + battery->pdata->temp_low_recovery_lpm; + battery->temp_low_threshold = + battery->pdata->temp_low_threshold_lpm; + } else { + battery->temp_highlimit_threshold = + battery->pdata->temp_highlimit_threshold_normal; + battery->temp_highlimit_recovery = + battery->pdata->temp_highlimit_recovery_normal; + battery->temp_high_threshold = + battery->pdata->temp_high_threshold_normal; + battery->temp_high_recovery = + battery->pdata->temp_high_recovery_normal; + battery->temp_low_recovery = + battery->pdata->temp_low_recovery_normal; + battery->temp_low_threshold = + battery->pdata->temp_low_threshold_normal; + } + } + + return ret; +} + +static bool sec_bat_temperature_check( + struct sec_battery_info *battery) +{ + int temp_value = 0; + int pre_health = POWER_SUPPLY_HEALTH_GOOD; + int health = POWER_SUPPLY_HEALTH_GOOD; + unsigned int pre_swell_sts = battery->current_event & SEC_BAT_CURRENT_EVENT_SWELLING_MODE; + bool swell_chg = false; + static int temp_chg_cnt = 0; + + if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING) { + battery->health_change = false; + temp_chg_cnt = 0; + dev_dbg(battery->dev, + "%s: Charging Disabled\n", __func__); + return true; + } + + if (battery->health != POWER_SUPPLY_HEALTH_GOOD && + battery->health != POWER_SUPPLY_HEALTH_OVERHEAT && + battery->health != POWER_SUPPLY_HEALTH_COLD && + battery->health != POWER_SUPPLY_HEALTH_OVERHEATLIMIT) { + dev_dbg(battery->dev, "%s: No need to check\n", __func__); + temp_chg_cnt = 0; + return false; + } + +#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY) + if (!battery->cooldown_mode) { + dev_err(battery->dev, "%s: Forced temp check block\n", __func__); + return true; + } +#endif + + sec_bat_temperature(battery); + + switch (battery->pdata->temp_check_type) { + case SEC_BATTERY_TEMP_CHECK_ADC: + temp_value = battery->temp_adc; + break; + case SEC_BATTERY_TEMP_CHECK_TEMP: + temp_value = battery->temperature; + break; + default: + dev_err(battery->dev, + "%s: Invalid Temp Check Type\n", __func__); + return true; + } + pre_health = battery->health; + if (battery->pdata->usb_thermal_source) { + if (battery->usb_temp >= battery->temp_highlimit_threshold || + (battery->usb_temp > battery->temp_highlimit_recovery + && battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) + health = POWER_SUPPLY_HEALTH_OVERHEATLIMIT; + } else { + if (temp_value >= battery->temp_highlimit_threshold || + (temp_value > battery->temp_highlimit_recovery + && battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) + health = POWER_SUPPLY_HEALTH_OVERHEATLIMIT; + } + if (health != POWER_SUPPLY_HEALTH_OVERHEATLIMIT) { + if (temp_value >= battery->temp_high_threshold || + (temp_value > battery->temp_high_recovery && + (battery->health == POWER_SUPPLY_HEALTH_OVERHEAT || + battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT))){ + health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (temp_value <= battery->temp_low_threshold || + (temp_value < battery->temp_low_recovery && + battery->health == POWER_SUPPLY_HEALTH_COLD)) { + health = POWER_SUPPLY_HEALTH_COLD; + } else { + health = POWER_SUPPLY_HEALTH_GOOD; + } + } + /* sweling check */ + if (battery->skip_swelling) { + sec_bat_set_current_event(battery, 0, + SEC_BAT_CURRENT_EVENT_SWELLING_MODE); + } else if (temp_value >= battery->pdata->swelling_high_temp_block || + (temp_value > battery->pdata->swelling_high_temp_recov && + (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING))) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING, + SEC_BAT_CURRENT_EVENT_SWELLING_MODE); + } else if (temp_value <= battery->pdata->swelling_low_temp_block_2nd || + (temp_value < battery->pdata->swelling_low_temp_recov_2nd && + (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING))) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING, + SEC_BAT_CURRENT_EVENT_SWELLING_MODE); + } else if (temp_value <= battery->pdata->swelling_low_temp_block_1st || + (temp_value < battery->pdata->swelling_low_temp_recov_1st && + (battery->current_event & (SEC_BAT_CURRENT_EVENT_LOW_TEMP | + SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING)))) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP, + SEC_BAT_CURRENT_EVENT_SWELLING_MODE); + } else { + sec_bat_set_current_event(battery, 0, + SEC_BAT_CURRENT_EVENT_SWELLING_MODE); + } + if (pre_swell_sts != (battery->current_event & SEC_BAT_CURRENT_EVENT_SWELLING_MODE)) { + swell_chg = true; +#if defined(CONFIG_BATTERY_CISD) + if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) { + battery->cisd.data[CISD_DATA_HIGH_TEMP_SWELLING]++; + battery->cisd.data[CISD_DATA_HIGH_TEMP_SWELLING_PER_DAY]++; + + } else if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) { + battery->cisd.data[CISD_DATA_LOW_TEMP_SWELLING]++; + battery->cisd.data[CISD_DATA_LOW_TEMP_SWELLING_PER_DAY]++; + } +#endif + } + + dev_info(battery->dev, "%s, health %d, swelling_mode %d(0x%x)\n",__func__, + health, battery->swelling_mode, + (battery->current_event & SEC_BAT_CURRENT_EVENT_SWELLING_MODE)); + if (battery->health != health) + temp_chg_cnt++; + else + temp_chg_cnt = 0; + + if (temp_chg_cnt >= battery->pdata->temp_check_count) { + battery->health = health; + temp_chg_cnt = 0; + } + if (pre_health != battery->health) { + battery->health_change = true; + dev_info(battery->dev, "%s, health_change true\n", __func__); + } else { + battery->health_change = false; + } + + if ((battery->health == POWER_SUPPLY_HEALTH_OVERHEAT) || + (battery->health == POWER_SUPPLY_HEALTH_COLD) || + (battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) { + if (battery->health_change) { + union power_supply_propval val = {0, }; + int float_voltage = battery->pdata->swelling_drop_float_voltage; + if (is_wireless_type(battery->cable_type)) { + val.intval = battery->health; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_HEALTH, val); + } + dev_info(battery->dev, + "%s: Unsafe Temperature\n", __func__); + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_NOT_CHARGING); +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_UNSAFETY_TEMPERATURE]++; + battery->cisd.data[CISD_DATA_UNSAFE_TEMPERATURE_PER_DAY]++; +#endif + + if (battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT) { + /* change charging current to battery (default 0mA) */ + sec_bat_set_charge(battery, SEC_BATTERY_BUCKOFF); + if (is_hv_afc_wire_type(battery->cable_type) && !battery->vbus_limit) { +#if defined(CONFIG_MUIC_HV) || defined(CONFIG_SUPPORT_QC30) + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_0V); +#endif + battery->vbus_limit = true; + pr_info("%s: Set AFC TA to 0V\n", __func__); + } + } else { + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + } + + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); + if (val.intval != float_voltage) { + val.intval = float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); + + } + return false; + } + } else { +#if defined(CONFIG_BATTERY_SWELLING) + if (swell_chg || battery->health_change) { + union power_supply_propval val = {0, }; + int float_voltage = battery->pdata->swelling_normal_float_voltage; + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); + + pr_info("%s: status(%d), swell_mode(%d:%d:%d), cv(%d)mV, temp(%d)\n", + __func__, battery->status, battery->swelling_mode, + battery->charging_mode, (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP), + val.intval, battery->temperature); + if (battery->current_event & (SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING | + SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING)) { + float_voltage = battery->pdata->swelling_drop_float_voltage; + if (!battery->swelling_mode) { + int swelling_rechg_voltage = battery->pdata->swelling_high_rechg_voltage; + pr_info("%s: swelling mode start\n", __func__); + battery->swelling_mode = SWELLING_MODE_CHARGING; + if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) { + swelling_rechg_voltage = battery->pdata->swelling_low_rechg_voltage; + } + if (battery->voltage_now > swelling_rechg_voltage) { + pr_info("%s: charging disable(%dmV)\n", __func__, battery->voltage_now); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + battery->expired_time = battery->pdata->expired_time; + battery->prev_safety_time = 0; + } + } + } + if (val.intval != float_voltage) { + pr_info("%s: float voltage change(%d -> %d)\n", __func__, val.intval, float_voltage); + if (battery->charging_mode > SEC_BATTERY_CHARGING_NONE) { + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + val.intval = float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + } else { + val.intval = float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); + } + } + if (battery->swelling_mode) { + if (!(battery->current_event & (SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING | + SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING))) { + pr_info("%s: swelling mode end. restart charging\n", __func__); + battery->swelling_mode = SWELLING_MODE_NONE; + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_SWELLING_RECOVERY_CNT]++; + battery->cisd.data[CISD_DATA_SWELLING_RECOVERY_CNT_PER_DAY]++; +#endif + } + } + } +#endif + /* if recovered from not charging */ + if ((battery->health == POWER_SUPPLY_HEALTH_GOOD) && + (battery->status == + POWER_SUPPLY_STATUS_NOT_CHARGING)) { + dev_info(battery->dev, + "%s: Safe Temperature\n", __func__); + if (battery->capacity >= 100) + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_FULL); + else /* Normal Charging */ + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_CHARGING); +#if !defined(CONFIG_BATTERY_SWELLING) + union power_supply_propval val = {0, }; + /* restore 4.4V float voltage */ + val.intval = battery->pdata->swelling_normal_float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); +#endif + if (battery->status == POWER_SUPPLY_STATUS_CHARGING) + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + return false; + } + } + return true; +} + +static bool sec_bat_check_fullcharged_condition( + struct sec_battery_info *battery) +{ + int full_check_type = SEC_BATTERY_FULLCHARGED_NONE; + +#if defined(CONFIG_BATTERY_SWELLING) + if (battery->swelling_mode != SWELLING_MODE_NONE) + return true; +#endif /* CONFIG_BATTERY_SWELLING */ + + if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST) + full_check_type = battery->pdata->full_check_type; + else + full_check_type = battery->pdata->full_check_type_2nd; + + switch (full_check_type) { + case SEC_BATTERY_FULLCHARGED_ADC: + case SEC_BATTERY_FULLCHARGED_FG_CURRENT: + case SEC_BATTERY_FULLCHARGED_SOC: + case SEC_BATTERY_FULLCHARGED_CHGGPIO: + case SEC_BATTERY_FULLCHARGED_CHGPSY: + break; + + /* If these is NOT full check type or NONE full check type, + * it is full-charged + */ + case SEC_BATTERY_FULLCHARGED_CHGINT: + case SEC_BATTERY_FULLCHARGED_TIME: + case SEC_BATTERY_FULLCHARGED_NONE: + default: + return true; + break; + } + + if (battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_SOC) { + if (battery->capacity < + battery->pdata->full_condition_soc) { + dev_dbg(battery->dev, + "%s: Not enough SOC (%d%%)\n", + __func__, battery->capacity); + return false; + } + } + + if (battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_VCELL) { + if (battery->voltage_now < + battery->pdata->full_condition_vcell) { + dev_dbg(battery->dev, + "%s: Not enough VCELL (%dmV)\n", + __func__, battery->voltage_now); + return false; + } + } + + if (battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_AVGVCELL) { + if (battery->voltage_avg < + battery->pdata->full_condition_avgvcell) { + dev_dbg(battery->dev, + "%s: Not enough AVGVCELL (%dmV)\n", + __func__, battery->voltage_avg); + return false; + } + } + + if (battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_OCV) { + if (battery->voltage_ocv < + battery->pdata->full_condition_ocv) { + dev_dbg(battery->dev, + "%s: Not enough OCV (%dmV)\n", + __func__, battery->voltage_ocv); + return false; + } + } + + return true; +} + +static void sec_bat_do_test_function( + struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + switch (battery->test_mode) { + case 1: + if (battery->status == POWER_SUPPLY_STATUS_CHARGING) { + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_DISCHARGING); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + } + break; + case 2: + if(battery->status == POWER_SUPPLY_STATUS_DISCHARGING) { + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_STATUS, value); + sec_bat_set_charging_status(battery, value.intval); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + } + battery->test_mode = 0; + break; + case 3: // clear temp block + battery->health = POWER_SUPPLY_HEALTH_GOOD; + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_DISCHARGING); + break; + case 4: + if(battery->status == POWER_SUPPLY_STATUS_DISCHARGING) { + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_STATUS, value); + sec_bat_set_charging_status(battery, value.intval); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + } + break; + default: + pr_info("%s: error test: unknown state\n", __func__); + break; + } +} + +static bool sec_bat_time_management( + struct sec_battery_info *battery) +{ + struct timespec ts = {0, }; + unsigned long charging_time; + + if (battery->charging_start_time == 0 || !battery->safety_timer_set) { + dev_dbg(battery->dev, + "%s: Charging Disabled\n", __func__); + return true; + } + + get_monotonic_boottime(&ts); + + if (ts.tv_sec >= battery->charging_start_time) { + charging_time = ts.tv_sec - battery->charging_start_time; + } else { + charging_time = 0xFFFFFFFF - battery->charging_start_time + + ts.tv_sec; + } + + battery->charging_passed_time = charging_time; + + switch (battery->status) { + case POWER_SUPPLY_STATUS_FULL: + if (battery->expired_time == 0) { + dev_info(battery->dev, + "%s: Recharging Timer Expired\n", __func__); + battery->health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING); + battery->is_recharging = false; + if (sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE)) { + dev_err(battery->dev, + "%s: Fail to Set Charger\n", __func__); + return true; + } + + return false; + } + break; + case POWER_SUPPLY_STATUS_CHARGING: + if ((battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) && + (battery->is_recharging && (battery->expired_time == 0))) { + dev_info(battery->dev, + "%s: Recharging Timer Expired\n", __func__); + battery->health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING); + battery->is_recharging = false; + if (sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE)) { + dev_err(battery->dev, + "%s: Fail to Set Charger\n", __func__); + return true; + } + return false; + } else if (!battery->is_recharging && + (battery->expired_time == 0)) { + dev_info(battery->dev, + "%s: Charging Timer Expired\n", __func__); + battery->health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING); +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_SAFETY_TIMER]++; + battery->cisd.data[CISD_DATA_SAFETY_TIMER_PER_DAY]++; +#endif +#if defined(CONFIG_SEC_ABC) + sec_abc_send_event("MODULE=battery@ERROR=safety_timer"); +#endif + if (sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE)) { + dev_err(battery->dev, + "%s: Fail to Set Charger\n", __func__); + return true; + } + return false; + } + break; + default: + dev_err(battery->dev, + "%s: Undefine Battery Status\n", __func__); + return true; + } + + return true; +} + +static bool sec_bat_check_fullcharged( + struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + int current_adc = 0; + int full_check_type = SEC_BATTERY_FULLCHARGED_NONE; + bool ret = false; + int err = 0; + int topoff_current = 0; + + if (!sec_bat_check_fullcharged_condition(battery)) + goto not_full_charged; + + if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST) { + full_check_type = battery->pdata->full_check_type; + topoff_current = battery->pdata->full_check_current_1st; + } else { + full_check_type = battery->pdata->full_check_type_2nd; + topoff_current = battery->pdata->full_check_current_2nd; + } +#if defined(CONFIG_BATTERY_SWELLING) + if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) + topoff_current = battery->pdata->swelling_low_temp_topoff; + else if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) + topoff_current = battery->pdata->swelling_high_temp_topoff; +#endif + + switch (full_check_type) { + case SEC_BATTERY_FULLCHARGED_ADC: + current_adc = + sec_bat_get_adc_value(battery, + SEC_BAT_ADC_CHANNEL_FULL_CHECK); + + dev_dbg(battery->dev, + "%s: Current ADC (%d)\n", + __func__, current_adc); + + if (current_adc < 0) + break; + battery->current_adc = current_adc; + + if (battery->current_adc < topoff_current) { + battery->full_check_cnt++; + dev_dbg(battery->dev, + "%s: Full Check ADC (%d)\n", + __func__, + battery->full_check_cnt); + } else + battery->full_check_cnt = 0; + break; + + case SEC_BATTERY_FULLCHARGED_FG_CURRENT: + if ((battery->current_now > 0 && battery->current_now < + battery->pdata->full_check_current_1st) && + (battery->current_avg > 0 && battery->current_avg < + topoff_current)) { + battery->full_check_cnt++; + dev_dbg(battery->dev, + "%s: Full Check Current (%d)\n", + __func__, + battery->full_check_cnt); + } else + battery->full_check_cnt = 0; + break; + + case SEC_BATTERY_FULLCHARGED_TIME: + if ((battery->charging_mode == + SEC_BATTERY_CHARGING_2ND ? + (battery->charging_passed_time - + battery->charging_fullcharged_time) : + battery->charging_passed_time) > + topoff_current) { + battery->full_check_cnt++; + dev_dbg(battery->dev, + "%s: Full Check Time (%d)\n", + __func__, + battery->full_check_cnt); + } else + battery->full_check_cnt = 0; + break; + + case SEC_BATTERY_FULLCHARGED_SOC: + if (battery->capacity <= topoff_current) { + battery->full_check_cnt++; + dev_dbg(battery->dev, + "%s: Full Check SOC (%d)\n", + __func__, + battery->full_check_cnt); + } else + battery->full_check_cnt = 0; + break; + + case SEC_BATTERY_FULLCHARGED_CHGGPIO: + err = gpio_request( + battery->pdata->chg_gpio_full_check, + "GPIO_CHG_FULL"); + if (err) { + dev_err(battery->dev, + "%s: Error in Request of GPIO\n", __func__); + break; + } + if (!(gpio_get_value_cansleep( + battery->pdata->chg_gpio_full_check) ^ + !battery->pdata->chg_polarity_full_check)) { + battery->full_check_cnt++; + dev_dbg(battery->dev, + "%s: Full Check GPIO (%d)\n", + __func__, battery->full_check_cnt); + } else + battery->full_check_cnt = 0; + gpio_free(battery->pdata->chg_gpio_full_check); + break; + + case SEC_BATTERY_FULLCHARGED_CHGINT: + case SEC_BATTERY_FULLCHARGED_CHGPSY: + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_STATUS, value); + + if (value.intval == POWER_SUPPLY_STATUS_FULL) { + battery->full_check_cnt++; + dev_info(battery->dev, + "%s: Full Check Charger (%d)\n", + __func__, battery->full_check_cnt); + } else + battery->full_check_cnt = 0; + break; + + /* If these is NOT full check type or NONE full check type, + * it is full-charged + */ + case SEC_BATTERY_FULLCHARGED_NONE: + battery->full_check_cnt = 0; + ret = true; + break; + default: + dev_err(battery->dev, + "%s: Invalid Full Check\n", __func__); + break; + } + + if (battery->full_check_cnt >= + battery->pdata->full_check_count) { + battery->full_check_cnt = 0; + ret = true; + } + +not_full_charged: + return ret; +} + +static void sec_bat_do_fullcharged( + struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + /* To let charger/fuel gauge know the full status, + * set status before calling sec_bat_set_charge() + */ +#if defined(CONFIG_BATTERY_CISD) + struct timespec now_ts; + + if (battery->status != POWER_SUPPLY_STATUS_FULL) { + battery->cisd.data[CISD_DATA_FULL_COUNT]++; + battery->cisd.data[CISD_DATA_FULL_COUNT_PER_DAY]++; + } +#endif + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_FULL); + + if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST && + battery->pdata->full_check_type_2nd != SEC_BATTERY_FULLCHARGED_NONE) { + battery->charging_fullcharged_time = battery->charging_passed_time; + value.intval = SEC_BAT_CHG_MODE_CHARGING_OFF; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CHARGING_ENABLED, value); + sec_bat_set_charging_current(battery, 0); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_2ND); + } else { +#if defined(CONFIG_BATTERY_CISD) + now_ts = ktime_to_timespec(ktime_get_boottime()); + if (!battery->is_recharging) { + battery->cisd.charging_end_time = now_ts.tv_sec; + } + if (battery->siop_level == 100) { + dev_info(battery->dev, "%s: cisd - leakage EFGH start(%ld)\n", __func__, ((unsigned long)now_ts.tv_sec)); + battery->cisd.state &= ~(CISD_STATE_LEAK_E|CISD_STATE_LEAK_F|CISD_STATE_LEAK_G); + battery->cisd.charging_end_time_2 = now_ts.tv_sec; + battery->cisd.recharge_count_2 = 0; + } else { + battery->cisd.state &= ~(CISD_STATE_LEAK_E|CISD_STATE_LEAK_F|CISD_STATE_LEAK_G); + battery->cisd.recharge_count_2 = 0; + battery->cisd.charging_end_time_2 = 0; + } +#endif + battery->is_recharging = false; + + if (!battery->wdt_kick_disable) { + pr_info("%s: wdt kick enable -> Charger Off, %d\n", + __func__, battery->wdt_kick_disable); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + } else { + pr_info("%s: wdt kick disabled -> skip charger off, %d\n", + __func__, battery->wdt_kick_disable); + } + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + sec_bat_aging_check(battery); +#endif + + /* this concept is only for power-off charging mode*/ + if (is_hv_wire_type(battery->cable_type) && is_hv_wire_type(battery->wire_status) && + !battery->store_mode && (battery->cable_type != SEC_BATTERY_CABLE_QC30) && + lpcharge && !battery->vbus_chg_by_full) { + /* vbus level : 9V --> 5V */ + battery->vbus_chg_by_full = true; +#if defined(CONFIG_MUIC_HV) || defined(CONFIG_SUPPORT_QC30) + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V); +#endif + pr_info("%s: vbus is set 5V by 2nd full\n", __func__); + } + + value.intval = POWER_SUPPLY_STATUS_FULL; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_STATUS, value); + } + + /* platform can NOT get information of battery + * because wakeup time is too short to check uevent + * To make sure that target is wakeup if full-charged, + * activated wake lock in a few seconds + */ + if (battery->pdata->polling_type == SEC_BATTERY_MONITOR_ALARM) + wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10); +} + +static bool sec_bat_fullcharged_check( + struct sec_battery_info *battery) +{ + if ((battery->charging_mode <= SEC_BATTERY_CHARGING_NONE) || + (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING)) { + dev_dbg(battery->dev, + "%s: No Need to Check Full-Charged\n", __func__); + return true; + } + + if (sec_bat_check_fullcharged(battery)) { + union power_supply_propval value = {0, }; +#if defined(CONFIG_BATTERY_SWELLING) + if (battery->swelling_mode != SWELLING_MODE_NONE) { + battery->is_recharging = false; + battery->swelling_mode = SWELLING_MODE_FULL; + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + battery->expired_time = battery->pdata->expired_time; + battery->prev_safety_time = 0; +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_SWELLING_FULL_CNT]++; + battery->cisd.data[CISD_DATA_SWELLING_FULL_CNT_PER_DAY]++; +#endif + pr_info("%s : swelling Full, capacity(%d)\n", + __func__, battery->capacity); + return true; + } +#endif + if (battery->capacity < 100) { + battery->full_check_cnt = battery->pdata->full_check_count; + } else { + sec_bat_do_fullcharged(battery); + } + + /* update capacity max */ + value.intval = battery->capacity; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_CHARGE_FULL, value); + pr_info("%s : forced full-charged sequence for the capacity(%d)\n", + __func__, battery->capacity); + } + + dev_info(battery->dev, + "%s: Charging Mode : %s\n", __func__, + battery->is_recharging ? + "Re-Charging" : + sec_bat_charging_mode_str[battery->charging_mode]); + + return true; +} + +static void sec_bat_get_temperature_info( + struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + switch (battery->pdata->thermal_source) { + case SEC_BATTERY_THERMAL_SOURCE_FG: + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_TEMP, value); + battery->temperature = value.intval; + + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_TEMP_AMBIENT, value); + battery->temper_amb = value.intval; + break; + case SEC_BATTERY_THERMAL_SOURCE_CALLBACK: + if (battery->pdata->get_temperature_callback) { + battery->pdata->get_temperature_callback( + POWER_SUPPLY_PROP_TEMP, &value); + battery->temperature = value.intval; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_TEMP, value); + + battery->pdata->get_temperature_callback( + POWER_SUPPLY_PROP_TEMP_AMBIENT, &value); + battery->temper_amb = value.intval; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_TEMP_AMBIENT, value); + } + break; + case SEC_BATTERY_THERMAL_SOURCE_ADC: + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_TEMP, &value); + battery->temperature = value.intval; + + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT, &value); + battery->temper_amb = value.intval; + + if (battery->pdata->usb_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_USB_TEMP, &value); + battery->usb_temp = value.intval; + if (battery->vbus_limit && battery->usb_temp <= battery->temp_highlimit_recovery) + battery->vbus_limit = false; + } + + if (battery->pdata->chg_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_CHG_TEMP, &value); + battery->chg_temp = value.intval; + } + + if (battery->pdata->wpc_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_WPC_TEMP, &value); + battery->wpc_temp = value.intval; + battery->coil_temp = value.intval; + } + + if (battery->pdata->slave_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP, &value); + battery->slave_chg_temp = value.intval; + + /* set temperature */ + value.intval = ((battery->slave_chg_temp) << 16) | (battery->chg_temp); + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_TEMP, value); + } + + if (battery->pdata->blkt_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_BLKT_TEMP, &value); + battery->blkt_temp = value.intval; + } +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + if (battery->temperature_test_battery > -300 && battery->temperature_test_battery < 3000) { + pr_info("%s : battery temperature test %d\n", __func__, battery->temperature_test_battery); + battery->temperature = battery->temperature_test_battery; + } + if (battery->temperature_test_usb > -300 && battery->temperature_test_usb < 3000) { + pr_info("%s : usb temperature test %d\n", __func__, battery->temperature_test_usb); + battery->usb_temp = battery->temperature_test_usb; + } + if (battery->temperature_test_wpc > -300 && battery->temperature_test_wpc < 3000) { + pr_info("%s : wpc temperature test %d\n", __func__, battery->temperature_test_wpc); + battery->wpc_temp = battery->temperature_test_wpc; + battery->coil_temp = battery->temperature_test_wpc; + } + if (battery->temperature_test_chg > -300 && battery->temperature_test_chg < 3000) { + pr_info("%s : chg temperature test %d\n", __func__, battery->temperature_test_chg); + battery->chg_temp = battery->temperature_test_chg; + } + if (battery->temperature_test_blkt > -300 && battery->temperature_test_blkt < 3000) { + pr_info("%s : blkt temperature test %d\n", __func__, battery->temperature_test_blkt); + battery->blkt_temp = battery->temperature_test_blkt; + } +#endif + +#if defined(CONFIG_SEC_FACTORY) + if (battery->temperature <= (-200)) { + value.intval = battery->usb_temp; + } else { + value.intval = battery->temperature; + } +#else + value.intval = battery->temperature; +#endif + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_TEMP, value); + + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_TEMP_AMBIENT, value); + + break; + default: + break; + } +} + +static void sec_bat_get_battery_info( + struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_NOW, value); + battery->voltage_now = value.intval; + + value.intval = SEC_BATTERY_VOLTAGE_AVERAGE; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_AVG, value); + battery->voltage_avg = value.intval; + + /* Do not call it to reduce time after cable_work, this funtion call FG full log*/ + if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL)) { + value.intval = SEC_BATTERY_VOLTAGE_OCV; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_AVG, value); + battery->voltage_ocv = value.intval; + } + + value.intval = SEC_BATTERY_CURRENT_MA; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CURRENT_NOW, value); + battery->current_now = value.intval; + + value.intval = SEC_BATTERY_CURRENT_MA; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CURRENT_AVG, value); + battery->current_avg = value.intval; + + /* input current limit in charger */ + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + battery->current_max = value.intval; + + /* check abnormal status for wireless charging */ + if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL) && + is_wireless_type(battery->cable_type)) { + value.intval = battery->capacity; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + } + + sec_bat_get_temperature_info(battery); + + /* To get SOC value (NOT raw SOC), need to reset value */ + value.intval = 0; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + /* if the battery status was full, and SOC wasn't 100% yet, + then ignore FG SOC, and report (previous SOC +1)% */ + battery->capacity = value.intval; + + dev_info(battery->dev, + "%s:Vnow(%dmV),Inow(%dmA),Imax(%dmA),Ichg(%dmA),SOC(%d%%),Tbat(%d),Tusb(%d),Tchg(%d),Twpc(%d),Tblkt(%d)" + "\n", __func__, + battery->voltage_now, battery->current_now, + battery->current_max, battery->charging_current, + battery->capacity, battery->temperature, + battery->usb_temp,battery->chg_temp, battery->wpc_temp, battery->blkt_temp + ); + dev_dbg(battery->dev, + "%s,Vavg(%dmV),Vocv(%dmV),Tamb(%d)," + "Iavg(%dmA),Iadc(%d)\n", + battery->present ? "Connected" : "Disconnected", + battery->voltage_avg, battery->voltage_ocv, + battery->temper_amb, + battery->current_avg, battery->current_adc); + + //battery_last_dcvs(battery->capacity, battery->voltage_avg, battery->temperature, battery->current_avg); +} + +static void sec_bat_polling_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of( + work, struct sec_battery_info, polling_work.work); + + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + dev_dbg(battery->dev, "%s: Activated\n", __func__); +} + +static void sec_bat_program_alarm( + struct sec_battery_info *battery, int seconds) +{ + alarm_start(&battery->polling_alarm, + ktime_add(battery->last_poll_time, ktime_set(seconds, 0))); +} + +static unsigned int sec_bat_get_polling_time( + struct sec_battery_info *battery) +{ + if (battery->status == + POWER_SUPPLY_STATUS_FULL) + battery->polling_time = + battery->pdata->polling_time[ + POWER_SUPPLY_STATUS_CHARGING]; + else + battery->polling_time = + battery->pdata->polling_time[ + battery->status]; + + battery->polling_short = true; + + switch (battery->status) { + case POWER_SUPPLY_STATUS_CHARGING: + if (battery->polling_in_sleep) + battery->polling_short = false; + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + if (battery->polling_in_sleep && (battery->ps_enable != true)) { + battery->polling_time = + battery->pdata->polling_time[ + SEC_BATTERY_POLLING_TIME_SLEEP]; + } else + battery->polling_time = + battery->pdata->polling_time[ + battery->status]; + if (!battery->wc_enable) { + battery->polling_time = battery->pdata->polling_time[ + SEC_BATTERY_POLLING_TIME_CHARGING]; + pr_info("%s: wc_enable is false, polling time is 30sec\n", __func__); + } + battery->polling_short = false; + break; + case POWER_SUPPLY_STATUS_FULL: + if (battery->polling_in_sleep) { + if (!(battery->pdata->full_condition_type & + SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL) && + battery->charging_mode == + SEC_BATTERY_CHARGING_NONE) { + battery->polling_time = + battery->pdata->polling_time[ + SEC_BATTERY_POLLING_TIME_SLEEP]; + } + battery->polling_short = false; + } else { + if (battery->charging_mode == + SEC_BATTERY_CHARGING_NONE) + battery->polling_short = false; + } + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE || + (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) && + (battery->health_check_count > 0)) { + battery->health_check_count--; + battery->polling_time = 1; + battery->polling_short = false; + } + break; + } + + if (battery->polling_short) + return battery->pdata->polling_time[ + SEC_BATTERY_POLLING_TIME_BASIC]; + /* set polling time to 46s to reduce current noise on wc */ + else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS && + battery->status == POWER_SUPPLY_STATUS_CHARGING) + battery->polling_time = 46; + + return battery->polling_time; +} + +static bool sec_bat_is_short_polling( + struct sec_battery_info *battery) +{ + /* Change the full and short monitoring sequence + * Originally, full monitoring was the last time of polling_count + * But change full monitoring to first time + * because temperature check is too late + */ + if (!battery->polling_short || battery->polling_count == 1) + return false; + else + return true; +} + +static void sec_bat_update_polling_count( + struct sec_battery_info *battery) +{ + /* do NOT change polling count in sleep + * even though it is short polling + * to keep polling count along sleep/wakeup + */ + if (battery->polling_short && battery->polling_in_sleep) + return; + + if (battery->polling_short && + ((battery->polling_time / + battery->pdata->polling_time[ + SEC_BATTERY_POLLING_TIME_BASIC]) + > battery->polling_count)) + battery->polling_count++; + else + battery->polling_count = 1; /* initial value = 1 */ +} + +static void sec_bat_set_polling( + struct sec_battery_info *battery) +{ + unsigned int polling_time_temp = 0; + + dev_dbg(battery->dev, "%s: Start\n", __func__); + + polling_time_temp = sec_bat_get_polling_time(battery); + + dev_dbg(battery->dev, + "%s: Status:%s, Sleep:%s, Charging:%s, Short Poll:%s\n", + __func__, sec_bat_status_str[battery->status], + battery->polling_in_sleep ? "Yes" : "No", + (battery->charging_mode == + SEC_BATTERY_CHARGING_NONE) ? "No" : "Yes", + battery->polling_short ? "Yes" : "No"); + dev_info(battery->dev, + "%s: Polling time %d/%d sec.\n", __func__, + battery->polling_short ? + (polling_time_temp * battery->polling_count) : + polling_time_temp, battery->polling_time); + + /* To sync with log above, + * change polling count after log is displayed + * Do NOT update polling count in initial monitor + */ + if (!battery->pdata->monitor_initial_count) + sec_bat_update_polling_count(battery); + else + dev_dbg(battery->dev, + "%s: Initial monitor %d times left.\n", __func__, + battery->pdata->monitor_initial_count); + + switch (battery->pdata->polling_type) { + case SEC_BATTERY_MONITOR_WORKQUEUE: + if (battery->pdata->monitor_initial_count) { + battery->pdata->monitor_initial_count--; + schedule_delayed_work(&battery->polling_work, HZ); + } else + schedule_delayed_work(&battery->polling_work, + polling_time_temp * HZ); + break; + case SEC_BATTERY_MONITOR_ALARM: + battery->last_poll_time = ktime_get_boottime(); + + if (battery->pdata->monitor_initial_count) { + battery->pdata->monitor_initial_count--; + sec_bat_program_alarm(battery, 1); + } else + sec_bat_program_alarm(battery, polling_time_temp); + break; + case SEC_BATTERY_MONITOR_TIMER: + break; + default: + break; + } + dev_dbg(battery->dev, "%s: End\n", __func__); +} + +/* OTG during HV wireless charging or sleep mode have 4.5W normal wireless charging UI */ +bool sec_bat_hv_wc_normal_mode_check(struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + if (value.intval || sleep_mode) { + pr_info("%s: otg(%d), sleep_mode(%d)\n", __func__, value.intval, sleep_mode); + return true; + } + return false; +} + + +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) +extern bool get_usb_enumeration_state(void); +/* To disaply slow charging when usb charging 100MA*/ +static void sec_bat_check_slowcharging_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, slowcharging_work.work); +#if defined(CONFIG_CCIC_NOTIFIER) + if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT && + battery->cable_type == SEC_BATTERY_CABLE_USB) { + if (!get_usb_enumeration_state() && + (battery->current_event & SEC_BAT_CURRENT_EVENT_USB_100MA)) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE, 0); + battery->max_charge_power = battery->input_voltage * battery->current_max; + } + } +#endif + dev_info(battery->dev, "%s: \n",__func__); +} +#endif + +static void sec_bat_wc_cv_mode_check(struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + + pr_info("%s: battery->wc_cv_mode = %d \n", __func__, battery->wc_cv_mode); + + if (battery->capacity >= battery->pdata->wireless_cc_cv) { + pr_info("%s: 4.5W WC Changed Vout input current limit\n", __func__); + battery->wc_cv_mode = true; + sec_bat_set_charging_current(battery, 0); + value.intval = WIRELESS_VOUT_CC_CV_VOUT; // 5.5V + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + value.intval = WIRELESS_VRECT_ADJ_ROOM_5; // 80mv + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + if ((battery->cable_type == SEC_BATTERY_CABLE_WIRELESS || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA)) { + value.intval = WIRELESS_CLAMP_ENABLE; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } + /* Change FOD values for CV mode */ + value.intval = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_STATUS, value); + } +} + +static void sec_bat_siop_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, siop_work.work); + + pr_info("%s : set current by siop level(%d)\n",__func__, battery->siop_level); + + sec_bat_set_charging_current(battery, 0); + wake_unlock(&battery->siop_wake_lock); +} + +static void sec_bat_siop_level_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, siop_level_work.work); + + if (battery->siop_prev_event != battery->siop_event) { + wake_unlock(&battery->siop_level_wake_lock); + return; + } + queue_delayed_work(battery->monitor_wqueue, &battery->siop_work, 0); + wake_lock(&battery->siop_wake_lock); + wake_unlock(&battery->siop_level_wake_lock); +} + +static void sec_bat_wc_headroom_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, wc_headroom_work.work); + union power_supply_propval value = {0, }; + + /* The default headroom is high, because initial wireless charging state is unstable. + After 10sec wireless charging, however, recover headroom level to avoid chipset damage */ + if (battery->wc_status != SEC_WIRELESS_PAD_NONE) { + /* When the capacity is higher than 99, and the device is in 5V wireless charging state, + then Vrect headroom has to be headroom_2. + Refer to the sec_bat_siop_work function. */ + if (battery->capacity < 99 && battery->status != POWER_SUPPLY_STATUS_FULL) { + if (is_nv_wireless_type(battery->cable_type)) { + if (battery->capacity < battery->pdata->wireless_cc_cv) + value.intval = WIRELESS_VRECT_ADJ_ROOM_4; /* WPC 4.5W, Vrect Room 30mV */ + else + value.intval = WIRELESS_VRECT_ADJ_ROOM_5; /* WPC 4.5W, Vrect Room 80mV */ + } else if (is_hv_wireless_type(battery->cable_type)) { + value.intval = WIRELESS_VRECT_ADJ_ROOM_5; + } else { + value.intval = WIRELESS_VRECT_ADJ_OFF; + } + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + pr_info("%s: Changed Vrect adjustment from Rx activation(10seconds)", __func__); + } + if (is_nv_wireless_type(battery->cable_type)) + sec_bat_wc_cv_mode_check(battery); + } + wake_unlock(&battery->wc_headroom_wake_lock); +} + +static void sec_bat_siop_event_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, siop_event_work.work); + + union power_supply_propval value = {0, }; + + if (battery->cable_type != SEC_BATTERY_CABLE_WIRELESS_PACK && + battery->cable_type != SEC_BATTERY_CABLE_WIRELESS_PACK_TA) { + battery->siop_prev_event = battery->siop_event; + wake_unlock(&battery->siop_event_wake_lock); + return; + } + + if (!(battery->siop_prev_event & SIOP_EVENT_WPC_CALL) && (battery->siop_event & SIOP_EVENT_WPC_CALL)) { + pr_info("%s : set current by siop event(%d)\n",__func__, battery->siop_event); + if (battery->capacity >= battery->pdata->wireless_cc_cv) { + pr_info("%s SIOP EVENT CALL CV START.\n", __func__); + value.intval = WIRELESS_VOUT_CV_CALL; + } else { + pr_info("%s SIOP EVENT CALL CC START.\n", __func__); + value.intval = WIRELESS_VOUT_CC_CALL; + } + /* set current first */ + sec_bat_set_charging_current(battery, 0); + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } else if ((battery->siop_prev_event & SIOP_EVENT_WPC_CALL) && !(battery->siop_event & SIOP_EVENT_WPC_CALL)) { + if (battery->wc_cv_mode) + value.intval = WIRELESS_VOUT_CC_CV_VOUT; // 5.5V + else + value.intval = WIRELESS_VOUT_5V; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + wake_lock(&battery->siop_level_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->siop_level_work, 0); + } + battery->siop_prev_event = battery->siop_event; + wake_unlock(&battery->siop_event_wake_lock); +} + +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) +static void sec_bat_fw_update_work(struct sec_battery_info *battery, int mode) +{ + union power_supply_propval value = {0, }; + + dev_info(battery->dev, "%s \n", __func__); + + wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10); + + switch (mode) { + case SEC_WIRELESS_RX_SDCARD_MODE: + case SEC_WIRELESS_RX_BUILT_IN_MODE: + value.intval = mode; + psy_do_property(battery->pdata->wireless_charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_POWERED_OTG_CONTROL, value); + break; + case SEC_WIRELESS_TX_ON_MODE: + value.intval = true; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + + value.intval = mode; + psy_do_property(battery->pdata->wireless_charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_POWERED_OTG_CONTROL, value); + + break; + case SEC_WIRELESS_TX_OFF_MODE: + value.intval = false; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + break; + default: + break; + } +} + +static void sec_bat_fw_init_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, fw_init_work.work); + + union power_supply_propval value = {0, }; + int uno_status = 0, wpc_det = 0; + + dev_info(battery->dev, "%s \n", __func__); + + wpc_det = gpio_get_value(battery->pdata->wpc_det); + + pr_info("%s wpc_det = %d \n", __func__, wpc_det); + + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + uno_status = value.intval; + pr_info("%s uno = %d \n", __func__, uno_status); + + if (!uno_status && !wpc_det) { + pr_info("%s uno on \n", __func__); + value.intval = true; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + } + + value.intval = SEC_WIRELESS_RX_INIT; + psy_do_property(battery->pdata->wireless_charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_POWERED_OTG_CONTROL, value); + + if (!uno_status && !wpc_det) { + pr_info("%s uno off \n", __func__); + value.intval = false; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + } +} +#endif +#if defined(CONFIG_UPDATE_BATTERY_DATA) +static int sec_bat_parse_dt(struct device *dev, struct sec_battery_info *battery); +static void sec_bat_update_data_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, batt_data_work.work); + + sec_battery_update_data(battery->data_path); + wake_unlock(&battery->batt_data_wake_lock); +} +#endif + +static void sec_bat_misc_event_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, misc_event_work.work); + int xor_misc_event = battery->prev_misc_event ^ battery->misc_event; + + if ((xor_misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE | BATT_MISC_EVENT_HICCUP_TYPE)) && + (battery->cable_type == SEC_BATTERY_CABLE_NONE)) { + if (battery->misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE | BATT_MISC_EVENT_HICCUP_TYPE)) { + sec_bat_set_charge(battery, SEC_BATTERY_BUCKOFF); + } else if (battery->prev_misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE | BATT_MISC_EVENT_HICCUP_TYPE)) { + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + } + } + + pr_info("%s: change misc event(0x%x --> 0x%x)\n", + __func__, battery->prev_misc_event, battery->misc_event); + battery->prev_misc_event = battery->misc_event; + wake_unlock(&battery->misc_event_wake_lock); + + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); +} + +static void sec_bat_calculate_safety_time(struct sec_battery_info *battery) +{ + unsigned long long expired_time = battery->expired_time; + struct timespec ts = {0, }; + int curr = 0; + int input_power = battery->current_max * battery->input_voltage * 1000; + int charging_power = battery->charging_current * (battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv); + static int discharging_cnt = 0; + + if (battery->current_avg < 0) { + discharging_cnt++; + } else { + discharging_cnt = 0; + } + + if (discharging_cnt >= 5) { + battery->expired_time = battery->pdata->expired_time; + battery->prev_safety_time = 0; + pr_info("%s : SAFETY TIME RESET! DISCHARGING CNT(%d)\n", + __func__, discharging_cnt); + discharging_cnt = 0; + return; + } else if (battery->lcd_status && battery->stop_timer) { + battery->prev_safety_time = 0; + return; + } + + get_monotonic_boottime(&ts); + + if (battery->prev_safety_time == 0) { + battery->prev_safety_time = ts.tv_sec; + } + + if (input_power > charging_power) { + curr = battery->charging_current; + } else { + curr = input_power / (battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv); + curr = (curr * 9) / 10; + } + + if (battery->lcd_status && !battery->stop_timer) { + battery->stop_timer = true; + } else if (!battery->lcd_status && battery->stop_timer) { + battery->stop_timer = false; + } + + pr_info("%s : EXPIRED_TIME(%llu), IP(%d), CP(%d), CURR(%d), STANDARD(%d)\n", + __func__, expired_time, input_power, charging_power, curr, battery->pdata->standard_curr); + + if (curr == 0) + return; + + expired_time = (expired_time * battery->pdata->standard_curr) / curr; + + pr_info("%s : CAL_EXPIRED_TIME(%llu) TIME NOW(%ld) TIME PREV(%ld)\n", __func__, expired_time, ts.tv_sec, battery->prev_safety_time); + + if (expired_time <= ((ts.tv_sec - battery->prev_safety_time) * 1000)) + expired_time = 0; + else + expired_time -= ((ts.tv_sec - battery->prev_safety_time) * 1000); + + battery->cal_safety_time = expired_time; + expired_time = (expired_time * curr) / battery->pdata->standard_curr; + + battery->expired_time = expired_time; + battery->prev_safety_time = ts.tv_sec; + pr_info("%s : REMAIN_TIME(%ld) CAL_REMAIN_TIME(%ld)\n", __func__, battery->expired_time, battery->cal_safety_time); +} + +static void sec_bat_monitor_work( + struct work_struct *work) +{ + struct sec_battery_info *battery = + container_of(work, struct sec_battery_info, + monitor_work.work); + static struct timespec old_ts = {0, }; + struct timespec c_ts = {0, }; + union power_supply_propval value = {0, }; + + dev_dbg(battery->dev, "%s: Start\n", __func__); + c_ts = ktime_to_timespec(ktime_get_boottime()); + + mutex_lock(&battery->wclock); + if (!battery->wc_enable) { + pr_info("%s: wc_enable(%d), cnt(%d)\n", + __func__, battery->wc_enable, battery->wc_enable_cnt); + if (battery->wc_enable_cnt > battery->wc_enable_cnt_value) { + battery->wc_enable = true; + battery->wc_enable_cnt = 0; + if (battery->pdata->wpc_en) { + gpio_direction_output(battery->pdata->wpc_en, 0); + pr_info("%s: WC CONTROL: Enable", __func__); + } + pr_info("%s: wpc_en(%d)\n", + __func__, gpio_get_value(battery->pdata->wpc_en)); + } + battery->wc_enable_cnt++; + } + mutex_unlock(&battery->wclock); + + /* monitor once after wakeup */ + if (battery->polling_in_sleep) { + battery->polling_in_sleep = false; + if ((battery->status == POWER_SUPPLY_STATUS_DISCHARGING) && + (battery->ps_enable != true)) { + if ((unsigned long)(c_ts.tv_sec - old_ts.tv_sec) < 10 * 60) { + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_NOW, value); + battery->voltage_now = value.intval; + + value.intval = 0; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + battery->capacity = value.intval; + + sec_bat_get_temperature_info(battery); +#if defined(CONFIG_BATTERY_CISD) + sec_bat_cisd_check(battery); +#endif + power_supply_changed(battery->psy_bat); + pr_info("Skip monitor work(%ld, Vnow:%d(mV), SoC:%d(%%), Tbat:%d(0.1'C))\n", + c_ts.tv_sec - old_ts.tv_sec, battery->voltage_now, battery->capacity, battery->temperature); + + goto skip_monitor; + } + } + } + /* update last monitor time */ + old_ts = c_ts; + + sec_bat_get_battery_info(battery); +#if defined(CONFIG_BATTERY_CISD) + sec_bat_cisd_check(battery); +#endif + /* time to full check */ + sec_bat_calc_time_to_full(battery); + + /* 0. test mode */ + if (battery->test_mode) { + dev_err(battery->dev, "%s: Test Mode\n", __func__); + sec_bat_do_test_function(battery); + if (battery->test_mode != 0) + goto continue_monitor; + } + + /* 1. battery check */ + if (!sec_bat_battery_cable_check(battery)) + goto continue_monitor; + + /* 2. voltage check */ + if (!sec_bat_voltage_check(battery)) + goto continue_monitor; + + /* monitor short routine in initial monitor */ + if (battery->pdata->monitor_initial_count || sec_bat_is_short_polling(battery)) + goto skip_current_monitor; + + /* 3. time management */ + if (!sec_bat_time_management(battery)) + goto continue_monitor; + + /* 4. temperature check */ + if (!sec_bat_temperature_check(battery)) + goto continue_monitor; + + /* 5. full charging check */ + sec_bat_fullcharged_check(battery); + + /* 6. additional check */ + if (battery->pdata->monitor_additional_check) + battery->pdata->monitor_additional_check(); + + if ((battery->cable_type == SEC_BATTERY_CABLE_WIRELESS || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA) && + !battery->wc_cv_mode && battery->charging_passed_time > 10) + sec_bat_wc_cv_mode_check(battery); + +continue_monitor: + /* clear HEATING_CONTROL*/ + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL); + + /* calculate safety time */ + if (battery->charging_mode > SEC_BATTERY_CHARGING_NONE) + sec_bat_calculate_safety_time(battery); + + /* set charging current */ + sec_bat_set_charging_current(battery, 0); + +skip_current_monitor: + dev_dbg(battery->dev, + "%s: HLT(%d) HLR(%d) HT(%d), HR(%d), LT(%d), LR(%d), lpcharge(%d)\n", + __func__, battery->temp_highlimit_threshold, battery->temp_highlimit_recovery, + battery->temp_high_threshold, battery->temp_high_recovery, + battery->temp_low_threshold, battery->temp_low_recovery, lpcharge); + + dev_info(battery->dev, + "%s: Status(%s), mode(%s), Health(%s), Cable(%s, %s, %d, %d), level(%d%%), slate_mode(%d), store_mode(%d)" +#if defined(CONFIG_AFC_CHARGER_MODE) + ", HV(%s), sleep_mode(%d)" +#endif +#if defined(CONFIG_BATTERY_AGE_FORECAST) +#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE) + ", Cycle(%dw)" +#else + ", Cycle(%d)" +#endif +#endif + "\n", __func__, + sec_bat_status_str[battery->status], + sec_bat_charging_mode_str[battery->charging_mode], + sec_bat_health_str[battery->health], + sec_cable_type[battery->cable_type], + sec_cable_type[battery->wire_status], + battery->muic_cable_type, + battery->pd_usb_attached, + battery->siop_level, + is_slate_mode(battery), + battery->store_mode +#if defined(CONFIG_AFC_CHARGER_MODE) + , battery->hv_chg_name, sleep_mode +#endif +#if defined(CONFIG_BATTERY_AGE_FORECAST) + , battery->batt_cycle +#endif + ); +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + dev_info(battery->dev, + "%s: battery->stability_test(%d), battery->eng_not_full_status(%d)\n", + __func__, battery->stability_test, battery->eng_not_full_status); +#endif +#if defined(CONFIG_SEC_FACTORY) + if (!is_nocharge_type(battery->cable_type)) { +#else + if (!is_nocharge_type(battery->cable_type) && battery->store_mode) { +#endif + dev_info(battery->dev, + "%s: @battery->capacity = (%d), battery->status= (%d), battery->store_mode=(%d)\n", + __func__, battery->capacity, battery->status, battery->store_mode); + + if (battery->capacity >= battery->pdata->store_mode_charging_max) { + int chg_mode = battery->misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE | BATT_MISC_EVENT_HICCUP_TYPE) ? + SEC_BATTERY_BUCKOFF : SEC_BATTERY_CHARGING_NONE; + /* to discharge the battery, off buck */ + if (battery->capacity > battery->pdata->store_mode_charging_max) + chg_mode = SEC_BATTERY_BUCKOFF; + + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_DISCHARGING); + sec_bat_set_charge(battery, chg_mode); + } + + if ((battery->capacity <= battery->pdata->store_mode_charging_min) && (battery->status == POWER_SUPPLY_STATUS_DISCHARGING)) { + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_CHARGING); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + } + } + power_supply_changed(battery->psy_bat); + +skip_monitor: + sec_bat_set_polling(battery); + + if (battery->capacity <= 0 || battery->health_change) + wake_lock_timeout(&battery->monitor_wake_lock, HZ * 5); + else + wake_unlock(&battery->monitor_wake_lock); + + dev_dbg(battery->dev, "%s: End\n", __func__); + + return; +} + +static enum alarmtimer_restart sec_bat_alarm( + struct alarm *alarm, ktime_t now) +{ + struct sec_battery_info *battery = container_of(alarm, + struct sec_battery_info, polling_alarm); + + dev_dbg(battery->dev, + "%s\n", __func__); + + /* In wake up, monitor work will be queued in complete function + * To avoid duplicated queuing of monitor work, + * do NOT queue monitor work in wake up by polling alarm + */ + if (!battery->polling_in_sleep) { + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + dev_dbg(battery->dev, "%s: Activated\n", __func__); + } + + return ALARMTIMER_NORESTART; +} + +static void sec_bat_check_input_voltage(struct sec_battery_info *battery) +{ + unsigned int voltage = 0; + int input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit; + + if (battery->cable_type == SEC_BATTERY_CABLE_PDIC) { + battery->max_charge_power = battery->pd_max_charge_power; + return; + } + else if (is_hv_wire_12v_type(battery->cable_type)) + voltage = SEC_INPUT_VOLTAGE_12V; + else if (is_hv_wire_9v_type(battery->cable_type)) + voltage = SEC_INPUT_VOLTAGE_9V; + else if (is_hv_wireless_type(battery->cable_type) || + battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) + voltage = SEC_INPUT_VOLTAGE_10V; + else + voltage = SEC_INPUT_VOLTAGE_5V; + + battery->input_voltage = voltage; + battery->charge_power = voltage * input_current; +#if !defined(CONFIG_SEC_FACTORY) + if (battery->charge_power > battery->max_charge_power) +#endif + battery->max_charge_power = battery->charge_power; + + pr_info("%s: battery->input_voltage : %dV, %dmW, %dmW)\n", __func__, + battery->input_voltage, battery->charge_power, battery->max_charge_power); +} + +static void sec_bat_cable_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, cable_work.work); + union power_supply_propval val = {0, }; + int current_cable_type = SEC_BATTERY_CABLE_NONE; + int prev_cable_type; + + dev_info(battery->dev, "%s: Start\n", __func__); + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL, + SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL); +#if defined(CONFIG_CCIC_NOTIFIER) + if (battery->wire_status == SEC_BATTERY_CABLE_PDIC) { + sec_bat_get_input_current_in_power_list(battery); + sec_bat_get_charging_current_in_power_list(battery); + } +#endif + + if (battery->wc_status && battery->wc_enable) { + int wireless_current, wire_current; + int temp_current_type; + + if (battery->wc_status == SEC_WIRELESS_PAD_WPC) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS; + else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_HV) + current_cable_type = SEC_BATTERY_CABLE_HV_WIRELESS; + else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS_PACK; + else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK_TA) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS_PACK_TA; + else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_STAND) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS_STAND; + else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_STAND_HV) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_STAND; + else if (battery->wc_status == SEC_WIRELESS_PAD_VEHICLE) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS_VEHICLE; + else if (battery->wc_status == SEC_WIRELESS_PAD_VEHICLE_HV) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE; + else if (battery->wc_status == SEC_WIRELESS_PAD_PREPARE_HV) + current_cable_type = SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV; + else + current_cable_type = SEC_BATTERY_CABLE_PMA_WIRELESS; + + if (current_cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) + temp_current_type = SEC_BATTERY_CABLE_HV_WIRELESS; + else + temp_current_type = current_cable_type; + + if (battery->wire_status != SEC_BATTERY_CABLE_NONE) { + wireless_current = battery->pdata->charging_current[temp_current_type].input_current_limit; + wireless_current = wireless_current * (is_hv_wireless_type(temp_current_type) ? + SEC_INPUT_VOLTAGE_9V : SEC_INPUT_VOLTAGE_5V); + if (battery->wire_status == SEC_BATTERY_CABLE_PDIC) { + if (wireless_current < battery->pd_max_charge_power) + current_cable_type = battery->wire_status; + } else { + wire_current = (battery->wire_status == SEC_BATTERY_CABLE_PREPARE_TA ? + battery->pdata->charging_current[SEC_BATTERY_CABLE_TA].input_current_limit : + battery->pdata->charging_current[battery->wire_status].input_current_limit); + + wire_current = wire_current * (is_hv_wire_type(battery->wire_status) ? + (battery->wire_status == SEC_BATTERY_CABLE_12V_TA ? SEC_INPUT_VOLTAGE_12V : SEC_INPUT_VOLTAGE_9V) + : SEC_INPUT_VOLTAGE_5V); + pr_info("%s: wl_cur(%d), wr_cur(%d), wc_cable_type(%d), wire_cable_type(%d)\n", + __func__, wireless_current, wire_current, current_cable_type, battery->wire_status); + + if (wireless_current < wire_current) + current_cable_type = battery->wire_status; + } + } + } +#if defined(CONFIG_USE_POGO) + else if (battery->pogo_status) { + int pogo_current, wire_current; + + current_cable_type = SEC_BATTERY_CABLE_POGO; + + if (battery->wire_status != SEC_BATTERY_CABLE_NONE) { + pogo_current = battery->pdata->charging_current[current_cable_type].input_current_limit; + pogo_current = pogo_current * SEC_INPUT_VOLTAGE_5V; + + if (battery->wire_status == SEC_BATTERY_CABLE_PDIC) { + if (pogo_current < battery->pd_max_charge_power) + current_cable_type = battery->wire_status; + } else { + wire_current = (battery->wire_status == SEC_BATTERY_CABLE_PREPARE_TA ? + battery->pdata->charging_current[SEC_BATTERY_CABLE_TA].input_current_limit : + battery->pdata->charging_current[battery->wire_status].input_current_limit); + + wire_current = wire_current * (is_hv_wire_type(battery->wire_status) ? + (battery->wire_status == SEC_BATTERY_CABLE_12V_TA ? SEC_INPUT_VOLTAGE_12V : SEC_INPUT_VOLTAGE_9V) + : SEC_INPUT_VOLTAGE_5V); + pr_info("%s: pogo_cur(%d), wr_cur(%d), pogo_cable_type(%d), wire_cable_type(%d)\n", + __func__, pogo_current, wire_current, current_cable_type, battery->wire_status); + + if (pogo_current < wire_current) + current_cable_type = battery->wire_status; + } + } + } +#endif + else + current_cable_type = battery->wire_status; + + if(is_slate_mode(battery)) { + dev_info(battery->dev, + "%s:slate mode on\n",__func__); + current_cable_type = SEC_BATTERY_CABLE_NONE; + sec_bat_set_charge(battery, SEC_BATTERY_BUCKOFF); +#if defined(CONFIG_MUIC_HV) || defined(CONFIG_SUPPORT_QC30) + /* set cable_type before slate mode off */ + if (battery->vbus_chg_by_siop) { + if (battery->chg_limit_recovery_cable == SEC_BATTERY_CABLE_12V_TA) { + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_12V); + } else { + muic_afc_set_voltage(SEC_INPUT_VOLTAGE_9V); + } + } +#endif + } + + if (current_cable_type == SEC_BATTERY_CABLE_PDIC && + battery->cable_type == SEC_BATTERY_CABLE_PDIC) { + cancel_delayed_work(&battery->afc_work); + wake_unlock(&battery->afc_wake_lock); + sec_bat_set_current_event(battery, 0, + SEC_BAT_CURRENT_EVENT_AFC | SEC_BAT_CURRENT_EVENT_AICL); + battery->aicl_current = 0; + sec_bat_set_charging_current(battery, 0); + goto end_of_cable_work; + } + + /* to clear this value when cable type switched without dettach */ + if ((is_wired_type(battery->cable_type) && is_wireless_type(current_cable_type)) || + (is_wireless_type(battery->cable_type) && is_wired_type(current_cable_type)) || + (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC)) + battery->max_charge_power = 0; + + if (current_cable_type == battery->cable_type) { + dev_dbg(battery->dev, + "%s: Cable is NOT Changed(%d)\n", + __func__, battery->cable_type); + /* Do NOT activate cable work for NOT changed */ + goto end_of_cable_work; + } + + prev_cable_type = battery->cable_type; + battery->cable_type = current_cable_type; + battery->wpc_vout_level = WIRELESS_VOUT_10V; + if (is_wireless_type(battery->cable_type)) { + power_supply_changed(battery->psy_bat); + /* After 10sec wireless charging, Vrect headroom has to be reduced */ + wake_lock(&battery->wc_headroom_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->wc_headroom_work, + msecs_to_jiffies(10000)); + } + + if (battery->pdata->check_cable_result_callback) + battery->pdata->check_cable_result_callback(battery->cable_type); + /* platform can NOT get information of cable connection + * because wakeup time is too short to check uevent + * To make sure that target is wakeup + * if cable is connected and disconnected, + * activated wake lock in a few seconds + */ + wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10); + + if (!is_nocharge_type(prev_cable_type) && !is_nocharge_type(battery->cable_type)) + goto skip_update_battery_status; +#if defined(CONFIG_BATTERY_SWELLING) + battery->swelling_mode = SWELLING_MODE_NONE; + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SWELLING_MODE); + /* restore 4.4V float voltage */ + val.intval = battery->pdata->swelling_normal_float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, val); + pr_info("%s: float voltage = %d\n", __func__, val.intval); +#endif + + if (is_nocharge_type(battery->cable_type) || + ((battery->pdata->cable_check_type & + SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE) && + battery->cable_type == SEC_BATTERY_CABLE_UNKNOWN)) { + /* initialize all status */ + battery->vbus_chg_by_siop = false; + battery->vbus_chg_by_full = false; + battery->is_recharging = false; +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.charging_end_time = 0; + battery->cisd.recharge_count = 0; + battery->cisd.charging_end_time_2 = 0; + battery->cisd.recharge_count_2 = 0; + battery->cisd.ab_vbat_check_count = 0; + battery->cisd.state &= ~CISD_STATE_OVER_VOLTAGE; +#endif + battery->input_voltage = 0; + battery->charge_power = 0; + battery->max_charge_power = 0; + battery->pd_max_charge_power = 0; + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_DISCHARGING); + battery->chg_limit = false; + battery->mix_limit = false; + battery->chg_limit_recovery_cable = SEC_BATTERY_CABLE_NONE; + battery->wc_heating_start_time = 0; + battery->health = POWER_SUPPLY_HEALTH_GOOD; + cancel_delayed_work(&battery->afc_work); + wake_unlock(&battery->afc_wake_lock); + sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB, + battery->pdata->default_usb_input_current, + battery->pdata->default_usb_charging_current); + sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_TA, + battery->pdata->default_input_current, + battery->pdata->default_charging_current); + /* usb default current is 100mA before configured*/ + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_100MA, + (SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE | + SEC_BAT_CURRENT_EVENT_AFC | + SEC_BAT_CURRENT_EVENT_USB_SUPER | + SEC_BAT_CURRENT_EVENT_USB_100MA | + SEC_BAT_CURRENT_EVENT_VBAT_OVP | + SEC_BAT_CURRENT_EVENT_VSYS_OVP | + SEC_BAT_CURRENT_EVENT_CHG_LIMIT | + SEC_BAT_CURRENT_EVENT_AICL)); +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) + cancel_delayed_work(&battery->slowcharging_work); +#endif + battery->wc_cv_mode = false; + battery->is_sysovlo = false; + battery->is_vbatovlo = false; + + if(is_slate_mode(battery)) { + sec_bat_set_charge(battery, SEC_BATTERY_BUCKOFF); + } else { + if (sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE)) + goto end_of_cable_work; + } + } else { +#if defined(CONFIG_EN_OOPS) + val.intval = battery->cable_type; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, val); +#endif + if (battery->status == POWER_SUPPLY_STATUS_FULL) + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_FULL); + else + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_CHARGING); + + battery->health = POWER_SUPPLY_HEALTH_GOOD; + + if (sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST)) + goto end_of_cable_work; + +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) + if (battery->cable_type == SEC_BATTERY_CABLE_USB && !lpcharge) + queue_delayed_work(battery->monitor_wqueue, &battery->slowcharging_work, + msecs_to_jiffies(3000)); +#endif + + } +skip_update_battery_status: + if (battery->cable_type == SEC_BATTERY_CABLE_TA || + battery->cable_type == SEC_BATTERY_CABLE_WIRELESS || + battery->cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_AFC, SEC_BAT_CURRENT_EVENT_AFC); + ttf_work_start(battery); + } else { + cancel_delayed_work(&battery->afc_work); + wake_unlock(&battery->afc_wake_lock); + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AFC); + } + + /* set online(cable type) */ + val.intval = battery->cable_type; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_ONLINE, val); + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_ONLINE, val); + /* set charging current */ + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CURRENT_AVG, val); + battery->aicl_current = 0; + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AICL); + battery->input_current = val.intval; + /* to init battery type current when wireless charging -> battery case */ + if (is_nocharge_type(battery->cable_type)) + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_MAX, val); + if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) + sec_bat_check_input_voltage(battery); + sec_bat_set_charging_current(battery, 0); + + /* polling time should be reset when cable is changed + * polling_in_sleep should be reset also + * before polling time is re-calculated + * to prevent from counting 1 for events + * right after cable is connected + */ + battery->polling_in_sleep = false; + sec_bat_get_polling_time(battery); + + dev_info(battery->dev, + "%s: Status:%s, Sleep:%s, Charging:%s, Short Poll:%s\n", + __func__, sec_bat_status_str[battery->status], + battery->polling_in_sleep ? "Yes" : "No", + (battery->charging_mode <= + SEC_BATTERY_CHARGING_NONE) ? "No" : "Yes", + battery->polling_short ? "Yes" : "No"); + dev_info(battery->dev, + "%s: Polling time is reset to %d sec.\n", __func__, + battery->polling_time); + + battery->polling_count = 1; /* initial value = 1 */ + + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); +end_of_cable_work: + wake_unlock(&battery->cable_wake_lock); + dev_info(battery->dev, "%s: End\n", __func__); +} + +static void sec_bat_afc_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, afc_work.work); + union power_supply_propval value = {0, }; + + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + battery->current_max = value.intval; + + if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) { + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AFC); + if ((battery->wc_status != SEC_WIRELESS_PAD_NONE && + battery->current_max >= battery->pdata->pre_wc_afc_input_current) || + ((battery->cable_type == SEC_BATTERY_CABLE_TA) && + battery->current_max >= battery->pdata->pre_afc_input_current)) { + sec_bat_set_charging_current(battery, 0); + } + } + wake_unlock(&battery->afc_wake_lock); +} + +ssize_t sec_bat_show_attrs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + const ptrdiff_t offset = attr - sec_battery_attrs; + union power_supply_propval value = {0, }; + int i = 0; + int ret = 0; + + switch (offset) { + case BATT_RESET_SOC: + break; + case BATT_READ_RAW_SOC: + { + value.intval = + SEC_FUELGAUGE_CAPACITY_TYPE_RAW; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } + break; + case BATT_READ_ADJ_SOC: + break; + case BATT_TYPE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", + battery->batt_type); + break; + case BATT_VFOCV: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->voltage_ocv); + break; + case BATT_VOL_ADC: + break; + case BATT_VOL_ADC_CAL: + break; + case BATT_VOL_AVER: + break; + case BATT_VOL_ADC_AVER: + break; + + case BATT_CURRENT_UA_NOW: + { + value.intval = SEC_BATTERY_CURRENT_UA; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CURRENT_NOW, value); +#if defined(CONFIG_SEC_FACTORY) + pr_err("%s: batt_current_ua_now (%d)\n", + __func__, value.intval); +#endif + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } + break; + case BATT_CURRENT_UA_AVG: + { + value.intval = SEC_BATTERY_CURRENT_UA; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CURRENT_AVG, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } + break; + + case BATT_FILTER_CFG: + { + psy_do_property(battery->pdata->fuelgauge_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_FILTER_CFG, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n", + value.intval); + } + break; + case BATT_TEMP: + switch (battery->pdata->thermal_source) { + case SEC_BATTERY_THERMAL_SOURCE_FG: + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_TEMP, value); + break; + case SEC_BATTERY_THERMAL_SOURCE_CALLBACK: + if (battery->pdata->get_temperature_callback) { + battery->pdata->get_temperature_callback( + POWER_SUPPLY_PROP_TEMP, &value); + } + break; + case SEC_BATTERY_THERMAL_SOURCE_ADC: + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_TEMP, &value); + break; + default: + break; + } + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + break; + case BATT_TEMP_ADC: + /* + If F/G is used for reading the temperature and + compensation table is used, + the raw value that isn't compensated can be read by + POWER_SUPPLY_PROP_TEMP_AMBIENT + */ + switch (battery->pdata->thermal_source) { + case SEC_BATTERY_THERMAL_SOURCE_FG: + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_TEMP_AMBIENT, value); + battery->temp_adc = value.intval; + break; + default: + break; + } + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->temp_adc); + break; + case BATT_TEMP_AVER: + break; + case BATT_TEMP_ADC_AVER: + break; + case USB_TEMP: + if (battery->pdata->usb_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_USB_TEMP, &value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case USB_TEMP_ADC: + if (battery->pdata->usb_thermal_source) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->usb_temp_adc); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_CHG_TEMP: + if (battery->pdata->chg_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_CHG_TEMP, &value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_CHG_TEMP_ADC: + if (battery->pdata->chg_thermal_source) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->chg_temp_adc); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case SLAVE_CHG_TEMP: + if (battery->pdata->slave_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP, &value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case SLAVE_CHG_TEMP_ADC: + if (battery->pdata->slave_thermal_source) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->slave_chg_temp_adc); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BLKT_TEMP: + if (battery->pdata->blkt_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_BLKT_TEMP, &value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BLKT_TEMP_ADC: + if (battery->pdata->blkt_thermal_source) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->blkt_temp_adc); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_VF_ADC: + if(battery->pdata->battery_check_type == SEC_BATTERY_CHECK_ADC) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->check_adc_value); + } + break; + case BATT_SLATE_MODE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + is_slate_mode(battery)); + break; + + case BATT_LP_CHARGING: + if (lpcharge) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + lpcharge ? 1 : 0); + } + break; + case SIOP_ACTIVATED: + break; + case SIOP_LEVEL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->siop_level); + break; + case SIOP_EVENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->siop_event); + break; + case BATT_CHARGING_SOURCE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->cable_type); + break; + case FG_REG_DUMP: + break; + case FG_RESET_CAP: + break; + case FG_CAPACITY: + { + value.intval = + SEC_BATTERY_CAPACITY_DESIGNED; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x ", + value.intval); + + value.intval = + SEC_BATTERY_CAPACITY_ABSOLUTE; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x ", + value.intval); + + value.intval = + SEC_BATTERY_CAPACITY_TEMPERARY; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x ", + value.intval); + + value.intval = + SEC_BATTERY_CAPACITY_CURRENT; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x\n", + value.intval); + } + break; + case FG_ASOC: + value.intval = -1; + { + struct power_supply *psy_fg = NULL; + psy_fg = get_power_supply_by_name(battery->pdata->fuelgauge_name); + if (!psy_fg) { + pr_err("%s: Fail to get psy (%s)\n", + __func__, battery->pdata->fuelgauge_name); + } else { + if (psy_fg->desc->get_property != NULL) { + ret = psy_fg->desc->get_property(psy_fg, + POWER_SUPPLY_PROP_ENERGY_FULL, &value); + if (ret < 0) { + pr_err("%s: Fail to %s get (%d=>%d)\n", + __func__, battery->pdata->fuelgauge_name, + POWER_SUPPLY_PROP_ENERGY_FULL, ret); + } + } + } + } + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + break; + case AUTH: + break; + case CHG_CURRENT_ADC: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->current_adc); + break; + case WC_ADC: + break; + case WC_STATUS: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + is_wireless_type(battery->cable_type) ? 1: 0); + break; + case WC_ENABLE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->wc_enable); + break; + case WC_CONTROL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->wc_enable); + break; + case WC_CONTROL_CNT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->wc_enable_cnt_value); + break; + case LED_COVER: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->led_cover); + break; + case HV_CHARGER_STATUS: + { + int check_val = 0; + if (is_hv_wire_12v_type(battery->cable_type) || + battery->max_charge_power >= HV_CHARGER_STATUS_STANDARD2) /* 20000mW */ + check_val = 2; + else if (is_hv_wire_type(battery->cable_type) || + battery->cable_type == SEC_BATTERY_CABLE_PREPARE_TA || + battery->max_charge_power >= HV_CHARGER_STATUS_STANDARD1) /* 12000mW */ + check_val = 1; + + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", check_val); + } + break; + case HV_WC_CHARGER_STATUS: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + is_hv_wireless_type(battery->cable_type) ? 1 : 0); + break; + case HV_CHARGER_SET: + break; + case FACTORY_MODE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->factory_mode); + break; + case STORE_MODE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->store_mode); + break; + case UPDATE: + break; + case TEST_MODE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->test_mode); + break; + + case BATT_EVENT_CALL: + case BATT_EVENT_2G_CALL: + case BATT_EVENT_TALK_GSM: + break; + case BATT_EVENT_3G_CALL: + case BATT_EVENT_TALK_WCDMA: + break; + case BATT_EVENT_MUSIC: + break; + case BATT_EVENT_VIDEO: + break; + case BATT_EVENT_BROWSER: + break; + case BATT_EVENT_HOTSPOT: + break; + case BATT_EVENT_CAMERA: + break; + case BATT_EVENT_CAMCORDER: + break; + case BATT_EVENT_DATA_CALL: + break; + case BATT_EVENT_WIFI: + break; + case BATT_EVENT_WIBRO: + break; + case BATT_EVENT_LTE: + break; + case BATT_EVENT_LCD: + break; + case BATT_EVENT_GPS: + break; + case BATT_EVENT: + break; + case BATT_TEMP_TABLE: + i += scnprintf(buf + i, PAGE_SIZE - i, + "%d %d %d %d %d %d %d %d\n", + battery->pdata->temp_high_threshold_normal, + battery->pdata->temp_high_recovery_normal, + battery->pdata->temp_low_threshold_normal, + battery->pdata->temp_low_recovery_normal, + battery->pdata->temp_high_threshold_lpm, + battery->pdata->temp_high_recovery_lpm, + battery->pdata->temp_low_threshold_lpm, + battery->pdata->temp_low_recovery_lpm); + break; + case BATT_HIGH_CURRENT_USB: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->is_hc_usb); + break; +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + case TEST_CHARGE_CURRENT: + { + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CURRENT_NOW, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } + break; +#endif + case SET_STABILITY_TEST: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->stability_test); + break; + case BATT_CAPACITY_MAX: + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case BATT_INBAT_VOLTAGE: + case BATT_INBAT_VOLTAGE_OCV: + if(battery->pdata->support_fgsrc_change == true) { + int j, k, ocv, ocv_data[10]; + value.intval = 0; + psy_do_property(battery->pdata->fgsrc_switch_name, set, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + for (j = 0; j < 10; j++) { + msleep(175); + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_NOW, value); + ocv_data[j] = value.intval; + } + value.intval = 1; + psy_do_property(battery->pdata->fgsrc_switch_name, set, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + for (j = 1; j < 10; j++) { + ocv = ocv_data[j]; + k = j; + while (k > 0 && ocv_data[k-1] > ocv) { + ocv_data[k] = ocv_data[k-1]; + k--; + } + ocv_data[k] = ocv; + } + ocv = 0; + for (j = 2; j < 8; j++) { + ocv += ocv_data[j]; + } + ret = ocv / 6; + } else { +#if defined(CONFIG_FUELGAUGE_SM5705) || defined(CONFIG_INBATVOLT_FG) + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + ret = value.intval; +#else + ret = sec_bat_get_adc_value(battery, SEC_BAT_ADC_CHANNEL_INBAT_VOLTAGE); +#endif + } + dev_info(battery->dev, "in-battery voltage ocv(%d)\n", ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case CHECK_SLAVE_CHG: + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHECK_SLAVE_I2C, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + pr_info("%s : CHECK_SLAVE_CHG=%d\n",__func__,value.intval); + break; + case BATT_INBAT_WIRELESS_CS100: + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_STATUS, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case HMT_TA_CONNECTED: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + (battery->cable_type == SEC_BATTERY_CABLE_HMT_CONNECTED) ? 1 : 0); + break; + case HMT_TA_CHARGE: +#if defined(CONFIG_CCIC_NOTIFIER) + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + (battery->current_event & SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE) ? 0 : 1); +#else + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + (battery->cable_type == SEC_BATTERY_CABLE_HMT_CHARGE) ? 1 : 0); +#endif + break; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + case FG_CYCLE: + value.intval = SEC_BATTERY_CAPACITY_CYCLE; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + value.intval = value.intval / 100; + dev_info(battery->dev, "fg cycle(%d)\n", value.intval); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case FG_FULL_VOLTAGE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d %d\n", + battery->pdata->chg_float_voltage, battery->pdata->recharge_condition_vcell); + break; + case FG_FULLCAPNOM: + value.intval = + SEC_BATTERY_CAPACITY_AGEDCELL; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; +#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE) + case BATT_AFTER_MANUFACTURED: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", battery->batt_cycle); + break; +#else + case BATTERY_CYCLE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", battery->batt_cycle); + break; +#endif +#endif + case BATT_WPC_TEMP: + if (battery->pdata->wpc_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_WPC_TEMP, &value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_WPC_TEMP_ADC: + if (battery->pdata->wpc_thermal_source) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->wpc_temp_adc); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_COIL_TEMP: + if (battery->pdata->coil_thermal_source) { + sec_bat_get_value_by_adc(battery, + SEC_BAT_ADC_CHANNEL_WPC_TEMP, &value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_COIL_TEMP_ADC: + if (battery->pdata->coil_thermal_source) { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->coil_temp_adc); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + 0); + } + break; + case BATT_WIRELESS_MST_SWITCH_TEST: + value.intval = SEC_WIRELESS_MST_SWITCH_VERIFY; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + pr_info("%s MST switch verify. result: %x\n", __func__, value.intval); + i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n", value.intval); + break; +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + case BATT_WIRELESS_FIRMWARE_UPDATE: + value.intval = SEC_WIRELESS_OTP_FIRM_VERIFY; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + pr_info("%s RX firmware verify. result: %d\n", __func__, value.intval); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case OTP_FIRMWARE_RESULT: + value.intval = SEC_WIRELESS_OTP_FIRM_RESULT; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case WC_IC_GRADE: + value.intval = SEC_WIRELESS_IC_GRADE; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%x ", value.intval); + + value.intval = SEC_WIRELESS_IC_REVISION; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%x\n", value.intval); + break; + case OTP_FIRMWARE_VER_BIN: + value.intval = SEC_WIRELESS_OTP_FIRM_VER_BIN; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n", value.intval); + break; + case OTP_FIRMWARE_VER: + value.intval = SEC_WIRELESS_OTP_FIRM_VER; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n", value.intval); + break; + case TX_FIRMWARE_RESULT: + value.intval = SEC_WIRELESS_TX_FIRM_RESULT; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case TX_FIRMWARE_VER: + value.intval = SEC_WIRELESS_TX_FIRM_VER; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n", value.intval); + break; + case BATT_TX_STATUS: + value.intval = SEC_TX_FIRMWARE; + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_MANUFACTURER, value); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n", value.intval); + break; +#endif + case WC_VOUT: + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case WC_VRECT: + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_ENERGY_AVG, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; +#if defined(CONFIG_WIRELESS_CHARGER_HIGH_VOLTAGE) + case BATT_HV_WIRELESS_STATUS: + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case BATT_HV_WIRELESS_PAD_CTRL: + break; +#endif + case WC_OP_FREQ: + psy_do_property(battery->pdata->wireless_charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_WIRELESS_OP_FREQ, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", value.intval); + break; + case WC_CMD_INFO: + psy_do_property(battery->pdata->wireless_charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_WIRELESS_TX_CMD, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%02x ", + value.intval); + + psy_do_property(battery->pdata->wireless_charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VAL, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%02x ", + value.intval); + break; + case BATT_TUNE_FLOAT_VOLTAGE: + ret = battery->pdata->chg_float_voltage; + pr_info("%s float voltage = %d mA",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_INPUT_CHARGE_CURRENT: + ret = battery->pdata->charging_current[i].input_current_limit; + pr_info("%s input charge current = %d mA",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_FAST_CHARGE_CURRENT: + ret = battery->pdata->charging_current[i].fast_charging_current; + pr_info("%s fast charge current = %d mA",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_UI_TERM_CURRENT_1ST: + ret = battery->pdata->full_check_current_1st; + pr_info("%s ui term current = %d mA",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_UI_TERM_CURRENT_2ND: + ret = battery->pdata->full_check_current_2nd; + pr_info("%s ui term current = %d mA",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_TEMP_HIGH_NORMAL: + ret = battery->pdata->temp_high_threshold_normal; + pr_info("%s temp high normal block = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_TEMP_HIGH_REC_NORMAL: + ret = battery->pdata->temp_high_recovery_normal; + pr_info("%s temp high normal recover = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_TEMP_LOW_NORMAL: + ret = battery->pdata->temp_low_threshold_normal; + pr_info("%s temp low normal block = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_TEMP_LOW_REC_NORMAL: + ret = battery->pdata->temp_low_recovery_normal; + pr_info("%s temp low normal recover = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_CHG_TEMP_HIGH: + ret = battery->pdata->chg_high_temp; + pr_info("%s chg_high_temp = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_CHG_TEMP_REC: + ret = battery->pdata->chg_high_temp_recovery; + pr_info("%s chg_high_temp_recovery = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_CHG_LIMMIT_CUR: + ret = battery->pdata->chg_charging_limit_current; + pr_info("%s chg_charging_limit_current = %d ",__func__, ret); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + ret); + break; + case BATT_TUNE_COIL_TEMP_HIGH: + break; + case BATT_TUNE_COIL_TEMP_REC: + break; + case BATT_TUNE_COIL_LIMMIT_CUR: + break; +#if defined(CONFIG_UPDATE_BATTERY_DATA) + case BATT_UPDATE_DATA: + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", + battery->data_path ? "OK" : "NOK"); + break; +#endif + case BATT_MISC_EVENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->misc_event); + break; + case BATT_EXT_DEV_CHG: + break; + case BATT_WDT_CONTROL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->wdt_kick_disable); + break; + case MODE: + value.strval = NULL; + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_MULTI_CHARGER_MODE, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", + (value.strval) ? value.strval : "master"); + break; + case CHECK_PS_READY: +#if defined(CONFIG_CCIC_NOTIFIER) + value.intval = battery->pdic_ps_rdy; + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + pr_info("%s : CHECK_PS_READY=%d\n",__func__,value.intval); +#endif + break; + case BATT_CHIP_ID: + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHIP_ID, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + break; + case CISD_FULLCAPREP_MAX: + { + union power_supply_propval fullcaprep_val; + + fullcaprep_val.intval = SEC_BATTERY_CAPACITY_FULL; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, fullcaprep_val); + + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + fullcaprep_val.intval); + } + break; +#if defined(CONFIG_BATTERY_CISD) + case CISD_DATA: + { + struct cisd *pcisd = &battery->cisd; + char temp_buf[1024] = {0,}; + int j = 0; + + sprintf(temp_buf+strlen(temp_buf), "%d", pcisd->data[CISD_DATA_RESET_ALG]); + for (j = CISD_DATA_RESET_ALG + 1; j < CISD_DATA_MAX_PER_DAY; j++) + sprintf(temp_buf+strlen(temp_buf), " %d", pcisd->data[j]); + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + } + break; + case CISD_DATA_JSON: + { + struct cisd *pcisd = &battery->cisd; + char temp_buf[1024] = {0,}; + int j = 0; + + sprintf(temp_buf+strlen(temp_buf), "\"%s\":\"%d\"", + cisd_data_str[CISD_DATA_RESET_ALG], pcisd->data[CISD_DATA_RESET_ALG]); + for (j = CISD_DATA_RESET_ALG + 1; j < CISD_DATA_MAX; j++) { + if (battery->pdata->ignore_cisd_index[j / 32] & (0x1 << (j % 32))) + continue; + sprintf(temp_buf+strlen(temp_buf), ",\"%s\":\"%d\"", cisd_data_str[j], pcisd->data[j]); + } + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + } + break; + case CISD_DATA_D_JSON: + { + struct cisd *pcisd = &battery->cisd; + char temp_buf[1024] = {0,}; + int j = 0; + + sprintf(temp_buf+strlen(temp_buf), "\"%s\":\"%d\"", + cisd_data_str_d[CISD_DATA_FULL_COUNT_PER_DAY-CISD_DATA_MAX], + pcisd->data[CISD_DATA_FULL_COUNT_PER_DAY]); + for (j = CISD_DATA_FULL_COUNT_PER_DAY + 1; j < CISD_DATA_MAX_PER_DAY; j++) { + if (battery->pdata->ignore_cisd_index_d[(j - CISD_DATA_FULL_COUNT_PER_DAY) / 32] & (0x1 << ((j - CISD_DATA_FULL_COUNT_PER_DAY) % 32))) + continue; + sprintf(temp_buf+strlen(temp_buf), ",\"%s\":\"%d\"", + cisd_data_str_d[j-CISD_DATA_MAX], pcisd->data[j]); + } + + /* Clear Daily Data */ + for (j = CISD_DATA_FULL_COUNT_PER_DAY; j < CISD_DATA_MAX_PER_DAY; j++) + pcisd->data[j] = 0; + + pcisd->data[CISD_DATA_FULL_COUNT_PER_DAY] = 1; + pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY] = 1000; + + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY] = 1000; + + pcisd->data[CISD_DATA_CAP_MIN_PER_DAY] = 0xFFFF; + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + } + break; + case CISD_WIRE_COUNT: + { + struct cisd *pcisd = &battery->cisd; + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + pcisd->data[CISD_DATA_WIRE_COUNT]); + } + break; + case CISD_WC_DATA: + { + struct cisd *pcisd = &battery->cisd; + struct pad_data *pad_data = pcisd->pad_array; + char temp_buf[1024] = {0,}; + int j = 0; + + sprintf(temp_buf+strlen(temp_buf), "%d %d", + PAD_INDEX_VALUE, pcisd->pad_count); + while (((pad_data = pad_data->next) != NULL) && + (pad_data->id < MAX_PAD_ID) && + (j++ < pcisd->pad_count)) + sprintf(temp_buf+strlen(temp_buf), " 0x%02x:%d", pad_data->id, pad_data->count); + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + } + break; + case CISD_WC_DATA_JSON: + { + struct cisd *pcisd = &battery->cisd; + struct pad_data *pad_data = pcisd->pad_array; + char temp_buf[1024] = {0,}; + int j = 0; + + sprintf(temp_buf+strlen(temp_buf), "\"%s\":\"%d\"", + PAD_INDEX_STRING, PAD_INDEX_VALUE); + while (((pad_data = pad_data->next) != NULL) && + (pad_data->id < MAX_PAD_ID) && + (j++ < pcisd->pad_count)) + sprintf(temp_buf+strlen(temp_buf), ",\"%s%02x\":\"%d\"", + PAD_JSON_STRING, pad_data->id, pad_data->count); + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + } + break; + case PREV_BATTERY_DATA: + { + if (battery->enable_update_data) + i += scnprintf(buf + i, PAGE_SIZE - i, "%d, %d, %d, %d\n", + battery->voltage_now, battery->temperature, + battery->is_jig_on, + (battery->charging_mode > SEC_BATTERY_CHARGING_NONE)?1:0); + } + break; + case PREV_BATTERY_INFO: + { + i += scnprintf(buf + i, PAGE_SIZE - i, "%d,%d,%d,%d\n", + battery->prev_volt, battery->prev_temp, + battery->prev_jig_on, battery->prev_chg_on); + pr_info("%s: Read Prev Battery Info : %d, %d, %d, %d\n", __func__, + battery->prev_volt, battery->prev_temp, + battery->prev_jig_on, battery->prev_chg_on); + } + break; +#endif + case SAFETY_TIMER_SET: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->safety_timer_set); + break; + case BATT_SWELLING_CONTROL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->skip_swelling); + break; + case SAFETY_TIMER_INFO: + i += scnprintf(buf + i, PAGE_SIZE - i, "%ld\n", + battery->cal_safety_time); + break; +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + case BATT_TEMP_TEST: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d %d %d %d %d\n", + battery->temperature_test_battery, + battery->temperature_test_usb, + battery->temperature_test_wpc, + battery->temperature_test_chg, + battery->temperature_test_blkt); + break; +#endif + case BATT_CURRENT_EVENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + battery->current_event); + break; + case CHARGE_OTG_CONTROL: + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + break; + case CHARGE_UNO_CONTROL: + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + value.intval); + break; + default: + i = -EINVAL; + break; + } + + return i; +} + +void update_external_temp_table(struct sec_battery_info *battery, int temp[]) +{ + battery->pdata->temp_high_threshold_normal = temp[0]; + battery->pdata->temp_high_recovery_normal = temp[1]; + battery->pdata->temp_low_threshold_normal = temp[2]; + battery->pdata->temp_low_recovery_normal = temp[3]; + battery->pdata->temp_high_threshold_lpm = temp[4]; + battery->pdata->temp_high_recovery_lpm = temp[5]; + battery->pdata->temp_low_threshold_lpm = temp[6]; + battery->pdata->temp_low_recovery_lpm = temp[7]; +} + +ssize_t sec_bat_store_attrs( + struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + const ptrdiff_t offset = attr - sec_battery_attrs; + int ret = -EINVAL; + int x = 0; + int t[12]; + int i = 0; + union power_supply_propval value = {0, }; + + switch (offset) { + case BATT_RESET_SOC: + /* Do NOT reset fuel gauge in charging mode */ + if (is_nocharge_type(battery->cable_type) || + battery->is_jig_on) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_BATT_RESET_SOC, 0); + + value.intval = + SEC_FUELGAUGE_CAPACITY_TYPE_RESET; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_CAPACITY, value); + dev_info(battery->dev,"do reset SOC\n"); + /* update battery info */ + sec_bat_get_battery_info(battery); + } + ret = count; + break; + case BATT_READ_RAW_SOC: + break; + case BATT_READ_ADJ_SOC: + break; + case BATT_TYPE: + strncpy(battery->batt_type, buf, sizeof(battery->batt_type) - 1); + battery->batt_type[sizeof(battery->batt_type)-1] = '\0'; + ret = count; + break; + case BATT_VFOCV: + break; + case BATT_VOL_ADC: + break; + case BATT_VOL_ADC_CAL: + break; + case BATT_VOL_AVER: + break; + case BATT_VOL_ADC_AVER: + break; + case BATT_CURRENT_UA_NOW: + break; + case BATT_CURRENT_UA_AVG: + break; + case BATT_FILTER_CFG: + if (sscanf(buf, "%10d\n", &x) == 1) { + value.intval = x; + psy_do_property(battery->pdata->fuelgauge_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_FILTER_CFG, value); + ret = count; + } + break; + case BATT_TEMP: +#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY) + if (sscanf(buf, "%10d\n", &x) == 1) { + dev_info(battery->dev, + "%s: cooldown mode %s \n", __func__, (x ? "enable" : "disable")); + if (x == 0) + battery->cooldown_mode = false; + else + battery->cooldown_mode = true; + ret = count; + } +#endif + break; + case BATT_TEMP_ADC: + break; + case BATT_TEMP_AVER: + break; + case BATT_TEMP_ADC_AVER: + break; + case USB_TEMP: + break; + case USB_TEMP_ADC: + break; + case BATT_CHG_TEMP: + break; + case BATT_CHG_TEMP_ADC: + break; + case SLAVE_CHG_TEMP: + break; + case SLAVE_CHG_TEMP_ADC: + break; + case BLKT_TEMP: + break; + case BLKT_TEMP_ADC: + break; + case BATT_VF_ADC: + break; + case BATT_SLATE_MODE: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == is_slate_mode(battery)) { + dev_info(battery->dev, + "%s : skip same slate mode : %d\n", __func__, x); + return count; + } else if (x == 1) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SLATE, SEC_BAT_CURRENT_EVENT_SLATE); + dev_info(battery->dev, + "%s: enable slate mode : %d\n", __func__, x); + } else if (x == 0) { + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SLATE); + dev_info(battery->dev, + "%s: disable slate mode : %d\n", __func__, x); + } else { + dev_info(battery->dev, + "%s: SLATE MODE unknown command\n", __func__); + return -EINVAL; + } + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + ret = count; + } + break; + case BATT_LP_CHARGING: + break; + case SIOP_ACTIVATED: + break; + case SIOP_LEVEL: + if (sscanf(buf, "%10d\n", &x) == 1) { + dev_info(battery->dev, + "%s: siop level: %d\n", __func__, x); + + battery->wc_heating_start_time = 0; + if (x == battery->siop_level) { + dev_info(battery->dev, + "%s: skip same siop level: %d\n", __func__, x); + return count; + } else if (x >= 0 && x <= 100) { + battery->siop_level = x; + } else { + battery->siop_level = 100; + } + + if (battery->siop_event == SIOP_EVENT_WPC_CALL_START || + battery->siop_event == SIOP_EVENT_WPC_CALL_END) + return count; + + if (delayed_work_pending(&battery->siop_event_work)) + return count; + + cancel_delayed_work(&battery->siop_work); + wake_lock(&battery->siop_level_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->siop_level_work, 0); + + ret = count; + } + break; + case SIOP_EVENT: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (battery->pdata->siop_event_check_type & SIOP_EVENT_WPC_CALL) { // To reduce call noise with battery pack + if (x == SIOP_EVENT_WPC_CALL_START) { + battery->siop_event |= SIOP_EVENT_WPC_CALL; + pr_info("%s : WPC Enable & SIOP EVENT CALL START. 0x%x\n", + __func__, battery->siop_event); + cancel_delayed_work(&battery->siop_level_work); + cancel_delayed_work(&battery->siop_work); + wake_lock(&battery->siop_event_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->siop_event_work, 0); + } else if (x == SIOP_EVENT_WPC_CALL_END) { + battery->siop_event &= ~SIOP_EVENT_WPC_CALL; + pr_info("%s : WPC Enable & SIOP EVENT CALL END. 0x%x\n", + __func__, battery->siop_event); + cancel_delayed_work(&battery->siop_level_work); + cancel_delayed_work(&battery->siop_work); + wake_lock(&battery->siop_event_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->siop_event_work, + msecs_to_jiffies(5000)); + } else { + battery->siop_event &= ~SIOP_EVENT_WPC_CALL; + pr_info("%s : WPC Disable & SIOP EVENT 0x%x\n", __func__, battery->siop_event); + } + } + ret = count; + } + break; + case BATT_CHARGING_SOURCE: + break; + case FG_REG_DUMP: + break; + case FG_RESET_CAP: + break; + case FG_CAPACITY: + break; + case FG_ASOC: + break; + case AUTH: + break; + case CHG_CURRENT_ADC: + break; + case WC_ADC: + break; + case WC_STATUS: + break; + case WC_ENABLE: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == 0) { + mutex_lock(&battery->wclock); + battery->wc_enable = false; + battery->wc_enable_cnt = 0; + mutex_unlock(&battery->wclock); + } else if (x == 1) { + mutex_lock(&battery->wclock); + battery->wc_enable = true; + battery->wc_enable_cnt = 0; + mutex_unlock(&battery->wclock); + } else { + dev_info(battery->dev, + "%s: WPC ENABLE unknown command\n", + __func__); + return -EINVAL; + } + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + ret = count; + } + break; + case WC_CONTROL: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (battery->pdata->wpc_en) { + if (x == 0) { + mutex_lock(&battery->wclock); + battery->wc_enable = false; + battery->wc_enable_cnt = 0; + value.intval = 0; + psy_do_property(battery->pdata->wireless_charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_WC_CONTROL, value); + gpio_direction_output(battery->pdata->wpc_en, 1); + pr_info("%s: WC CONTROL: Disable", __func__); + mutex_unlock(&battery->wclock); + } else if (x == 1) { + mutex_lock(&battery->wclock); + battery->wc_enable = true; + battery->wc_enable_cnt = 0; + value.intval = 1; + psy_do_property(battery->pdata->wireless_charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_WC_CONTROL, value); + gpio_direction_output(battery->pdata->wpc_en, 0); + pr_info("%s: WC CONTROL: Enable", __func__); + mutex_unlock(&battery->wclock); + } else { + dev_info(battery->dev, + "%s: WC CONTROL unknown command\n", + __func__); + return -EINVAL; + } + } + ret = count; + } + break; + case WC_CONTROL_CNT: + if (sscanf(buf, "%10d\n", &x) == 1) { + battery->wc_enable_cnt_value = x; + ret = count; + } + break; + case LED_COVER: + if (sscanf(buf, "%10d\n", &x) == 1) { + pr_info("%s: MFC, LED_COVER(%d)\n", __func__, x); + battery->led_cover = x; + value.intval = battery->led_cover; + psy_do_property(battery->pdata->wireless_charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_FILTER_CFG, value); + ret = count; + } + break; + case HV_CHARGER_STATUS: + break; + case HV_WC_CHARGER_STATUS: + break; + case HV_CHARGER_SET: + if (sscanf(buf, "%10d\n", &x) == 1) { + dev_info(battery->dev, + "%s: HV_CHARGER_SET(%d)\n", __func__, x); + if (x == 1) { + battery->wire_status = SEC_BATTERY_CABLE_9V_TA; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } else { + battery->wire_status = SEC_BATTERY_CABLE_NONE; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } + ret = count; + } + break; + case FACTORY_MODE: + if (sscanf(buf, "%10d\n", &x) == 1) { + battery->factory_mode = x ? true : false; + ret = count; + } + break; + case STORE_MODE: + if (sscanf(buf, "%10d\n", &x) == 1) { +#if !defined(CONFIG_SEC_FACTORY) + if (x) { + battery->store_mode = true; + wake_lock(&battery->parse_mode_dt_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->parse_mode_dt_work, 0); + } +#endif + ret = count; + } + break; + case UPDATE: + if (sscanf(buf, "%10d\n", &x) == 1) { + /* update battery info */ + sec_bat_get_battery_info(battery); + ret = count; + } + break; + case TEST_MODE: + if (sscanf(buf, "%10d\n", &x) == 1) { + battery->test_mode = x; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, 0); + ret = count; + } + break; + + case BATT_EVENT_CALL: + case BATT_EVENT_2G_CALL: + case BATT_EVENT_TALK_GSM: + case BATT_EVENT_3G_CALL: + case BATT_EVENT_TALK_WCDMA: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_MUSIC: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_VIDEO: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_BROWSER: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_HOTSPOT: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_CAMERA: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_CAMCORDER: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_DATA_CALL: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_WIFI: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_WIBRO: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_LTE: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_EVENT_LCD: + if (sscanf(buf, "%10d\n", &x) == 1) { + struct timespec ts; + get_monotonic_boottime(&ts); + if (x) { + battery->lcd_status = true; + } else { + battery->lcd_status = false; + } + ret = count; + } + break; + case BATT_EVENT_GPS: + if (sscanf(buf, "%10d\n", &x) == 1) { + ret = count; + } + break; + case BATT_TEMP_TABLE: + if (sscanf(buf, "%10d %10d %10d %10d %10d %10d %10d %10d\n", + &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6], &t[7]) == 8) { + pr_info("%s: (new) %d %d %d %d %d %d %d %d\n", + __func__, t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7]); + pr_info("%s: (default) %d %d %d %d %d %d %d %d\n", + __func__, + battery->pdata->temp_high_threshold_normal, + battery->pdata->temp_high_recovery_normal, + battery->pdata->temp_low_threshold_normal, + battery->pdata->temp_low_recovery_normal, + battery->pdata->temp_high_threshold_lpm, + battery->pdata->temp_high_recovery_lpm, + battery->pdata->temp_low_threshold_lpm, + battery->pdata->temp_low_recovery_lpm); + update_external_temp_table(battery, t); + ret = count; + } + break; + case BATT_HIGH_CURRENT_USB: + if (sscanf(buf, "%10d\n", &x) == 1) { + battery->is_hc_usb = x ? true : false; + pr_info("%s: is_hc_usb (%d)\n", __func__, battery->is_hc_usb); + ret = count; + } + break; +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + case TEST_CHARGE_CURRENT: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x >= 0 && x <= 2000) { + dev_err(battery->dev, + "%s: BATT_TEST_CHARGE_CURRENT(%d)\n", __func__, x); + battery->pdata->charging_current[ + SEC_BATTERY_CABLE_USB].input_current_limit = x; + battery->pdata->charging_current[ + SEC_BATTERY_CABLE_USB].fast_charging_current = x; + if (x > 500) { + battery->eng_not_full_status = true; + battery->pdata->temp_check_type = + SEC_BATTERY_TEMP_CHECK_NONE; + } + if (battery->cable_type == SEC_BATTERY_CABLE_USB) { + value.intval = x; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_NOW, + value); + } + } + ret = count; + } + break; +#endif + case SET_STABILITY_TEST: + if (sscanf(buf, "%10d\n", &x) == 1) { + dev_err(battery->dev, + "%s: BATT_STABILITY_TEST(%d)\n", __func__, x); + if (x) { + battery->stability_test = true; + battery->eng_not_full_status = true; + } + else { + battery->stability_test = false; + battery->eng_not_full_status = false; + } + ret = count; + } + break; + case BATT_CAPACITY_MAX: + if (sscanf(buf, "%10d\n", &x) == 1) { + dev_err(battery->dev, + "%s: BATT_CAPACITY_MAX(%d), fg_reset(%d)\n", __func__, x, fg_reset); + if (!fg_reset && !battery->store_mode) { + value.intval = x; + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, value); + + /* update soc */ + value.intval = 0; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + battery->capacity = value.intval; + } else { +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + battery->fg_reset = 1; +#endif + } + ret = count; + } + break; + case BATT_INBAT_VOLTAGE: + break; + case BATT_INBAT_VOLTAGE_OCV: + break; + case CHECK_SLAVE_CHG: + break; + case BATT_INBAT_WIRELESS_CS100: + if (sscanf(buf, "%10d\n", &x) == 1) { + pr_info("%s send cs100 command \n",__func__); + value.intval = POWER_SUPPLY_STATUS_FULL; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_STATUS, value); + ret = count; + } + break; + case HMT_TA_CONNECTED: + if (sscanf(buf, "%10d\n", &x) == 1) { +#if !defined(CONFIG_CCIC_NOTIFIER) + dev_info(battery->dev, + "%s: HMT_TA_CONNECTED(%d)\n", __func__, x); + if (x) { + value.intval = false; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + value); + dev_info(battery->dev, + "%s: changed to OTG cable detached\n", __func__); + + battery->wire_status = SEC_BATTERY_CABLE_HMT_CONNECTED; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } else { + value.intval = true; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + value); + dev_info(battery->dev, + "%s: changed to OTG cable attached\n", __func__); + + battery->wire_status = SEC_BATTERY_CABLE_OTG; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } +#endif + ret = count; + } + break; + case HMT_TA_CHARGE: + if (sscanf(buf, "%10d\n", &x) == 1) { +#if defined(CONFIG_CCIC_NOTIFIER) + dev_info(battery->dev, + "%s: HMT_TA_CHARGE(%d), cable_type(%d), status(%d)\n", + __func__, x, battery->cable_type, battery->status); + + /* do not charge off without cable type, since wdt could be expired */ + if(x) { + /* clear this event every time in case wrong hmt control*/ + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE); + /* No charging when FULL & NONE */ + if(!is_nocharge_type(battery->cable_type) && + (battery->status != POWER_SUPPLY_STATUS_FULL) && + (battery->status != POWER_SUPPLY_STATUS_NOT_CHARGING)) { + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_1ST); + } + } else if(!x && !is_nocharge_type(battery->cable_type)) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE, + SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + } else + dev_info(battery->dev, + "%s: Wrong HMT control \n", __func__); + + ret = count; +#else + dev_info(battery->dev, + "%s: HMT_TA_CHARGE(%d)\n", __func__, x); + psy_do_property(battery->pdata->charger_name, get, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + if (value.intval) { + dev_info(battery->dev, + "%s: ignore HMT_TA_CHARGE(%d)\n", __func__, x); + } else { + if (x) { + value.intval = false; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + value); + dev_info(battery->dev, + "%s: changed to OTG cable detached\n", __func__); + battery->wire_status = SEC_BATTERY_CABLE_HMT_CHARGE; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } else { + value.intval = false; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + value); + dev_info(battery->dev, + "%s: changed to OTG cable detached\n", __func__); + battery->wire_status = SEC_BATTERY_CABLE_HMT_CONNECTED; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } + } + ret = count; +#endif + } + break; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + case FG_CYCLE: + break; + case FG_FULL_VOLTAGE: + break; + case FG_FULLCAPNOM: + break; +#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE) + case BATT_AFTER_MANUFACTURED: +#else + case BATTERY_CYCLE: +#endif + if (sscanf(buf, "%10d\n", &x) == 1) { + dev_info(battery->dev, "%s: %s(%d)\n", __func__, + (offset == BATTERY_CYCLE) ? + "BATTERY_CYCLE" : "BATTERY_CYCLE(W)", x); + if (x >= 0) { + int prev_battery_cycle = battery->batt_cycle; + battery->batt_cycle = x; +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_CYCLE] = x; +#endif + if (prev_battery_cycle < 0) { + sec_bat_aging_check(battery); + } + } + ret = count; + } + break; +#endif + case BATT_WPC_TEMP: + case BATT_WPC_TEMP_ADC: + case BATT_COIL_TEMP: + case BATT_COIL_TEMP_ADC: + break; +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + case BATT_WIRELESS_FIRMWARE_UPDATE: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == SEC_WIRELESS_RX_SDCARD_MODE) { + pr_info("%s fw mode is SDCARD \n", __func__); + sec_bat_fw_update_work(battery, SEC_WIRELESS_RX_SDCARD_MODE); + } else if (x == SEC_WIRELESS_RX_BUILT_IN_MODE) { + pr_info("%s fw mode is BUILD IN \n", __func__); + sec_bat_fw_update_work(battery, SEC_WIRELESS_RX_BUILT_IN_MODE); + } else if (x == SEC_WIRELESS_TX_ON_MODE) { + pr_info("%s tx mode is on \n", __func__); + sec_bat_fw_update_work(battery, SEC_WIRELESS_TX_ON_MODE); + } else if (x == SEC_WIRELESS_TX_OFF_MODE) { + pr_info("%s tx mode is off \n", __func__); + sec_bat_fw_update_work(battery, SEC_WIRELESS_TX_OFF_MODE); + } else { + dev_info(battery->dev, "%s: wireless firmware unknown command\n", __func__); + return -EINVAL; + } + ret = count; + } + break; + case OTP_FIRMWARE_RESULT: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == 2) { + value.intval = x; + pr_info("%s RX firmware update ready!\n", __func__); + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_MANUFACTURER, value); + } else { + dev_info(battery->dev, "%s: firmware unknown command\n", __func__); + return -EINVAL; + } + ret = count; + } + break; + case WC_IC_GRADE: + case OTP_FIRMWARE_VER_BIN: + case OTP_FIRMWARE_VER: + case TX_FIRMWARE_RESULT: + case TX_FIRMWARE_VER: + break; + case BATT_TX_STATUS: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == SEC_TX_OFF) { + pr_info("%s TX mode is off \n", __func__); + sec_bat_fw_update_work(battery, SEC_WIRELESS_TX_OFF_MODE); + } else if (x == SEC_TX_STANDBY) { + pr_info("%s TX mode is on \n", __func__); + sec_bat_fw_update_work(battery, SEC_WIRELESS_TX_ON_MODE); + } else { + dev_info(battery->dev, "%s: TX firmware unknown command\n", __func__); + return -EINVAL; + } + ret = count; + } + break; +#endif + case WC_VOUT: + case WC_VRECT: + break; +#if defined(CONFIG_WIRELESS_CHARGER_HIGH_VOLTAGE) + case BATT_HV_WIRELESS_STATUS: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == 1 && is_hv_wireless_type(battery->cable_type)) { + wake_lock(&battery->cable_wake_lock); +#ifdef CONFIG_SEC_FACTORY + pr_info("%s change cable type HV WIRELESS -> WIRELESS \n", __func__); + battery->wc_status = SEC_WIRELESS_PAD_WPC; + battery->cable_type = SEC_BATTERY_CABLE_WIRELESS; + sec_bat_set_charging_current(battery, 0); +#endif + pr_info("%s HV_WIRELESS_STATUS set to 1. Vout set to 5V. \n", __func__); + value.intval = WIRELESS_VOUT_5V; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + wake_unlock(&battery->cable_wake_lock); + } else if (x == 3 && is_hv_wireless_type(battery->cable_type)) { + pr_info("%s HV_WIRELESS_STATUS set to 3. Vout set to 10V. \n", __func__); + value.intval = WIRELESS_VOUT_10V; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } else { + dev_info(battery->dev, "%s: HV_WIRELESS_STATUS unknown command\n", __func__); + return -EINVAL; + } + ret = count; + } + break; + case BATT_HV_WIRELESS_PAD_CTRL: + if (sscanf(buf, "%10d\n", &x) == 1) { + union power_supply_propval value; + unsigned int param_val; + + pr_err("%s: x : %d\n", __func__, x); + + if (x == 1) { + param_val = '1'; + ret = sec_set_param(param_index_wireless_charging_mode, ¶m_val); + if (ret < 0) { + pr_err("%s:sec_set_param failed\n", __func__); + return ret; + } else { + pr_info("%s: hv wirelee charging is disabled\n", __func__); + sleep_mode = true; + } + } else if (x == 2) { + param_val = '0'; + ret = sec_set_param(param_index_wireless_charging_mode, ¶m_val); + if (ret < 0) { + pr_err("%s: sec_set_param failed\n", __func__); + return ret; + } else { + pr_info("%s: hv wireless charging is enabled\n", __func__); + sleep_mode = false; + } + } else if (x == 3) { + pr_info("%s led off \n", __func__); + value.intval = WIRELESS_PAD_LED_OFF; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } else if (x == 4) { + pr_info("%s led on \n", __func__); + value.intval = WIRELESS_PAD_LED_ON; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + } else { + dev_info(battery->dev, "%s: BATT_HV_WIRELESS_PAD_CTRL unknown command\n", __func__); + return -EINVAL; + } + ret = count; + } + break; +#endif + case BATT_TUNE_FLOAT_VOLTAGE: + sscanf(buf, "%10d\n", &x); + pr_info("%s float voltage = %d mV",__func__, x); + + if(x > 4000 && x <= 4400 ){ + value.intval = x; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, value); + } + break; + case BATT_TUNE_INPUT_CHARGE_CURRENT: + sscanf(buf, "%10d\n", &x); + pr_info("%s input charge current = %d mA",__func__, x); + + if(x >= 0 && x <= 4000 ){ + for(i=0; i < SEC_BATTERY_CABLE_MAX; i++) + battery->pdata->charging_current[i].input_current_limit = x; + + value.intval = x; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + } + break; + case BATT_TUNE_FAST_CHARGE_CURRENT: + sscanf(buf, "%10d\n", &x); + pr_info("%s fast charge current = %d mA",__func__, x); + if(x >= 0 && x <= 4000 ){ + for(i=0; i < SEC_BATTERY_CABLE_MAX; i++) + battery->pdata->charging_current[i].fast_charging_current = x; + + value.intval = x; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_AVG, value); + } + break; + case BATT_TUNE_UI_TERM_CURRENT_1ST: + sscanf(buf, "%10d\n", &x); + pr_info("%s ui term current = %d mA",__func__, x); + + if(x > 0 && x < 1000 ){ + battery->pdata->full_check_current_1st = x; + } + break; + case BATT_TUNE_UI_TERM_CURRENT_2ND: + sscanf(buf, "%10d\n", &x); + pr_info("%s ui term current = %d mA",__func__, x); + + if(x > 0 && x < 1000 ){ + battery->pdata->full_check_current_2nd = x; + } + break; + case BATT_TUNE_TEMP_HIGH_NORMAL: + sscanf(buf, "%10d\n", &x); + pr_info("%s temp high normal block = %d ",__func__, x); + if(x < 1000 && x >= -200) + battery->pdata->temp_high_threshold_normal = x; + break; + case BATT_TUNE_TEMP_HIGH_REC_NORMAL: + sscanf(buf, "%10d\n", &x); + pr_info("%s temp high normal recover = %d ",__func__, x); + if(x < 1000 && x >= -200) + battery->pdata->temp_high_recovery_normal = x; + break; + case BATT_TUNE_TEMP_LOW_NORMAL: + sscanf(buf, "%10d\n", &x); + pr_info("%s temp low normal block = %d ",__func__, x); + if(x < 1000 && x >= -200) + battery->pdata->temp_low_threshold_normal = x; + break; + case BATT_TUNE_TEMP_LOW_REC_NORMAL: + sscanf(buf, "%10d\n", &x); + pr_info("%s temp low normal recover = %d ",__func__, x); + if(x < 1000 && x >= -200) + battery->pdata->temp_low_recovery_normal = x; + break; + case BATT_TUNE_CHG_TEMP_HIGH: + sscanf(buf, "%10d\n", &x); + pr_info("%s chg_high_temp = %d ",__func__, x); + if(x < 1000 && x >= -200) + battery->pdata->chg_high_temp = x; + break; + case BATT_TUNE_CHG_TEMP_REC: + sscanf(buf, "%10d\n", &x); + pr_info("%s chg_high_temp_recovery = %d ",__func__, x); + if(x < 1000 && x >= -200) + battery->pdata->chg_high_temp_recovery = x; + break; + case BATT_TUNE_CHG_LIMMIT_CUR: + sscanf(buf, "%10d\n", &x); + pr_info("%s chg_charging_limit_current = %d ",__func__, x); + if(x < 3000 && x > 0) + { + battery->pdata->chg_charging_limit_current = x; + battery->pdata->charging_current[SEC_BATTERY_CABLE_9V_ERR].input_current_limit = x; + battery->pdata->charging_current[SEC_BATTERY_CABLE_9V_UNKNOWN].input_current_limit = x; + battery->pdata->charging_current[SEC_BATTERY_CABLE_9V_TA].input_current_limit = x; + } + break; + case BATT_TUNE_COIL_TEMP_HIGH: + break; + case BATT_TUNE_COIL_TEMP_REC: + break; + case BATT_TUNE_COIL_LIMMIT_CUR: + sscanf(buf, "%10d\n", &x); + pr_info("%s wpc_charging_limit_current = %d ",__func__, x); + if(x < 3000 && x > 0) + { + battery->pdata->charging_current[SEC_BATTERY_CABLE_9V_ERR].input_current_limit = x; + battery->pdata->charging_current[SEC_BATTERY_CABLE_9V_UNKNOWN].input_current_limit = x; + battery->pdata->charging_current[SEC_BATTERY_CABLE_9V_TA].input_current_limit = x; + } + break; +#if defined(CONFIG_UPDATE_BATTERY_DATA) + case BATT_UPDATE_DATA: + if (!battery->data_path && (count * sizeof(char)) < 256) { + battery->data_path = kzalloc((count * sizeof(char) + 1), GFP_KERNEL); + if (battery->data_path) { + sscanf(buf, "%s\n", battery->data_path); + cancel_delayed_work(&battery->batt_data_work); + wake_lock(&battery->batt_data_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->batt_data_work, msecs_to_jiffies(100)); + } else { + pr_info("%s: failed to alloc data_path buffer\n", __func__); + } + } + ret = count; + break; +#endif + case BATT_MISC_EVENT: + if (sscanf(buf, "%10d\n", &x) == 1) { + pr_info("%s: PMS sevice hiccup read done : %d ", __func__, x); + if (!battery->hiccup_status && + (battery->misc_event & BATT_MISC_EVENT_HICCUP_TYPE)) + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_HICCUP_TYPE, 1); + } + ret = count; + break; + case BATT_EXT_DEV_CHG: + if (sscanf(buf, "%10d\n", &x) == 1) { + pr_info("%s: Connect Ext Device : %d ",__func__, x); + + switch (x) { + case EXT_DEV_NONE: + battery->wire_status = SEC_BATTERY_CABLE_NONE; + value.intval = 0; + break; + case EXT_DEV_GAMEPAD_CHG: + battery->wire_status = SEC_BATTERY_CABLE_TA; + value.intval = 0; + break; + case EXT_DEV_GAMEPAD_OTG: + battery->wire_status = SEC_BATTERY_CABLE_OTG; + value.intval = 1; + break; + default: + break; + } + + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + value); + + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + ret = count; + } + break; + case BATT_WDT_CONTROL: + if (sscanf(buf, "%10d\n", &x) == 1) { + pr_info("%s: Charger WDT Set : %d\n", __func__, x); + battery->wdt_kick_disable = x; + } + ret = count; + break; + case MODE: + if (sscanf(buf, "%10d\n", &x) == 1) { + value.intval = x; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_MULTI_CHARGER_MODE, value); + ret = count; + } + break; + case CHECK_PS_READY: + case BATT_CHIP_ID: + break; + case CISD_FULLCAPREP_MAX: + break; +#if defined(CONFIG_BATTERY_CISD) + case CISD_DATA: + { + struct cisd *pcisd = &battery->cisd; + int temp_data[CISD_DATA_MAX_PER_DAY] = {0,}; + + sscanf(buf, "%10d\n", &temp_data[0]); + + if (temp_data[CISD_DATA_RESET_ALG] > 0) { + if (sscanf(buf, "%10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d, %10d\n", + &temp_data[0], &temp_data[1], &temp_data[2], + &temp_data[3], &temp_data[4], &temp_data[5], + &temp_data[6], &temp_data[7], &temp_data[8], + &temp_data[9], &temp_data[10], &temp_data[11], + &temp_data[12], &temp_data[13], &temp_data[14], + &temp_data[15], &temp_data[16], &temp_data[17], + &temp_data[18], &temp_data[19], &temp_data[20], + &temp_data[21], &temp_data[22], &temp_data[23], + &temp_data[24], &temp_data[25], &temp_data[26], + &temp_data[27], &temp_data[28], &temp_data[29], + &temp_data[30], &temp_data[31], &temp_data[32], + &temp_data[33], &temp_data[34], &temp_data[35], + &temp_data[36], &temp_data[37], &temp_data[38], + &temp_data[39], &temp_data[40], &temp_data[41], + &temp_data[42], &temp_data[43], &temp_data[44], + &temp_data[45], &temp_data[46], &temp_data[47], + &temp_data[48], &temp_data[49], &temp_data[50], + &temp_data[51], &temp_data[52], &temp_data[53], + &temp_data[54], &temp_data[55], &temp_data[56], + &temp_data[57], &temp_data[58], &temp_data[59], + &temp_data[60], &temp_data[61], &temp_data[62], + &temp_data[63], &temp_data[64], &temp_data[65], + &temp_data[66], &temp_data[67], &temp_data[68], + &temp_data[69], &temp_data[70], &temp_data[71], + &temp_data[72], &temp_data[73], &temp_data[74], + &temp_data[75], &temp_data[76]) <= CISD_DATA_MAX_PER_DAY) { + for (i = 0; i < CISD_DATA_MAX_PER_DAY; i++) + pcisd->data[i] = 0; + pcisd->data[CISD_DATA_ALG_INDEX] = battery->pdata->cisd_alg_index; + pcisd->data[CISD_DATA_FULL_COUNT] = temp_data[0]; + pcisd->data[CISD_DATA_CAP_MAX] = temp_data[1]; + pcisd->data[CISD_DATA_CAP_MIN] = temp_data[2]; + pcisd->data[CISD_DATA_VALERT_COUNT] = temp_data[16]; + pcisd->data[CISD_DATA_CYCLE] = temp_data[17]; + pcisd->data[CISD_DATA_WIRE_COUNT] = temp_data[18]; + pcisd->data[CISD_DATA_WIRELESS_COUNT] = temp_data[19]; + pcisd->data[CISD_DATA_HIGH_TEMP_SWELLING] = temp_data[20]; + pcisd->data[CISD_DATA_LOW_TEMP_SWELLING] = temp_data[21]; + pcisd->data[CISD_DATA_SWELLING_CHARGING_COUNT] = temp_data[22]; + pcisd->data[CISD_DATA_AICL_COUNT] = temp_data[26]; + pcisd->data[CISD_DATA_BATT_TEMP_MAX] = temp_data[27]; + pcisd->data[CISD_DATA_BATT_TEMP_MIN] = temp_data[28]; + pcisd->data[CISD_DATA_CHG_TEMP_MAX] = temp_data[29]; + pcisd->data[CISD_DATA_CHG_TEMP_MIN] = temp_data[30]; + pcisd->data[CISD_DATA_WPC_TEMP_MAX] = temp_data[31]; + pcisd->data[CISD_DATA_WPC_TEMP_MIN] = temp_data[32]; + pcisd->data[CISD_DATA_UNSAFETY_VOLTAGE] = temp_data[33]; + pcisd->data[CISD_DATA_UNSAFETY_TEMPERATURE] = temp_data[34]; + pcisd->data[CISD_DATA_SAFETY_TIMER] = temp_data[35]; + pcisd->data[CISD_DATA_VSYS_OVP] = temp_data[36]; + pcisd->data[CISD_DATA_VBAT_OVP] = temp_data[37]; + pcisd->data[CISD_DATA_AFC_FAIL] = temp_data[39]; + } + } else { + const char *p = buf; + + pr_info("%s: %s\n", __func__, buf); + for (i = CISD_DATA_RESET_ALG; i < CISD_DATA_MAX_PER_DAY; i++) { + if (sscanf(p, "%10d%n", &pcisd->data[i], &x) > 0) + p += (size_t)x; + else { + pr_info("%s: NO DATA (cisd_data)\n", __func__); + temp_data[CISD_DATA_RESET_ALG] = -1; + break; + } + } + + pr_info("%s: %s cisd data\n", __func__, + ((temp_data[CISD_DATA_RESET_ALG] < 0 || battery->fg_reset) ? "init" : "update")); + + if (temp_data[CISD_DATA_RESET_ALG] < 0 || battery->fg_reset) { + /* initialize data */ + for (i = CISD_DATA_RESET_ALG; i < CISD_DATA_MAX_PER_DAY; i++) + pcisd->data[i] = 0; + + battery->fg_reset = 0; + + pcisd->data[CISD_DATA_ALG_INDEX] = battery->pdata->cisd_alg_index; + + pcisd->data[CISD_DATA_FULL_COUNT] = 1; + pcisd->data[CISD_DATA_BATT_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_WPC_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_USB_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_BATT_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_WPC_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_USB_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CAP_MIN] = 0xFFFF; + + pcisd->data[CISD_DATA_FULL_COUNT_PER_DAY] = 1; + pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY] = 1000; + + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN] = 1000; + + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY] = 1000; + + pcisd->data[CISD_DATA_CAP_MIN_PER_DAY] = 0xFFFF; + + /* initialize pad data */ + init_cisd_pad_data(&battery->cisd); + } + } + ret = count; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } + break; + case CISD_DATA_JSON: + { + char tc; + struct cisd *pcisd = &battery->cisd; + + if (sscanf(buf, "%1c\n", &tc) == 1) { + if (tc == 'c') { + for (i = 0; i < CISD_DATA_MAX; i++) + pcisd->data[i] = 0; + + pcisd->data[CISD_DATA_FULL_COUNT] = 1; + pcisd->data[CISD_DATA_BATT_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_WPC_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_USB_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_BATT_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_WPC_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_USB_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CAP_MIN] = 0xFFFF; + + pcisd->data[CISD_DATA_FULL_COUNT_PER_DAY] = 1; + pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY] = 1000; + + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX] = -300; + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN] = 1000; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN] = 1000; + + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY] = -300; + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = 1000; + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY] = 1000; + + pcisd->data[CISD_DATA_CAP_MIN_PER_DAY] = 0xFFFF; + + } + } + ret = count; + } + break; + case CISD_DATA_D_JSON: + break; + case CISD_WIRE_COUNT: + if (sscanf(buf, "%10d\n", &x) == 1) { + struct cisd *pcisd = &battery->cisd; + pr_info("%s: Wire Count : %d\n", __func__, x); + pcisd->data[CISD_DATA_WIRE_COUNT] = x; + pcisd->data[CISD_DATA_WIRE_COUNT_PER_DAY]++; + } + ret = count; + break; + case CISD_WC_DATA: + set_cisd_pad_data(battery, buf); + ret = count; + break; + case CISD_WC_DATA_JSON: + break; + case PREV_BATTERY_DATA: + if (sscanf(buf, "%10d, %10d, %10d, %10d\n", + &battery->prev_volt, &battery->prev_temp, + &battery->prev_jig_on, &battery->prev_chg_on) >= 4) { + pr_info("%s: prev voltage : %d, prev_temp : %d, prev_jig_on : %d, prev_chg_on : %d\n", + __func__, battery->prev_volt, battery->prev_temp, + battery->prev_jig_on, battery->prev_chg_on); + + if (battery->prev_volt >= 3700 && battery->prev_temp >= 150 && + !battery->prev_jig_on && battery->fg_reset) + pr_info("%s: Battery have been Removed\n", __func__); + + ret = count; + } + battery->enable_update_data = 1; + break; + case PREV_BATTERY_INFO: + break; +#endif + case SAFETY_TIMER_SET: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x) { + battery->safety_timer_set = true; + } else { + battery->safety_timer_set = false; + } + ret = count; + } + break; + case BATT_SWELLING_CONTROL: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x) { + pr_info("%s : 15TEST START!! SWELLING MODE DISABLE\n", __func__); + battery->skip_swelling = true; + } else { + pr_info("%s : 15TEST END!! SWELLING MODE END\n", __func__); + battery->skip_swelling = false; + } + ret = count; + } + break; + case SAFETY_TIMER_INFO: + break; +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + case BATT_TEMP_TEST: + { + char tc; + if (sscanf(buf, "%c %10d\n", &tc, &x) == 2) { + pr_info("%s : temperature t: %c, temp: %d\n", __func__, tc, x); + if (tc == 'u') { + battery->temperature_test_usb = x; + } else if (tc == 'w') { + battery->temperature_test_wpc = x; + } else if (tc == 'b') { + battery->temperature_test_battery = x; + } else if (tc == 'c') { + battery->temperature_test_chg = x; + } else if (tc == 'k') { + battery->temperature_test_blkt = x; + } + ret = count; + } + break; + } +#endif + case BATT_CURRENT_EVENT: + break; + case CHARGE_OTG_CONTROL: + if (sscanf(buf, "%10d\n", &x) == 1) { + value.intval = x; + pr_info("%s: CHGIN-OTG %s\n", __func__, value.intval > 0 ? "on" : "off"); + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + ret = count; + } + break; + case CHARGE_UNO_CONTROL: + if (sscanf(buf, "%10d\n", &x) == 1) { + value.intval = x; + pr_info("%s: WCIN-UNO %s\n", __func__, value.intval > 0 ? "on" : "off"); + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value); + ret = count; + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int sec_bat_create_attrs(struct device *dev) +{ + unsigned long i = 0; + int rc = 0; + + for (i = 0; i < ARRAY_SIZE(sec_battery_attrs); i++) { + rc = device_create_file(dev, &sec_battery_attrs[i]); + if (rc) + goto create_attrs_failed; + } + goto create_attrs_succeed; + +create_attrs_failed: + while (i--) + device_remove_file(dev, &sec_battery_attrs[i]); +create_attrs_succeed: + return rc; +} + +static int sec_bat_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + int current_cable_type = SEC_BATTERY_CABLE_NONE; + int full_check_type = SEC_BATTERY_FULLCHARGED_NONE; + enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; + + dev_dbg(battery->dev, + "%s: (%d,%d)\n", __func__, psp, val->intval); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST) + full_check_type = battery->pdata->full_check_type; + else + full_check_type = battery->pdata->full_check_type_2nd; + if ((full_check_type == SEC_BATTERY_FULLCHARGED_CHGINT) && + (val->intval == POWER_SUPPLY_STATUS_FULL)) + sec_bat_do_fullcharged(battery); + sec_bat_set_charging_status(battery, val->intval); + break; + case POWER_SUPPLY_PROP_HEALTH: + sec_bat_ovp_uvlo_result(battery, val->intval); + break; + case POWER_SUPPLY_PROP_ONLINE: + current_cable_type = val->intval; +#if !defined(CONFIG_CCIC_NOTIFIER) + if ((battery->muic_cable_type != ATTACHED_DEV_SMARTDOCK_TA_MUIC) + && ((current_cable_type == SEC_BATTERY_CABLE_SMART_OTG) || + (current_cable_type == SEC_BATTERY_CABLE_SMART_NOTG))) + break; +#endif + + if (current_cable_type < 0) { + dev_info(battery->dev, + "%s: ignore event(%d)\n", + __func__, current_cable_type); + } else if (current_cable_type == SEC_BATTERY_CABLE_OTG) { + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_DISCHARGING); + battery->cable_type = current_cable_type; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, 0); + break; + } else { + battery->wire_status = current_cable_type; + if (is_nocharge_type(battery->wire_status) && + (battery->wc_status != SEC_WIRELESS_PAD_NONE) ) + current_cable_type = SEC_BATTERY_CABLE_WIRELESS; + } + dev_info(battery->dev, + "%s: current_cable(%d), wc_status(%d), wire_status(%d)\n", + __func__, current_cable_type, battery->wc_status, + battery->wire_status); + + /* cable is attached or detached + * if current_cable_type is minus value, + * check cable by sec_bat_get_cable_type() + * although SEC_BATTERY_CABLE_SOURCE_EXTERNAL is set + * (0 is SEC_BATTERY_CABLE_UNKNOWN) + */ + if ((current_cable_type >= 0) && + (current_cable_type < SEC_BATTERY_CABLE_MAX) && + (battery->pdata->cable_source_type & + SEC_BATTERY_CABLE_SOURCE_EXTERNAL)) { + + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work,0); + } else { + if (sec_bat_get_cable_type(battery, + battery->pdata->cable_source_type)) { + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work,0); + } + } + break; + case POWER_SUPPLY_PROP_CAPACITY: + battery->capacity = val->intval; + power_supply_changed(battery->psy_bat); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* If JIG is attached, the voltage is set as 1079 */ + pr_info("%s : set to the battery history : (%d)\n",__func__, val->intval); + if(val->intval == 1079) { + battery->voltage_now = 1079; + battery->voltage_avg = 1079; + power_supply_changed(battery->psy_bat); + } + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + break; + case POWER_SUPPLY_PROP_PRESENT: + battery->present = val->intval; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, 0); + break; +#if defined(CONFIG_BATTERY_SWELLING) + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + break; +#endif + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: + break; +#if defined(CONFIG_UPDATE_BATTERY_DATA) + case POWER_SUPPLY_PROP_POWER_DESIGN: + sec_bat_parse_dt(battery->dev, battery); + break; +#endif +#if defined(CONFIG_BATTERY_CISD) + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + pr_info("%s: Valert was occured! run monitor work for updating cisd data!\n", __func__); + battery->cisd.data[CISD_DATA_VALERT_COUNT]++; + battery->cisd.data[CISD_DATA_VALERT_COUNT_PER_DAY]++; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work_on(0, battery->monitor_wqueue, + &battery->monitor_work, 0); + break; +#endif + case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: + switch (ext_psp) { + case POWER_SUPPLY_EXT_PROP_AICL_CURRENT: + battery->aicl_current = val->intval; + battery->max_charge_power = battery->charge_power = battery->input_voltage * val->intval; + pr_info("%s: aicl : %dmA, %dmW)\n", __func__, + battery->aicl_current, battery->charge_power); + if (is_wired_type(battery->cable_type)) + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_AICL, + SEC_BAT_CURRENT_EVENT_AICL); +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_AICL_COUNT]++; + battery->cisd.data[CISD_DATA_AICL_COUNT_PER_DAY]++; +#endif + break; + case POWER_SUPPLY_EXT_PROP_SYSOVLO: + if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) { + pr_info("%s: Vsys is ovlo !!\n", __func__); + battery->is_sysovlo = true; + battery->is_recharging = false; + battery->health = POWER_SUPPLY_HEALTH_VSYS_OVP; + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_VSYS_OVP, SEC_BAT_CURRENT_EVENT_VSYS_OVP); + sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING); +#if defined(CONFIG_BATTERY_CISD) + battery->cisd.data[CISD_DATA_VSYS_OVP]++; + battery->cisd.data[CISD_DATA_VSYS_OVP_PER_DAY]++; +#endif +#if defined(CONFIG_SEC_ABC) + sec_abc_send_event("MODULE=battery@ERROR=vsys_ovp"); +#endif + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, 0); + } + break; + case POWER_SUPPLY_EXT_PROP_VBAT_OVP: + if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) { + pr_info("%s: Vbat is ovlo !!\n", __func__); + battery->is_vbatovlo = true; + battery->is_recharging = false; + battery->health = POWER_SUPPLY_HEALTH_VBAT_OVP; + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_VBAT_OVP, SEC_BAT_CURRENT_EVENT_VBAT_OVP); + sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING); + + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, 0); + } + break; + case POWER_SUPPLY_EXT_PROP_USB_CONFIGURE: +#if defined(CONFIG_CCIC_NOTIFIER) + if (battery->pdic_info.sink_status.rp_currentlvl > RP_CURRENT_LEVEL_DEFAULT) + return 0; +#endif + pr_info("%s: usb configured %d\n", __func__, val->intval); + if (val->intval == USB_CURRENT_UNCONFIGURED) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_100MA, + (SEC_BAT_CURRENT_EVENT_USB_100MA | SEC_BAT_CURRENT_EVENT_USB_SUPER)); + } else if (val->intval == USB_CURRENT_HIGH_SPEED) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE, 1); + sec_bat_set_current_event(battery, 0, + (SEC_BAT_CURRENT_EVENT_USB_100MA | SEC_BAT_CURRENT_EVENT_USB_SUPER)); + sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB, + battery->pdata->default_usb_input_current, + battery->pdata->default_usb_charging_current); + } else if (val->intval == USB_CURRENT_SUPER_SPEED) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE, 1); + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_SUPER, + (SEC_BAT_CURRENT_EVENT_USB_100MA | SEC_BAT_CURRENT_EVENT_USB_SUPER)); + sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB, + USB_CURRENT_SUPER_SPEED, USB_CURRENT_SUPER_SPEED); + } + sec_bat_set_charging_current(battery, 0); + break; + case POWER_SUPPLY_EXT_PROP_HV_DISABLE: + pr_info("HV wired charging mode is %s\n", (val->intval == CH_MODE_AFC_DISABLE_VAL ? "Disabled" : "Enabled")); + if (val->intval == CH_MODE_AFC_DISABLE_VAL) { + sec_bat_set_current_event(battery, + SEC_BAT_CURRENT_EVENT_HV_DISABLE, SEC_BAT_CURRENT_EVENT_HV_DISABLE); + + if (is_pd_wire_type(battery->cable_type)) { + battery->update_pd_list = true; + pr_info("%s: update pd list\n", __func__); + select_pdo(1); + } + } else { + sec_bat_set_current_event(battery, + 0, SEC_BAT_CURRENT_EVENT_HV_DISABLE); + + if (is_pd_wire_type(battery->cable_type)) { + int target_pd_index = 0; + + battery->update_pd_list = true; + pr_info("%s: update pd list\n", __func__); +#if defined(CONFIG_PDIC_PD30) + target_pd_index = battery->pd_list.num_fpdo - 1; +#else + target_pd_index = battery->pd_list.max_pd_count - 1; +#endif + if (target_pd_index >= 0 && target_pd_index < MAX_PDO_NUM) + select_pdo(battery->pd_list.pd_info[target_pd_index].pdo_index); + } + } + break; + case POWER_SUPPLY_EXT_PROP_WC_CONTROL: + pr_info("%s: Recover MFC IC (wc_enable: %d)\n", + __func__, battery->wc_enable); + + if (battery->pdata->wpc_en) { + mutex_lock(&battery->wclock); + if (battery->wc_enable) { + gpio_direction_output(battery->pdata->wpc_en, 1); + msleep(500); + gpio_direction_output(battery->pdata->wpc_en, 0); + } + mutex_unlock(&battery->wclock); + } +#if defined(CONFIG_BATTERY_CISD) + increase_cisd_count(CISD_DATA_DROP_VALUE); +#endif + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sec_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + union power_supply_propval value = {0, }; + enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) || + (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + if ((battery->pdata->cable_check_type & + SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE) && + !lpcharge) { + switch (battery->cable_type) { + case SEC_BATTERY_CABLE_USB: + case SEC_BATTERY_CABLE_USB_CDP: + val->intval = + POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + } +#if defined(CONFIG_STORE_MODE) + if (battery->store_mode && !lpcharge && + !is_nocharge_type(battery->cable_type) && + battery->status == POWER_SUPPLY_STATUS_DISCHARGING) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else +#endif + val->intval = battery->status; + } + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + if (is_nocharge_type(battery->cable_type)) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + } else if (is_hv_wire_type(battery->cable_type) || is_pd_wire_type(battery->cable_type)) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + } else { + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CHARGE_TYPE, value); + if (value.intval == SEC_BATTERY_CABLE_UNKNOWN) + /* if error in CHARGE_TYPE of charger + * set CHARGE_TYPE as NONE + */ + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + else + val->intval = value.intval; + } + break; + case POWER_SUPPLY_PROP_HEALTH: + if (battery->health >= POWER_SUPPLY_HEALTH_MAX) + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + else + val->intval = battery->health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = battery->present; +#if defined(CONFIG_SEC_FACTORY) || defined(CONFIG_DUMMY_BATTERY) + if (factory_mode == 2) + val->intval = 2; +#endif + break; + case POWER_SUPPLY_PROP_ONLINE: + if (is_hv_wireless_type(battery->cable_type) || + (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV)) { + if (sec_bat_hv_wc_normal_mode_check(battery)) + val->intval = SEC_BATTERY_CABLE_WIRELESS; + else + val->intval = SEC_BATTERY_CABLE_HV_WIRELESS_ETX; + } + else if(battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK) + val->intval = SEC_BATTERY_CABLE_WIRELESS; + else if(battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA) + val->intval = SEC_BATTERY_CABLE_WIRELESS; + else if(battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND) + val->intval = SEC_BATTERY_CABLE_WIRELESS; + else if(battery->cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS) + val->intval = SEC_BATTERY_CABLE_WIRELESS; + else if(battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_VEHICLE) + val->intval = SEC_BATTERY_CABLE_WIRELESS; + else + val->intval = battery->cable_type; + pr_info("%s cable type = %d sleep_mode = %d\n", __func__, val->intval, sleep_mode); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = battery->pdata->technology; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: +#ifdef CONFIG_SEC_FACTORY + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_NOW, value); + battery->voltage_now = value.intval; + dev_err(battery->dev, + "%s: voltage now(%d)\n", __func__, battery->voltage_now); +#endif + /* voltage value should be in uV */ + val->intval = battery->voltage_now * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: +#ifdef CONFIG_SEC_FACTORY + value.intval = SEC_BATTERY_VOLTAGE_AVERAGE; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_AVG, value); + battery->voltage_avg = value.intval; + dev_err(battery->dev, + "%s: voltage avg(%d)\n", __func__, battery->voltage_avg); +#endif + /* voltage value should be in uV */ + val->intval = battery->voltage_avg * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = battery->current_now; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = battery->current_avg; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = battery->pdata->battery_full_capacity * battery->capacity; + break; + /* charging mode (differ from power supply) */ + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = battery->charging_mode; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = battery->pdata->battery_full_capacity * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (battery->pdata->fake_capacity) { + val->intval = 90; + pr_info("%s : fixed capacity(%d)\n", __func__, val->intval); + } else { +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + if (battery->status == POWER_SUPPLY_STATUS_FULL) { + if(battery->eng_not_full_status) + val->intval = battery->capacity; + else + val->intval = 100; + } else { + val->intval = battery->capacity; + } +#else + if (battery->status == POWER_SUPPLY_STATUS_FULL) + val->intval = 100; + else + val->intval = battery->capacity; +#endif + } + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery->temperature; + break; + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + val->intval = battery->temper_amb; + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + val->intval = ttf_display(battery); + break; +#if defined(CONFIG_BATTERY_SWELLING) + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + if (battery->swelling_mode) + val->intval = 1; + else + val->intval = 0; + break; +#endif + case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: + val->intval = battery->wire_status; + break; + case POWER_SUPPLY_PROP_POWER_NOW: + val->intval = battery->charge_power; + break; + case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: + switch (ext_psp) { + case POWER_SUPPLY_EXT_PROP_SUB_PBA_TEMP_REC: + val->intval = !battery->vbus_limit; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int sec_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) || + (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) { + val->intval = 0; + return 0; + } + /* Set enable=1 only if the USB charger is connected */ + switch (battery->wire_status) { + case SEC_BATTERY_CABLE_USB: + case SEC_BATTERY_CABLE_USB_CDP: + val->intval = 1; + break; + case SEC_BATTERY_CABLE_PDIC: + case SEC_BATTERY_CABLE_NONE: + val->intval = (battery->pd_usb_attached) ? 1:0; + break; + default: + val->intval = 0; + break; + } + + if (is_slate_mode(battery)) + val->intval = 0; + return 0; +} + +static int sec_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) || + (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) { + val->intval = 0; + return 0; + } + + /* Set enable=1 only if the AC charger is connected */ + switch (battery->cable_type) { + case SEC_BATTERY_CABLE_TA: + case SEC_BATTERY_CABLE_UARTOFF: + case SEC_BATTERY_CABLE_LAN_HUB: + case SEC_BATTERY_CABLE_UNKNOWN: + case SEC_BATTERY_CABLE_PREPARE_TA: + case SEC_BATTERY_CABLE_9V_ERR: + case SEC_BATTERY_CABLE_9V_UNKNOWN: + case SEC_BATTERY_CABLE_9V_TA: + case SEC_BATTERY_CABLE_12V_TA: + case SEC_BATTERY_CABLE_HMT_CONNECTED: + case SEC_BATTERY_CABLE_HMT_CHARGE: + case SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT: + case SEC_BATTERY_CABLE_QC20: + case SEC_BATTERY_CABLE_QC30: + case SEC_BATTERY_CABLE_TIMEOUT: + case SEC_BATTERY_CABLE_SMART_OTG: + case SEC_BATTERY_CABLE_SMART_NOTG: +#if defined(CONFIG_USE_POGO) + case SEC_BATTERY_CABLE_POGO: +#endif + val->intval = 1; + break; + case SEC_BATTERY_CABLE_PDIC: + val->intval = (battery->pd_usb_attached) ? 0:1; + break; + default: + val->intval = 0; + break; + } + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery->chg_temp; + break; + default: + return -EINVAL; + } + + if (lpcharge && (battery->misc_event & BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE)) { + val->intval = 1; + } + + return 0; +} + +static int sec_wireless_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = is_wireless_type(battery->cable_type) ? + 1 : 0; + break; + case POWER_SUPPLY_PROP_PRESENT: +#if defined(CONFIG_USE_POGO) + val->intval = 0; +#else + val->intval = (battery->pdata->wireless_charger_name) ? + 1 : 0; +#endif + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sec_wireless_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: +#if defined(CONFIG_BATTERY_CISD) + if (val->intval != SEC_WIRELESS_PAD_NONE && battery->wc_status == SEC_WIRELESS_PAD_NONE) { + battery->cisd.data[CISD_DATA_WIRELESS_COUNT]++; + battery->cisd.data[CISD_DATA_WIRELESS_COUNT_PER_DAY]++; + } +#endif + /* Clear the FOD State */ + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_FOD, 1); + + if (val->intval == SEC_WIRELESS_PAD_A4WP) + battery->wc_status = SEC_WIRELESS_PAD_WPC; + else + battery->wc_status = val->intval; + + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + if (battery->wc_status == SEC_WIRELESS_PAD_NONE || + battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK || + battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK_TA || + battery->wc_status == SEC_WIRELESS_PAD_VEHICLE) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_BACKPACK_TYPE, + (battery->wc_status == SEC_WIRELESS_PAD_NONE)); + } + break; + case POWER_SUPPLY_PROP_AUTHENTIC: +#if defined(CONFIG_BATTERY_CISD) + pr_info("%s : tx_type(0x%x)\n", __func__, val->intval); + count_cisd_pad_data(&battery->cisd, val->intval); +#endif + break; + case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: + switch (ext_psp) { + case POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL: + if (val->intval) { + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK, + SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK); + } else { + sec_bat_set_current_event(battery, 0, + SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK); + } + + if (is_hv_wireless_type(battery->cable_type)) { + union power_supply_propval value = {0, }; + int cnt; + + mutex_lock(&battery->iolock); + value.intval = (val->intval) ? WIRELESS_VOUT_5V : + battery->wpc_vout_level; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value); + battery->aicl_current = 0; /* reset aicl current */ + mutex_unlock(&battery->iolock); + + for (cnt = 0; cnt < 5; cnt++) { + msleep(100); + psy_do_property(battery->pdata->wireless_charger_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + if (value.intval <= 6000) { + pr_info("%s: wireless vout goes to 5V Vout(%d).\n", + __func__, value.intval); + break; + } + } + } + break; + case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_CHG_ERR: + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_FOD, !val->intval); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +#if defined(CONFIG_USE_POGO) +static int sec_pogo_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + val->intval = battery->pogo_status; + pr_info("%s: POGO online : %d\n", __func__, val->intval); + + return 0; +} + +static int sec_pogo_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + battery->pogo_status = val->intval; + + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + pr_info("%s: pogo_status : %d\n", __func__, battery->pogo_status); + break; + default: + return -EINVAL; + } + + return 0; +} +#endif + +static int sec_ps_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + union power_supply_propval value; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (val->intval == 0 && battery->ps_enable == true) { + battery->ps_enable = false; + value.intval = val->intval; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + } else if ((val->intval == 1) && (battery->ps_enable == false) && + (battery->ps_status == true)) { + battery->ps_enable = true; + value.intval = val->intval; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + } else { + dev_err(battery->dev, + "%s: invalid setting (%d)\n", __func__, val->intval); + } + break; + case POWER_SUPPLY_PROP_ONLINE: + if (val->intval == SEC_BATTERY_CABLE_POWER_SHARING) { + battery->ps_status = true; + battery->ps_enable = true; + value.intval = battery->ps_enable; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + } else { + battery->ps_status = false; + battery->ps_enable = false; + value.intval = battery->ps_enable; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sec_ps_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_battery_info *battery = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = (battery->ps_enable) ? 1 : 0; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = (battery->ps_status) ? 1 : 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) || defined(CONFIG_MUIC_NOTIFIER) +static int sec_bat_cable_check(struct sec_battery_info *battery, + muic_attached_dev_t attached_dev) +{ + int current_cable_type = -1; + union power_supply_propval val = {0, }; + + pr_info("[%s]ATTACHED(%d)\n", __func__, attached_dev); + + switch (attached_dev) + { + case ATTACHED_DEV_JIG_UART_OFF_MUIC: + case ATTACHED_DEV_JIG_UART_ON_MUIC: + battery->is_jig_on = true; +#if defined(CONFIG_BATTERY_CISD) + battery->skip_cisd = true; +#endif + case ATTACHED_DEV_SMARTDOCK_MUIC: + case ATTACHED_DEV_DESKDOCK_MUIC: + case ATTACHED_DEV_JIG_USB_ON_MUIC: + current_cable_type = SEC_BATTERY_CABLE_NONE; + break; + case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC: + case ATTACHED_DEV_UNDEFINED_RANGE_MUIC: + current_cable_type = SEC_BATTERY_CABLE_NONE; + break; + case ATTACHED_DEV_HICCUP_MUIC: + current_cable_type = SEC_BATTERY_CABLE_NONE; + break; + case ATTACHED_DEV_OTG_MUIC: + case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC: + case ATTACHED_DEV_HMT_MUIC: + current_cable_type = SEC_BATTERY_CABLE_OTG; + break; + case ATTACHED_DEV_TIMEOUT_OPEN_MUIC: + current_cable_type = SEC_BATTERY_CABLE_TIMEOUT; + break; + case ATTACHED_DEV_USB_MUIC: + case ATTACHED_DEV_JIG_USB_OFF_MUIC: + case ATTACHED_DEV_SMARTDOCK_USB_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC: + current_cable_type = SEC_BATTERY_CABLE_USB; + break; + case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: + case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: + case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC: + current_cable_type = SEC_BATTERY_CABLE_UARTOFF; + break; + case ATTACHED_DEV_RDU_TA_MUIC: + battery->store_mode = true; + wake_lock(&battery->parse_mode_dt_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->parse_mode_dt_work, 0); + case ATTACHED_DEV_TA_MUIC: + case ATTACHED_DEV_CARDOCK_MUIC: + case ATTACHED_DEV_DESKDOCK_VB_MUIC: + case ATTACHED_DEV_SMARTDOCK_TA_MUIC: + case ATTACHED_DEV_UNOFFICIAL_TA_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC: + case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC: + case ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC: + current_cable_type = SEC_BATTERY_CABLE_TA; + break; + case ATTACHED_DEV_AFC_CHARGER_5V_MUIC: + case ATTACHED_DEV_QC_CHARGER_5V_MUIC: + case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC: + if (is_hv_wire_type(battery->cable_type) && + (battery->chg_limit || battery->vbus_chg_by_siop || battery->vbus_chg_by_full)) { + current_cable_type = SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT; + } else if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC && + battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT) { + current_cable_type = SEC_BATTERY_CABLE_PREPARE_TA; + } else { + current_cable_type = SEC_BATTERY_CABLE_TA; + } + break; + case ATTACHED_DEV_CDP_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC: + current_cable_type = SEC_BATTERY_CABLE_USB_CDP; + break; + case ATTACHED_DEV_USB_LANHUB_MUIC: + current_cable_type = SEC_BATTERY_CABLE_LAN_HUB; + break; + case ATTACHED_DEV_CHARGING_CABLE_MUIC: + current_cable_type = SEC_BATTERY_CABLE_POWER_SHARING; + break; + case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC: + case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC: + current_cable_type = SEC_BATTERY_CABLE_PREPARE_TA; + break; + case ATTACHED_DEV_AFC_CHARGER_9V_MUIC: + case ATTACHED_DEV_QC_CHARGER_9V_MUIC: + case ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC: + current_cable_type = SEC_BATTERY_CABLE_9V_TA; + break; +#if defined(CONFIG_MUIC_HV_12V) + case ATTACHED_DEV_AFC_CHARGER_12V_MUIC: + case ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC: + current_cable_type = SEC_BATTERY_CABLE_12V_TA; + break; +#endif + case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC: + case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC: + case ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC: + current_cable_type = SEC_BATTERY_CABLE_9V_ERR; + break; + case ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC: + case ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC: + case ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC: + current_cable_type = SEC_BATTERY_CABLE_9V_UNKNOWN; + break; + case ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC: + current_cable_type = SEC_BATTERY_CABLE_UNKNOWN; + break; + default: + pr_err("%s: invalid type for charger:%d\n", + __func__, attached_dev); + break; + } + + if (battery->is_jig_on && !battery->pdata->support_fgsrc_change) + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_ENERGY_NOW, val); + + return current_cable_type; +} +#endif + +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) +#if defined(CONFIG_CCIC_NOTIFIER) +static int sec_bat_get_pd_list_index(PDIC_SINK_STATUS *sink_status, struct sec_bat_pdic_list *pd_list) +{ + int i = 0; + + for (i = 0; i < pd_list->max_pd_count; i++) { + if (pd_list->pd_info[i].pdo_index == sink_status->current_pdo_num) + return i; + } + + return 0; +} + +static void sec_bat_set_rp_current(struct sec_battery_info *battery, int cable_type) +{ + if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_ABNORMAL) { + sec_bat_change_default_current(battery, cable_type, + RP_CURRENT_ABNORMAL_RP3, RP_CURRENT_ABNORMAL_RP3); + } else if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL3) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_HV_DISABLE) + sec_bat_change_default_current(battery, cable_type, + battery->pdata->default_input_current, battery->pdata->default_charging_current); + else + sec_bat_change_default_current(battery, cable_type, + RP_CURRENT_RP3, battery->pdata->max_charging_current); + } else if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL2) { + sec_bat_change_default_current(battery, cable_type, + RP_CURRENT_RP2, RP_CURRENT_RP2); + } else if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT) { + if (cable_type == SEC_BATTERY_CABLE_USB) { + if (battery->current_event & SEC_BAT_CURRENT_EVENT_USB_SUPER) + sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB, + USB_CURRENT_SUPER_SPEED, USB_CURRENT_SUPER_SPEED); + else + sec_bat_change_default_current(battery, cable_type, + battery->pdata->default_usb_input_current, + battery->pdata->default_usb_charging_current); + } else if (cable_type == SEC_BATTERY_CABLE_TA) { + sec_bat_change_default_current(battery, cable_type, + battery->pdata->default_input_current, + battery->pdata->default_charging_current); + } + } + battery->aicl_current = 0; + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AICL); + + pr_info("%s:(%d)\n", __func__, battery->pdic_info.sink_status.rp_currentlvl); + battery->max_charge_power = 0; + if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) + sec_bat_check_input_voltage(battery); + sec_bat_set_charging_current(battery, 1); +} +#endif + +static int make_pd_list(struct sec_battery_info *battery) +{ + int i = 0; + int base_charge_power = 0, selected_pdo_voltage = 0, selected_pdo_power = 0, selected_pdo_num = 0; + int pd_list_index = 0, temp_power = 0, num_pd_list = 0, pd_list_select = 0; + int pd_charging_charge_power = battery->current_event & SEC_BAT_CURRENT_EVENT_HV_DISABLE ? + battery->pdata->nv_charge_power : battery->pdata->pd_charging_charge_power; + union power_supply_propval value = {0, }; + POWER_LIST* pPower_list; + + /* If PD charger is attached first, current_pdo_num should be 1 supports 5V */ +#if defined(CONFIG_PDIC_PD30) + battery->pd_list.pd_info[0].max_voltage = + battery->pdic_info.sink_status.power_list[1].max_voltage; + battery->pd_list.pd_info[0].max_current = + battery->pdic_info.sink_status.power_list[1].max_current; +#else + battery->pd_list.pd_info[0].input_voltage = + battery->pdic_info.sink_status.power_list[1].max_voltage; + battery->pd_list.pd_info[0].input_current = + battery->pdic_info.sink_status.power_list[1].max_current; +#endif + battery->pd_list.pd_info[0].pdo_index = 1; + pd_list_index++; + + base_charge_power = + battery->pdic_info.sink_status.power_list[1].max_voltage * battery->pdic_info.sink_status.power_list[1].max_current; + + selected_pdo_voltage = SEC_INPUT_VOLTAGE_5V * 1000; + selected_pdo_power = 0; + selected_pdo_num = 0; + + for (i = 1; i <= battery->pdic_info.sink_status.available_pdo_num; i++) + { + pPower_list = &battery->pdic_info.sink_status.power_list[i]; +#if defined(CONFIG_PDIC_PD30) + if (!pPower_list->accept || pPower_list->apdo) /* skip not accept of apdo list */ + continue; +#endif + temp_power = pPower_list->max_voltage * pPower_list->max_current; + + if ((temp_power >= base_charge_power - 1000000) && (temp_power <= pd_charging_charge_power * 1000)) + { + if (temp_power >= selected_pdo_power && + pPower_list->max_voltage > selected_pdo_voltage && pPower_list->max_voltage <= battery->pdata->max_input_voltage) + { + selected_pdo_voltage = pPower_list->max_voltage; + selected_pdo_power = temp_power; + selected_pdo_num = i; + } + } + } + if (selected_pdo_num) + { + POWER_LIST* pSelected_power_list = + &battery->pdic_info.sink_status.power_list[selected_pdo_num]; + + battery->pd_list.pd_info[pd_list_index].pdo_index = selected_pdo_num; +#if defined(CONFIG_PDIC_PD30) + battery->pd_list.pd_info[pd_list_index].apdo = false; + battery->pd_list.pd_info[pd_list_index].max_voltage = pSelected_power_list->max_voltage; + battery->pd_list.pd_info[pd_list_index].max_current = pSelected_power_list->max_current; + battery->pd_list.pd_info[pd_list_index].min_voltage = 0; +#else + battery->pd_list.pd_info[pd_list_index].input_voltage = pSelected_power_list->max_voltage; + battery->pd_list.pd_info[pd_list_index].input_current = pSelected_power_list->max_current; +#endif + pd_list_index++; + } + +#if defined(CONFIG_PDIC_PD30) + battery->pd_list.num_fpdo = pd_list_index; + + if (battery->pdic_info.sink_status.has_apdo) { + /* unconditionally add APDO list */ + for (i = 1; i <= battery->pdic_info.sink_status.available_pdo_num; i++) + { + pPower_list = &battery->pdic_info.sink_status.power_list[i]; + + if (pPower_list->apdo) { + battery->pd_list.pd_info[pd_list_index].pdo_index = i; + battery->pd_list.pd_info[pd_list_index].apdo = true; + battery->pd_list.pd_info[pd_list_index].max_voltage = pPower_list->max_voltage; + battery->pd_list.pd_info[pd_list_index].min_voltage = pPower_list->min_voltage; + battery->pd_list.pd_info[pd_list_index].max_current = pPower_list->max_current; + + pd_list_index++; + } + } + battery->pd_list.num_apdo = pd_list_index - battery->pd_list.num_fpdo; + } else { + /* battery->pdic_info.sink_status has no apdo */ + battery->pd_list.num_apdo = 0; + } +#endif + + num_pd_list = pd_list_index; + + if (num_pd_list <= 0) { + pr_info("%s : PDO list is empty!!\n", __func__); + return 0; + } else { +#if defined(CONFIG_PDIC_PD30) + pr_info("%s: total num_pd_list: %d, num_fpdo: %d, num_apdo: %d\n", + __func__, num_pd_list, battery->pd_list.num_fpdo, battery->pd_list.num_apdo); +#else + pr_info("%s: total num_pd_list: %d\n", __func__, num_pd_list); +#endif + } + +#if defined(CONFIG_PDIC_PD30) + if (battery->pdic_info.sink_status.has_apdo) { + for (i = 0; i < battery->pd_list.num_fpdo - 1; i++) { + /* select pdo 1, if pd have apdo */ + if (battery->pd_list.pd_info[i].pdo_index == 1) { + pd_list_select = i; + break; + } + } + } else { + pd_list_select = num_pd_list - battery->pd_list.num_apdo - 1; + } +#else + pd_list_select = num_pd_list - 1; +#endif + + for (i = 0; i < num_pd_list; i++) { +#if defined(CONFIG_PDIC_PD30) + pr_info("%s: Made pd_list[%d] %s[%d,%s] maxVol:%d, minVol:%d, maxCur:%d\n", + __func__, i, i == pd_list_select ? "**" : " ", + battery->pd_list.pd_info[i].pdo_index, + battery->pd_list.pd_info[i].apdo ? "APDO" : "FIXED", + battery->pd_list.pd_info[i].max_voltage, + battery->pd_list.pd_info[i].min_voltage, + battery->pd_list.pd_info[i].max_current); +#else + pr_info("%s: Made pd_list[%d] %s[%d] voltage : %d, current : %d\n", + __func__, i, i == pd_list_select ? "**" : " ", + battery->pd_list.pd_info[i].pdo_index, + battery->pd_list.pd_info[i].input_voltage, + battery->pd_list.pd_info[i].input_current); +#endif + } + + battery->pd_list.max_pd_count = num_pd_list; + battery->max_charge_power = battery->pdic_info.sink_status.power_list[ \ + battery->pd_list.pd_info[pd_list_select].pdo_index].max_voltage * \ + battery->pdic_info.sink_status.power_list[battery->pd_list.pd_info[ \ + pd_list_select].pdo_index].max_current / 1000; + battery->pd_max_charge_power = battery->max_charge_power; + + if (battery->cable_type == SEC_BATTERY_CABLE_NONE) { + if (battery->pd_max_charge_power > 12000) + battery->cisd.cable_data[CISD_CABLE_PD_HIGH]++; + else + battery->cisd.cable_data[CISD_CABLE_PD]++; + } + + if (battery->pdic_info.sink_status.selected_pdo_num == battery->pd_list.pd_info[pd_list_select].pdo_index) { + battery->pdic_ps_rdy = true; + dev_info(battery->dev, "%s: battery->pdic_ps_rdy(%d)\n", __func__, battery->pdic_ps_rdy); + } else { + /* change input current before request new pdo if new pdo's input current is less than now */ +#if defined(CONFIG_PDIC_PD30) + if (battery->pd_list.pd_info[pd_list_select].max_current < battery->input_current) { + int input_current = battery->pd_list.pd_info[pd_list_select].max_current; +#else + if (battery->pd_list.pd_info[pd_list_select].input_current < battery->input_current) { + int input_current = battery->pd_list.pd_info[pd_list_select].input_current; +#endif + + value.intval = input_current; + battery->input_current = input_current; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + } + battery->pdic_ps_rdy = false; + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO, + SEC_BAT_CURRENT_EVENT_SELECT_PDO); + select_pdo(battery->pd_list.pd_info[pd_list_select].pdo_index); + } + + battery->pd_list.now_pd_index = sec_bat_get_pd_list_index(&battery->pdic_info.sink_status, + &battery->pd_list); + pr_info("%s : now_pd_index : %d\n", __func__, battery->pd_list.now_pd_index); + +#if defined(CONFIG_DIRECT_CHARGING) + value.intval = battery->pd_list.num_apdo > 0 ? 1 : 0; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_DIRECT_FIXED_PDO, value); +#endif + + return battery->pd_list.max_pd_count; +} + +static int usb_typec_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + const char *cmd; + struct sec_battery_info *battery = + container_of(nb, struct sec_battery_info, usb_typec_nb); + int cable_type = SEC_BATTERY_CABLE_NONE, i = 0, current_pdo = 0; + int pd_charging_charge_power = battery->current_event & SEC_BAT_CURRENT_EVENT_HV_DISABLE ? + battery->pdata->nv_charge_power : battery->pdata->pd_charging_charge_power; + CC_NOTI_ATTACH_TYPEDEF usb_typec_info = *(CC_NOTI_ATTACH_TYPEDEF *)data; + + dev_info(battery->dev, "%s: action (%ld) dump(0x%01x, 0x%01x, 0x%02x, 0x%04x, 0x%04x, 0x%04x)\n", + __func__, action, usb_typec_info.src, usb_typec_info.dest, usb_typec_info.id, + usb_typec_info.attach, usb_typec_info.rprd, usb_typec_info.cable_type); + + if (usb_typec_info.dest != CCIC_NOTIFY_DEV_BATTERY) { + dev_info(battery->dev, "%s: skip handler dest(%d)\n", + __func__, usb_typec_info.dest); + return 0; + } + + mutex_lock(&battery->typec_notylock); + switch (usb_typec_info.id) { + case CCIC_NOTIFY_ID_WATER: + case CCIC_NOTIFY_ID_ATTACH: + switch (usb_typec_info.attach) { + case MUIC_NOTIFY_CMD_DETACH: + case MUIC_NOTIFY_CMD_LOGICALLY_DETACH: + cmd = "DETACH"; + battery->is_jig_on = false; + battery->pd_usb_attached = false; + cable_type = SEC_BATTERY_CABLE_NONE; + battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC; + battery->pdic_info.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE; + break; + case MUIC_NOTIFY_CMD_ATTACH: + case MUIC_NOTIFY_CMD_LOGICALLY_ATTACH: + /* Skip notify from MUIC if PDIC is attached already */ + if (battery->wire_status == SEC_BATTERY_CABLE_PDIC) { + mutex_unlock(&battery->typec_notylock); + return 0; + } + cmd = "ATTACH"; + battery->muic_cable_type = usb_typec_info.cable_type; + cable_type = sec_bat_cable_check(battery, battery->muic_cable_type); + if (battery->cable_type != cable_type && + battery->pdic_info.sink_status.rp_currentlvl >= RP_CURRENT_LEVEL_DEFAULT && + (cable_type == SEC_BATTERY_CABLE_USB || cable_type == SEC_BATTERY_CABLE_TA)) { + sec_bat_set_rp_current(battery, cable_type); + } else if ((struct pdic_notifier_struct *)usb_typec_info.pd != NULL && + (*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_CCIC_ATTACH && + (*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.rp_currentlvl >= RP_CURRENT_LEVEL_DEFAULT && + (cable_type == SEC_BATTERY_CABLE_USB || cable_type == SEC_BATTERY_CABLE_TA)) { + battery->pdic_info.sink_status.rp_currentlvl = + (*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.rp_currentlvl; + sec_bat_set_rp_current(battery, cable_type); + } + break; + default: + cmd = "ERROR"; + cable_type = -1; + battery->muic_cable_type = usb_typec_info.cable_type; + break; + } + battery->pdic_attach = false; + battery->pdic_ps_rdy = false; +#if defined(CONFIG_AFC_CHARGER_MODE) + if (battery->muic_cable_type == ATTACHED_DEV_QC_CHARGER_9V_MUIC || + battery->muic_cable_type == ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC) + battery->hv_chg_name = "QC"; + else if (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_9V_MUIC || + battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC || + battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC || + battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC) + battery->hv_chg_name = "AFC"; +#if defined(CONFIG_MUIC_HV_12V) + else if (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_MUIC || + battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC) + battery->hv_chg_name = "12V"; +#endif + else + battery->hv_chg_name = "NONE"; +#endif + break; + case CCIC_NOTIFY_ID_POWER_STATUS: +#ifdef CONFIG_SEC_FACTORY + dev_info(battery->dev, "%s: pd_event(%d)\n", __func__, + (*(struct pdic_notifier_struct *)usb_typec_info.pd).event); +#endif + if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_DETACH){ + dev_info(battery->dev, "%s: skip pd operation - attach(%d)\n", __func__, usb_typec_info.attach); + battery->pdic_attach = false; + battery->pdic_ps_rdy = false; + battery->pd_list.now_pd_index = 0; + mutex_unlock(&battery->typec_notylock); + return 0; + } else if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC) { + cmd = "PD_PRWAP"; + dev_info(battery->dev, "%s: PRSWAP_SNKTOSRC(%d)\n", __func__, usb_typec_info.attach); + cable_type = SEC_BATTERY_CABLE_NONE; + + battery->pdic_attach = false; + battery->pdic_ps_rdy = false; + battery->pd_list.now_pd_index = 0; + goto skip_cable_check; + } + + cmd = "PD_ATTACH"; + if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_CCIC_ATTACH) { + battery->pdic_info.sink_status.rp_currentlvl = + (*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.rp_currentlvl; + dev_info(battery->dev, "%s: battery->rp_currentlvl(%d)\n", __func__, battery->pdic_info.sink_status.rp_currentlvl); + if (battery->wire_status == SEC_BATTERY_CABLE_USB || battery->wire_status == SEC_BATTERY_CABLE_TA) { + cable_type = battery->wire_status; + battery->chg_limit = false; + sec_bat_set_rp_current(battery, cable_type); + goto skip_cable_check; + } + mutex_unlock(&battery->typec_notylock); + return 0; + } + if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_PD_SINK_CAP || + battery->update_pd_list) { + battery->update_pd_list = false; + battery->pdic_attach = false; + } + if (!battery->pdic_attach) { + battery->pdic_info = *(struct pdic_notifier_struct *)usb_typec_info.pd; + battery->pd_list.now_pd_index = 0; + } else { + battery->pdic_info.sink_status.current_pdo_num = + (*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.current_pdo_num; + battery->pd_list.now_pd_index = sec_bat_get_pd_list_index(&battery->pdic_info.sink_status, + &battery->pd_list); + battery->pdic_ps_rdy = true; + dev_info(battery->dev, "%s: battery->pdic_ps_rdy(%d)\n", __func__, battery->pdic_ps_rdy); + } + current_pdo = battery->pdic_info.sink_status.current_pdo_num; + cable_type = SEC_BATTERY_CABLE_PDIC; + battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC; +#if defined(CONFIG_AFC_CHARGER_MODE) + battery->hv_chg_name = "PDIC"; +#endif + battery->input_voltage = + battery->pdic_info.sink_status.power_list[current_pdo].max_voltage / 1000; + dev_info(battery->dev, "%s: available pdo : %d, current pdo : %d\n", __func__, + battery->pdic_info.sink_status.available_pdo_num, current_pdo); + + for(i=1; i<= battery->pdic_info.sink_status.available_pdo_num; i++) { + pr_info("%s: power_list[%d], voltage : %d, current : %d, power : %d\n", __func__, i, + battery->pdic_info.sink_status.power_list[i].max_voltage, + battery->pdic_info.sink_status.power_list[i].max_current, + battery->pdic_info.sink_status.power_list[i].max_voltage * + battery->pdic_info.sink_status.power_list[i].max_current); + + if ((battery->pdic_info.sink_status.power_list[i].max_voltage * + battery->pdic_info.sink_status.power_list[i].max_current) > + (pd_charging_charge_power * 1000)) { + battery->pdic_info.sink_status.power_list[i].max_current = + (pd_charging_charge_power * 1000) / + battery->pdic_info.sink_status.power_list[i].max_voltage; + + pr_info("%s: ->updated [%d], voltage : %d, current : %d, power : %d\n", __func__, i, + battery->pdic_info.sink_status.power_list[i].max_voltage, + battery->pdic_info.sink_status.power_list[i].max_current, + battery->pdic_info.sink_status.power_list[i].max_voltage * + battery->pdic_info.sink_status.power_list[i].max_current); + } + if(battery->pdic_info.sink_status.power_list[i].max_current > + battery->pdata->max_input_current) { + battery->pdic_info.sink_status.power_list[i].max_current = + battery->pdata->max_input_current; + + pr_info("%s: ->updated [%d], voltage : %d, current : %d, power : %d\n", __func__, i, + battery->pdic_info.sink_status.power_list[i].max_voltage, + battery->pdic_info.sink_status.power_list[i].max_current, + battery->pdic_info.sink_status.power_list[i].max_voltage * + battery->pdic_info.sink_status.power_list[i].max_current); + } + } + if (!battery->pdic_attach) { + if (make_pd_list(battery) <= 0) + goto skip_cable_work; + } + battery->pdic_attach = true; + break; + case CCIC_NOTIFY_ID_USB: + if(usb_typec_info.cable_type == PD_USB_TYPE) + battery->pd_usb_attached = true; + dev_info(battery->dev, "%s: CCIC_NOTIFY_ID_USB: %d\n",__func__, battery->pd_usb_attached); + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + mutex_unlock(&battery->typec_notylock); + return 0; + default: + cmd = "ERROR"; + cable_type = -1; + battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC; +#if defined(CONFIG_AFC_CHARGER_MODE) + battery->hv_chg_name = "NONE"; +#endif + break; + } + +skip_cable_check: + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE, + (battery->muic_cable_type != ATTACHED_DEV_UNDEFINED_CHARGING_MUIC) && + (battery->muic_cable_type != ATTACHED_DEV_UNDEFINED_RANGE_MUIC)); + + if (battery->muic_cable_type == ATTACHED_DEV_HICCUP_MUIC) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_HICCUP_TYPE, 0); + battery->hiccup_status = 1; + } else { + battery->hiccup_status = 0; + if (battery->misc_event & BATT_MISC_EVENT_HICCUP_TYPE) { + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } + } + + /* showing charging icon and noti(no sound, vi, haptic) only + if slow insertion is detected by MUIC */ + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE, + (battery->muic_cable_type != ATTACHED_DEV_TIMEOUT_OPEN_MUIC)); + + if (cable_type < 0 || cable_type > SEC_BATTERY_CABLE_MAX) { + dev_info(battery->dev, "%s: ignore event(%d)\n", + __func__, battery->muic_cable_type); + goto skip_cable_work; + } else if ((cable_type == SEC_BATTERY_CABLE_UNKNOWN) && + (battery->status != POWER_SUPPLY_STATUS_DISCHARGING)) { + battery->cable_type = cable_type; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + dev_info(battery->dev, "%s: UNKNOWN cable plugin\n", __func__); + goto skip_cable_work; + } + battery->wire_status = cable_type; + + cancel_delayed_work(&battery->cable_work); + wake_unlock(&battery->cable_wake_lock); + + if (cable_type == SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT) { + /* set current event */ + cancel_delayed_work(&battery->afc_work); + wake_unlock(&battery->afc_wake_lock); + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_CHG_LIMIT, + (SEC_BAT_CURRENT_EVENT_CHG_LIMIT | SEC_BAT_CURRENT_EVENT_AFC)); + wake_lock(&battery->monitor_wake_lock); + battery->polling_count = 1; /* initial value = 1 */ + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } else if ((battery->wire_status == battery->cable_type) && + (((battery->wire_status == SEC_BATTERY_CABLE_USB || battery->wire_status == SEC_BATTERY_CABLE_TA) && + battery->pdic_info.sink_status.rp_currentlvl > RP_CURRENT_LEVEL_DEFAULT) || + is_hv_wire_type(battery->wire_status))) { + cancel_delayed_work(&battery->afc_work); + wake_unlock(&battery->afc_wake_lock); + sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AFC); + + wake_lock(&battery->monitor_wake_lock); + battery->polling_count = 1; /* initial value = 1 */ + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } else if (cable_type == SEC_BATTERY_CABLE_PREPARE_TA) { + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, msecs_to_jiffies(500)); + } else { + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + } + +skip_cable_work: + dev_info(battery->dev, "%s: CMD[%s], CABLE_TYPE[%d]\n", __func__, cmd, cable_type); + mutex_unlock(&battery->typec_notylock); + return 0; +} +#else +#if defined(CONFIG_CCIC_NOTIFIER) +static int batt_pdic_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + const char *cmd; + struct sec_battery_info *battery = + container_of(nb, struct sec_battery_info, + pdic_nb); + battery->pdic_info = *(struct pdic_notifier_struct *)data; + + mutex_lock(&battery->batt_handlelock); + pr_info("%s: pdic_event: %d\n", __func__, battery->pdic_info.event); + + switch (battery->pdic_info.event) { + int i, selected_pdo; + + case PDIC_NOTIFY_EVENT_DETACH: + cmd = "DETACH"; + battery->pdic_attach = false; + if (battery->wire_status == SEC_BATTERY_CABLE_PDIC) { + battery->wire_status = SEC_BATTERY_CABLE_NONE; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + } + break; + case PDIC_NOTIFY_EVENT_CCIC_ATTACH: + cmd = "ATTACH"; + break; + case PDIC_NOTIFY_EVENT_PD_SINK: + selected_pdo = battery->pdic_info.sink_status.selected_pdo_num; + cmd = "ATTACH"; + battery->wire_status = SEC_BATTERY_CABLE_PDIC; + battery->pdic_attach = true; + battery->input_voltage = + battery->pdic_info.sink_status.power_list[selected_pdo].max_voltage / 1000; + + pr_info("%s: total pdo : %d, selected pdo : %d\n", __func__, + battery->pdic_info.sink_status.available_pdo_num, selected_pdo); + for(i=1; i<= battery->pdic_info.sink_status.available_pdo_num; i++) + { + pr_info("%s: power_list[%d], voltage : %d, current : %d, power : %d\n", __func__, i, + battery->pdic_info.sink_status.power_list[i].max_voltage, + battery->pdic_info.sink_status.power_list[i].max_current, + battery->pdic_info.sink_status.power_list[i].max_voltage * + battery->pdic_info.sink_status.power_list[i].max_current); + } + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + break; + case PDIC_NOTIFY_EVENT_PD_SOURCE: + cmd = "ATTACH"; + break; + default: + cmd = "ERROR"; + break; + } + pr_info("%s: CMD=%s, cable_type : %d\n", __func__, cmd, battery->cable_type); + mutex_unlock(&battery->batt_handlelock); + return 0; +} +#endif + +#if defined(CONFIG_MUIC_NOTIFIER) +static int batt_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + const char *cmd; + int cable_type = SEC_BATTERY_CABLE_NONE; + struct sec_battery_info *battery = + container_of(nb, struct sec_battery_info, + batt_nb); + union power_supply_propval value = {0, }; + +#if defined(CONFIG_CCIC_NOTIFIER) + CC_NOTI_ATTACH_TYPEDEF *p_noti = (CC_NOTI_ATTACH_TYPEDEF *)data; + muic_attached_dev_t attached_dev = p_noti->cable_type; +#else + muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data; +#endif + + mutex_lock(&battery->batt_handlelock); + switch (action) { + case MUIC_NOTIFY_CMD_DETACH: + case MUIC_NOTIFY_CMD_LOGICALLY_DETACH: + cmd = "DETACH"; + battery->is_jig_on = false; + cable_type = SEC_BATTERY_CABLE_NONE; + battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC; + break; + case MUIC_NOTIFY_CMD_ATTACH: + case MUIC_NOTIFY_CMD_LOGICALLY_ATTACH: + cmd = "ATTACH"; + cable_type = sec_bat_cable_check(battery, attached_dev); + battery->muic_cable_type = attached_dev; + break; + default: + cmd = "ERROR"; + cable_type = -1; + battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC; + break; + } + + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE, +#if !defined(CONFIG_ENG_BATTERY_CONCEPT) && !defined(CONFIG_SEC_FACTORY) + (battery->muic_cable_type != ATTACHED_DEV_JIG_UART_ON_MUIC) && + (battery->muic_cable_type != ATTACHED_DEV_JIG_USB_ON_MUIC) && +#endif + (battery->muic_cable_type != ATTACHED_DEV_UNDEFINED_RANGE_MUIC)); + + if (battery->muic_cable_type == ATTACHED_DEV_HICCUP_MUIC) { + sec_bat_set_misc_event(battery, BATT_MISC_EVENT_HICCUP_TYPE, 0); + battery->hiccup_status = 1; + } else { + battery->hiccup_status = 0; + if (battery->misc_event & BATT_MISC_EVENT_HICCUP_TYPE) { + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } + } + +#if defined(CONFIG_CCIC_NOTIFIER) + /* If PD cable is already attached, return this function */ + if(battery->pdic_attach) { + dev_info(battery->dev, "%s: ignore event pdic attached(%d)\n", + __func__, battery->pdic_attach); + mutex_unlock(&battery->batt_handlelock); + return 0; + } +#endif + + if (attached_dev == ATTACHED_DEV_MHL_MUIC) { + mutex_unlock(&battery->batt_handlelock); + return 0; + } + + if (cable_type < 0) { + dev_info(battery->dev, "%s: ignore event(%d)\n", + __func__, cable_type); + } else if (cable_type == SEC_BATTERY_CABLE_POWER_SHARING) { + battery->ps_status = true; + battery->ps_enable = true; + battery->wire_status = cable_type; + dev_info(battery->dev, "%s: power sharing cable plugin\n", __func__); + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS) { + battery->wc_status = SEC_WIRELESS_PAD_WPC; + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK) { + battery->wc_status = SEC_WIRELESS_PAD_WPC_PACK; + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK_TA) { + battery->wc_status = SEC_WIRELESS_PAD_WPC_PACK_TA; + } else if (cable_type == SEC_BATTERY_CABLE_HV_WIRELESS) { + battery->wc_status = SEC_WIRELESS_PAD_WPC_HV; + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND) { + battery->wc_status = SEC_WIRELESS_PAD_WPC_STAND; + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_STAND) { + battery->wc_status = SEC_WIRELESS_PAD_WPC_STAND_HV; + } else if (cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS) { + battery->wc_status = SEC_WIRELESS_PAD_PMA; + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_VEHICLE) { + battery->wc_status = SEC_WIRELESS_PAD_VEHICLE; + } else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE) { + battery->wc_status = SEC_WIRELESS_PAD_VEHICLE_HV; + } else if ((cable_type == SEC_BATTERY_CABLE_UNKNOWN) && + (battery->status != POWER_SUPPLY_STATUS_DISCHARGING)) { + battery->cable_type = cable_type; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + dev_info(battery->dev, + "%s: UNKNOWN cable plugin\n", __func__); + mutex_unlock(&battery->batt_handlelock); + return 0; + } else { + battery->wire_status = cable_type; + if (is_nocharge_type(battery->wire_status) && + (battery->wc_status) && (!battery->ps_status)) + cable_type = SEC_BATTERY_CABLE_WIRELESS; + } + dev_info(battery->dev, + "%s: current_cable(%d), wc_status(%d), wire_status(%d)\n", + __func__, cable_type, battery->wc_status, + battery->wire_status); + + mutex_unlock(&battery->batt_handlelock); + if (attached_dev == ATTACHED_DEV_USB_LANHUB_MUIC) { + if (!strcmp(cmd, "ATTACH")) { + value.intval = true; + psy_do_property("otg", set, POWER_SUPPLY_PROP_ONLINE, value); + dev_info(battery->dev, + "%s: Powered OTG cable attached\n", __func__); + } else { + value.intval = false; + psy_do_property("otg", set, POWER_SUPPLY_PROP_ONLINE, value); + dev_info(battery->dev, + "%s: Powered OTG cable detached\n", __func__); + } + } + +#if defined(CONFIG_AFC_CHARGER_MODE) + if (!strcmp(cmd, "ATTACH")) { + if ((battery->muic_cable_type >= ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC) && + (battery->muic_cable_type <= ATTACHED_DEV_QC_CHARGER_9V_MUIC)) { + battery->hv_chg_name = "QC"; + } else if ((battery->muic_cable_type >= ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC) && + (battery->muic_cable_type <= ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC)) { + battery->hv_chg_name = "AFC"; +#if defined(CONFIG_MUIC_HV_12V) + } else if (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_MUIC || + battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC) { + battery->hv_chg_name = "12V"; +#endif + } else + battery->hv_chg_name = "NONE"; + } else { + battery->hv_chg_name = "NONE"; + } + + pr_info("%s : HV_CHARGER_NAME(%s)\n", + __func__, battery->hv_chg_name); +#endif + + if ((cable_type >= 0) && + cable_type <= SEC_BATTERY_CABLE_MAX) { + if (cable_type == SEC_BATTERY_CABLE_POWER_SHARING) { + value.intval = battery->ps_enable; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } else if((cable_type == SEC_BATTERY_CABLE_NONE) && (battery->ps_status)) { + if (battery->ps_enable) { + battery->ps_enable = false; + value.intval = battery->ps_enable; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); + } + battery->ps_status = false; + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } else if(cable_type != battery->cable_type) { + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->cable_work, 0); + } else { + dev_info(battery->dev, + "%s: Cable is Not Changed(%d)\n", + __func__, battery->cable_type); + } + } + + pr_info("%s: CMD=%s, attached_dev=%d\n", __func__, cmd, attached_dev); + + return 0; +} +#endif /* CONFIG_MUIC_NOTIFIER */ +#endif + +#if defined(CONFIG_VBUS_NOTIFIER) +static int vbus_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + vbus_status_t vbus_status = *(vbus_status_t *)data; + struct sec_battery_info *battery = + container_of(nb, struct sec_battery_info, + vbus_nb); + union power_supply_propval value = {0, }; + + mutex_lock(&battery->batt_handlelock); + if (battery->muic_cable_type == ATTACHED_DEV_HMT_MUIC && + battery->muic_vbus_status != vbus_status && + battery->muic_vbus_status == STATUS_VBUS_HIGH && + vbus_status == STATUS_VBUS_LOW) { + msleep(500); + value.intval = true; + psy_do_property(battery->pdata->charger_name, set, + (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, + value); + dev_info(battery->dev, + "%s: changed to OTG cable attached\n", __func__); + + battery->wire_status = SEC_BATTERY_CABLE_OTG; + wake_lock(&battery->cable_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0); + } + pr_info("%s: action=%d, vbus_status=%d\n", __func__, (int)action, vbus_status); + mutex_unlock(&battery->batt_handlelock); + battery->muic_vbus_status = vbus_status; + + return 0; +} +#endif + +#ifdef CONFIG_OF +static int sec_bat_parse_dt(struct device *dev, + struct sec_battery_info *battery) +{ + struct device_node *np; + sec_battery_platform_data_t *pdata = battery->pdata; + int ret = 0, len = 0; + unsigned int i = 0; + const u32 *p; + u32 temp = 0; + + np = of_find_node_by_name(NULL, "cable-info"); + if (!np) { + pr_err ("%s : np NULL\n", __func__); + } else { + struct device_node *child; + u32 input_current = 0, charging_current = 0; + + ret = of_property_read_u32(np, "default_input_current", &input_current); + ret = of_property_read_u32(np, "default_charging_current", &charging_current); + ret = of_property_read_u32(np, "full_check_current_1st", &pdata->full_check_current_1st); + ret = of_property_read_u32(np, "full_check_current_2nd", &pdata->full_check_current_2nd); + + pdata->default_input_current = input_current; + pdata->default_charging_current = charging_current; + + pdata->charging_current = + kzalloc(sizeof(sec_charging_current_t) * SEC_BATTERY_CABLE_MAX, + GFP_KERNEL); + + for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++) { + pdata->charging_current[i].input_current_limit = (unsigned int)input_current; + pdata->charging_current[i].fast_charging_current = (unsigned int)charging_current; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_u32(child, "input_current", &input_current); + ret = of_property_read_u32(child, "charging_current", &charging_current); + + p = of_get_property(child, "cable_number", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + for (i = 0; i <= len; i++) { + ret = of_property_read_u32_index(child, "cable_number", i, &temp); + pdata->charging_current[temp].input_current_limit = (unsigned int)input_current; + pdata->charging_current[temp].fast_charging_current = (unsigned int)charging_current; + } + + } + } + + for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++) { + pr_info("%s : CABLE_NUM(%d) INPUT(%d) CHARGING(%d)\n", + __func__, i, + pdata->charging_current[i].input_current_limit, + pdata->charging_current[i].fast_charging_current); + } + + pr_info("%s : TOPOFF_1ST(%d), TOPOFF_2ND(%d)\n", + __func__, pdata->full_check_current_1st, pdata->full_check_current_2nd); + pdata->default_usb_input_current = pdata->charging_current[SEC_BATTERY_CABLE_USB].input_current_limit; + pdata->default_usb_charging_current = pdata->charging_current[SEC_BATTERY_CABLE_USB].fast_charging_current; +#ifdef CONFIG_SEC_FACTORY + pdata->default_charging_current = 1500; + pdata->charging_current[SEC_BATTERY_CABLE_TA].fast_charging_current = 1500; +#endif + np = of_find_node_by_name(NULL, "battery"); + if (!np) { + pr_info("%s: np NULL\n", __func__); + return 1; + } + + ret = of_property_read_u32(np, "battery,battery_full_capacity", + &pdata->battery_full_capacity); + if (ret) + pr_info("%s : battery_full_capacity is Empty\n", __func__); +#if defined(CONFIG_BATTERY_CISD) + else { + pr_info("%s : battery_full_capacity : %d\n", __func__, pdata->battery_full_capacity); + pdata->cisd_cap_high_thr = pdata->battery_full_capacity + 1000; /* battery_full_capacity + 1000 */ + pdata->cisd_cap_low_thr = pdata->battery_full_capacity + 500; /* battery_full_capacity + 500 */ + pdata->cisd_cap_limit = (pdata->battery_full_capacity * 11) / 10; /* battery_full_capacity + 10% */ + } + +#if defined(CONFIG_QH_ALGORITHM) + ret = of_property_read_u32(np, "battery,cisd_qh_current_low_thr", + &pdata->cisd_qh_current_low_thr); + if (ret) { + pr_info("%s : cisd_qh_current_low_thr is Empty\n", __func__); + } + ret = of_property_read_u32(np, "battery,cisd_qh_current_high_thr", + &pdata->cisd_qh_current_high_thr); + if (ret) { + pr_info("%s : cisd_qh_current_high_thr is Empty\n", __func__); + } + ret = of_property_read_u32(np, "battery,cisd_qh_vfsoc_thr", + &pdata->cisd_qh_vfsoc_thr); + if (ret) { + pr_info("%s : cisd_qh_vfsoc_thr is Empty\n", __func__); + } +#endif + ret = of_property_read_u32(np, "battery,cisd_max_voltage_thr", + &pdata->max_voltage_thr); + if (ret) { + pr_info("%s : cisd_max_voltage_thr is Empty\n", __func__); + pdata->max_voltage_thr = 4400; + } + + ret = of_property_read_u32(np, "battery,cisd_alg_index", + &pdata->cisd_alg_index); + if (ret) { + pr_info("%s : cisd_alg_index is Empty. Defalut set to six\n", __func__); + pdata->cisd_alg_index = 6; + } else { + pr_info("%s : set cisd_alg_index : %d\n", __func__, pdata->cisd_alg_index); + } +#endif + + ret = of_property_read_u32(np, + "battery,expired_time", &temp); + if (ret) { + pr_info("expired time is empty\n"); + pdata->expired_time = 3 * 60 * 60; + } else { + pdata->expired_time = (unsigned int) temp; + } + pdata->expired_time *= 1000; + battery->expired_time = pdata->expired_time; + + ret = of_property_read_u32(np, + "battery,recharging_expired_time", &temp); + if (ret) { + pr_info("expired time is empty\n"); + pdata->recharging_expired_time = 90 * 60; + } else { + pdata->recharging_expired_time = (unsigned int) temp; + } + pdata->recharging_expired_time *= 1000; + + ret = of_property_read_u32(np, + "battery,standard_curr", &pdata->standard_curr); + if (ret) { + pr_info("standard_curr is empty\n"); + pdata->standard_curr = 2150; + } + + ret = of_property_read_string(np, + "battery,vendor", (char const **)&pdata->vendor); + if (ret) + pr_info("%s: Vendor is Empty\n", __func__); + + ret = of_property_read_string(np, + "battery,charger_name", (char const **)&pdata->charger_name); + if (ret) + pr_info("%s: Charger name is Empty\n", __func__); + + ret = of_property_read_string(np, + "battery,fuelgauge_name", (char const **)&pdata->fuelgauge_name); + if (ret) + pr_info("%s: Fuelgauge name is Empty\n", __func__); + + ret = of_property_read_string(np, + "battery,fgsrc_switch_name", (char const **)&pdata->fgsrc_switch_name); + if (ret) { + pdata->support_fgsrc_change = false; + pr_info("%s: fgsrc_switch_name is Empty\n", __func__); + } + else + pdata->support_fgsrc_change = true; + + ret = of_property_read_string(np, + "battery,wireless_charger_name", (char const **)&pdata->wireless_charger_name); + if (ret) + pr_info("%s: Wireless charger name is Empty\n", __func__); + + ret = of_property_read_string(np, + "battery,chip_vendor", (char const **)&pdata->chip_vendor); + if (ret) + pr_info("%s: Chip vendor is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,technology", + &pdata->technology); + if (ret) + pr_info("%s : technology is Empty\n", __func__); + + ret = of_property_read_u32(np, + "battery,wireless_cc_cv", &pdata->wireless_cc_cv); + + ret = of_property_read_u32(np, + "battery,set_cv_vout_in_low_capacity", &pdata->set_cv_vout_in_low_capacity); + + pdata->fake_capacity = of_property_read_bool(np, + "battery,fake_capacity"); + + p = of_get_property(np, "battery,polling_time", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + pdata->polling_time = kzalloc(sizeof(*pdata->polling_time) * len, GFP_KERNEL); + ret = of_property_read_u32_array(np, "battery,polling_time", + pdata->polling_time, len); + if (ret) + pr_info("%s : battery,polling_time is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,thermal_source", + &pdata->thermal_source); + if (ret) + pr_info("%s : Thermal source is Empty\n", __func__); + + if (pdata->thermal_source == SEC_BATTERY_THERMAL_SOURCE_ADC) { + p = of_get_property(np, "battery,temp_table_adc", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + pdata->temp_adc_table_size = len; + pdata->temp_amb_adc_table_size = len; + + pdata->temp_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->temp_adc_table_size, GFP_KERNEL); + pdata->temp_amb_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->temp_adc_table_size, GFP_KERNEL); + + for(i = 0; i < pdata->temp_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,temp_table_adc", i, &temp); + pdata->temp_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : Temp_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,temp_table_data", i, &temp); + pdata->temp_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : Temp_adc_table(data) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,temp_table_adc", i, &temp); + pdata->temp_amb_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : Temp_amb_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,temp_table_data", i, &temp); + pdata->temp_amb_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : Temp_amb_adc_table(data) is Empty\n", + __func__); + } + } + ret = of_property_read_u32(np, "battery,usb_thermal_source", + &pdata->usb_thermal_source); + if (ret) + pr_info("%s : usb_thermal_source is Empty\n", __func__); + + if(pdata->usb_thermal_source) { + p = of_get_property(np, "battery,usb_temp_table_adc", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + pdata->usb_temp_adc_table_size = len; + + pdata->usb_temp_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->usb_temp_adc_table_size, GFP_KERNEL); + + for(i = 0; i < pdata->usb_temp_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,usb_temp_table_adc", i, &temp); + pdata->usb_temp_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : Usb_Temp_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,usb_temp_table_data", i, &temp); + pdata->usb_temp_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : Usb_Temp_adc_table(data) is Empty\n", + __func__); + } + } + + ret = of_property_read_u32(np, "battery,chg_thermal_source", + &pdata->chg_thermal_source); + if (ret) + pr_info("%s : chg_thermal_source is Empty\n", __func__); + + if(pdata->chg_thermal_source) { + p = of_get_property(np, "battery,chg_temp_table_adc", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + pdata->chg_temp_adc_table_size = len; + + pdata->chg_temp_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->chg_temp_adc_table_size, GFP_KERNEL); + + for(i = 0; i < pdata->chg_temp_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,chg_temp_table_adc", i, &temp); + pdata->chg_temp_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : CHG_Temp_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,chg_temp_table_data", i, &temp); + pdata->chg_temp_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : CHG_Temp_adc_table(data) is Empty\n", + __func__); + } + } + + ret = of_property_read_u32(np, "battery,wpc_thermal_source", + &pdata->wpc_thermal_source); + if (ret) + pr_info("%s : wpc_thermal_source is Empty\n", __func__); + + if(pdata->wpc_thermal_source) { + p = of_get_property(np, "battery,wpc_temp_table_adc", &len); + if (!p) { + pr_info("%s : wpc_temp_table_adc(adc) is Empty\n",__func__); + } else { + len = len / sizeof(u32); + + pdata->wpc_temp_adc_table_size = len; + + pdata->wpc_temp_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->wpc_temp_adc_table_size, GFP_KERNEL); + + for(i = 0; i < pdata->wpc_temp_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,wpc_temp_table_adc", i, &temp); + pdata->wpc_temp_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : WPC_Temp_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,wpc_temp_table_data", i, &temp); + pdata->wpc_temp_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : WPC_Temp_adc_table(data) is Empty\n", + __func__); + } + } + } + + ret = of_property_read_u32(np, "battery,coil_thermal_source", + &pdata->coil_thermal_source); + if (ret) + pr_info("%s : coil_thermal_source is Empty\n", __func__); + else + pr_info("%s : coil_thermal_source exists\n", __func__); + + ret = of_property_read_u32(np, "battery,slave_thermal_source", + &pdata->slave_thermal_source); + if (ret) + pr_info("%s : slave_thermal_source is Empty\n", __func__); + + if(pdata->slave_thermal_source) { + p = of_get_property(np, "battery,slave_chg_temp_table_adc", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + pdata->slave_chg_temp_adc_table_size = len; + + pdata->slave_chg_temp_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->slave_chg_temp_adc_table_size, GFP_KERNEL); + + for(i = 0; i < pdata->slave_chg_temp_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,slave_chg_temp_table_adc", i, &temp); + pdata->slave_chg_temp_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : slave_chg_temp_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,slave_chg_temp_table_data", i, &temp); + pdata->slave_chg_temp_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : slave_chg_temp_adc_table(data) is Empty\n", + __func__); + } + } + + ret = of_property_read_u32(np, "battery,blkt_thermal_source", + &pdata->blkt_thermal_source); + if (ret) + pr_info("%s : blkt_thermal_source is Empty\n", __func__); + + if (pdata->blkt_thermal_source) { + p = of_get_property(np, "battery,blkt_temp_table_adc", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + pdata->blkt_temp_adc_table_size = len; + + pdata->blkt_temp_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->blkt_temp_adc_table_size, GFP_KERNEL); + + for (i = 0; i < pdata->blkt_temp_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,blkt_temp_table_adc", i, &temp); + pdata->blkt_temp_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : Blkt_Temp_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,blkt_temp_table_data", i, &temp); + pdata->blkt_temp_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : Blkt_Temp_adc_table(data) is Empty\n", + __func__); + } + } + + ret = of_property_read_u32(np, "battery,slave_chg_temp_check", + &pdata->slave_chg_temp_check); + if (ret) + pr_info("%s : slave_chg_temp_check is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,blkt_temp_check", + &pdata->blkt_temp_check); + if (ret) + pr_info("%s : blkt_temp_check is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_temp_check", + &pdata->chg_temp_check); + if (ret) + pr_info("%s : chg_temp_check is Empty\n", __func__); + + if (pdata->chg_temp_check) { + ret = of_property_read_u32(np, "battery,chg_12v_high_temp", + &temp); + pdata->chg_12v_high_temp = (int)temp; + if (ret) + pr_info("%s : chg_12v_high_temp is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_high_temp", + &temp); + pdata->chg_high_temp = (int)temp; + if (ret) + pr_info("%s : chg_high_temp is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_high_temp_recovery", + &temp); + pdata->chg_high_temp_recovery = (int)temp; + if (ret) + pr_info("%s : chg_temp_recovery is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_charging_limit_current", + &pdata->chg_charging_limit_current); + if (ret) + pr_info("%s : chg_charging_limit_current is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_input_limit_current", + &pdata->chg_input_limit_current); + if (ret) + pr_info("%s : chg_input_limit_current is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,mix_high_temp", + &temp); + pdata->mix_high_temp = (int)temp; + if (ret) + pr_info("%s : mix_high_temp is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,mix_high_chg_temp", + &temp); + pdata->mix_high_chg_temp = (int)temp; + if (ret) + pr_info("%s : mix_high_chg_temp is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,mix_high_temp_recovery", + &temp); + pdata->mix_high_temp_recovery = (int)temp; + if (ret) + pr_info("%s : mix_high_temp_recovery is Empty\n", __func__); + } + + ret = of_property_read_u32(np, "battery,wpc_temp_check", + &pdata->wpc_temp_check); + if (ret) + pr_info("%s : wpc_temp_check is Empty\n", __func__); + + if (pdata->wpc_temp_check) { + ret = of_property_read_u32(np, "battery,wpc_temp_control_source", + &pdata->wpc_temp_control_source); + if (ret) { + pr_info("%s : wpc_temp_control_source is Empty\n", __func__); + pdata->wpc_temp_control_source = TEMP_CONTROL_SOURCE_CHG_THM; + } + + ret = of_property_read_u32(np, "battery,wpc_temp_lcd_on_control_source", + &pdata->wpc_temp_lcd_on_control_source); + if (ret) { + pr_info("%s : wpc_temp_lcd_on_control_source is Empty\n", __func__); + pdata->wpc_temp_lcd_on_control_source = TEMP_CONTROL_SOURCE_CHG_THM; + } + + ret = of_property_read_u32(np, "battery,wpc_high_temp", + &pdata->wpc_high_temp); + if (ret) + pr_info("%s : wpc_high_temp is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_high_temp_recovery", + &pdata->wpc_high_temp_recovery); + if (ret) + pr_info("%s : wpc_high_temp_recovery is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_charging_limit_current", + &pdata->wpc_charging_limit_current); + if (ret) + pr_info("%s : wpc_charging_limit_current is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_lcd_on_high_temp", + &pdata->wpc_lcd_on_high_temp); + if (ret) + pr_info("%s : wpc_lcd_on_high_temp is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_lcd_on_high_temp_rec", + &pdata->wpc_lcd_on_high_temp_rec); + if (ret) + pr_info("%s : wpc_lcd_on_high_temp_rec is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_lcd_on_charging_limit_current", + &pdata->wpc_lcd_on_charging_limit_current); + if (ret) { + pr_info("%s : wpc_lcd_on_charging_limit_current is Empty\n", __func__); + pdata->wpc_lcd_on_charging_limit_current = + pdata->wpc_charging_limit_current; + } + } + + ret = of_property_read_u32(np, "battery,wc_full_input_limit_current", + &pdata->wc_full_input_limit_current); + if (ret) + pr_info("%s : wc_full_input_limit_current is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wc_cv_current", + &pdata->wc_cv_current); + if (ret) + pr_info("%s : wc_cv_current is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wc_cv_pack_current", + &pdata->wc_cv_pack_current); + if (ret) { + pr_info("%s : wc_cv_pack_current is Empty\n", __func__); + pdata->wc_cv_pack_current = 500; + } + + ret = of_property_read_u32(np, "battery,wc_hero_stand_cc_cv", + &pdata->wc_hero_stand_cc_cv); + if (ret) { + pr_info("%s : wc_hero_stand_cc_cv is Empty\n", __func__); + pdata->wc_hero_stand_cc_cv = 70; + } + ret = of_property_read_u32(np, "battery,wc_hero_stand_cv_current", + &pdata->wc_hero_stand_cv_current); + if (ret) { + pr_info("%s : wc_hero_stand_cv_current is Empty\n", __func__); + pdata->wc_hero_stand_cv_current = 600; + } + ret = of_property_read_u32(np, "battery,wc_hero_stand_hv_cv_current", + &pdata->wc_hero_stand_hv_cv_current); + if (ret) { + pr_info("%s : wc_hero_stand_hv_cv_current is Empty\n", __func__); + pdata->wc_hero_stand_hv_cv_current = 450; + } + + ret = of_property_read_u32(np, "battery,sleep_mode_limit_current", + &pdata->sleep_mode_limit_current); + if (ret) + pr_info("%s : sleep_mode_limit_current is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,inbat_voltage", + &pdata->inbat_voltage); + if (ret) + pr_info("%s : inbat_voltage is Empty\n", __func__); + + if (pdata->inbat_voltage) { + p = of_get_property(np, "battery,inbat_voltage_table_adc", &len); + if (!p) + return 1; + + len = len / sizeof(u32); + + pdata->inbat_adc_table_size = len; + + pdata->inbat_adc_table = + kzalloc(sizeof(sec_bat_adc_table_data_t) * + pdata->inbat_adc_table_size, GFP_KERNEL); + + for(i = 0; i < pdata->inbat_adc_table_size; i++) { + ret = of_property_read_u32_index(np, + "battery,inbat_voltage_table_adc", i, &temp); + pdata->inbat_adc_table[i].adc = (int)temp; + if (ret) + pr_info("%s : inbat_adc_table(adc) is Empty\n", + __func__); + + ret = of_property_read_u32_index(np, + "battery,inbat_voltage_table_data", i, &temp); + pdata->inbat_adc_table[i].data = (int)temp; + if (ret) + pr_info("%s : inbat_adc_table(data) is Empty\n", + __func__); + } + } + + ret = of_property_read_u32(np, "battery,pre_afc_input_current", + &pdata->pre_afc_input_current); + if (ret) { + pr_info("%s : pre_afc_input_current is Empty\n", __func__); + pdata->pre_afc_input_current = 1000; + } + + ret = of_property_read_u32(np, "battery,pre_afc_work_delay", + &pdata->pre_afc_work_delay); + if (ret) { + pr_info("%s : pre_afc_work_delay is Empty\n", __func__); + pdata->pre_afc_work_delay = 2000; + } + + ret = of_property_read_u32(np, "battery,pre_wc_afc_input_current", + &pdata->pre_wc_afc_input_current); + if (ret) { + pr_info("%s : pre_wc_afc_input_current is Empty\n", __func__); + pdata->pre_wc_afc_input_current = 500; /* wc input default */ + } + + ret = of_property_read_u32(np, "battery,pre_wc_afc_work_delay", + &pdata->pre_wc_afc_work_delay); + if (ret) { + pr_info("%s : pre_wc_afc_work_delay is Empty\n", __func__); + pdata->pre_wc_afc_work_delay = 4000; + } + + ret = of_property_read_u32(np, "battery,adc_check_count", + &pdata->adc_check_count); + if (ret) + pr_info("%s : Adc check count is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_adc_type", + &pdata->temp_adc_type); + if (ret) + pr_info("%s : Temp adc type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_adc_channel", + &pdata->temp_adc_channel); + if (ret) { + pr_info("%s : Temp adc channel is Empty\n", __func__); + pdata->temp_adc_channel = 0x51; /* VADC_AMUX_THM5_PU2 */ + } + + ret = of_property_read_u32(np, "battery,chg_temp_adc_channel", + &pdata->chg_temp_adc_channel); + if (ret) { + pr_info("%s : CHG Temp adc channel is Empty\n", __func__); + pdata->chg_temp_adc_channel = 0x4e; /* VADC_AMUX_THM2_PU2 */ + } + + ret = of_property_read_u32(np, "battery,usb_temp_adc_channel", + &pdata->usb_temp_adc_channel); + if (ret) { + pr_info("%s : USB Temp adc channel is Empty\n", __func__); + pdata->usb_temp_adc_channel = 0x56; /* VADC_AMUX5_GPIO_PU2 */ + } + + ret = of_property_read_u32(np, "battery,blkt_temp_adc_channel", + &pdata->blkt_temp_adc_channel); + if (ret) { + pr_info("%s : BLKT Temp adc channel is Empty\n", __func__); + pdata->blkt_temp_adc_channel = 0x4d; /* VADC_AMUX_THM1_PU2 */ + } + + ret = of_property_read_u32(np, "battery,cable_check_type", + &pdata->cable_check_type); + if (ret) + pr_info("%s : Cable check type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,cable_source_type", + &pdata->cable_source_type); + if (ret) + pr_info("%s: Cable_source_type is Empty\n", __func__); +#if defined(CONFIG_CHARGING_VZWCONCEPT) + pdata->cable_check_type &= ~SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE; + pdata->cable_check_type |= SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE; +#endif + ret = of_property_read_u32(np, "battery,polling_type", + &pdata->polling_type); + if (ret) + pr_info("%s : Polling type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,monitor_initial_count", + &pdata->monitor_initial_count); + if (ret) + pr_info("%s : Monitor initial count is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,battery_check_type", + &pdata->battery_check_type); + if (ret) + pr_info("%s : Battery check type is Empty\n", __func__); + + if (pdata->battery_check_type == SEC_BATTERY_CHECK_ADC) { + ret = of_property_read_u32(np, "battery,batt_channel", + &pdata->batt_channel); + if (ret) { + pr_info("%s : Mux Channel is Empty.\n", __func__); + pdata->batt_channel = 0; + } + } + + ret = of_property_read_u32(np, "battery,check_count", + &pdata->check_count); + if (ret) + pr_info("%s : Check count is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,check_adc_max", + &pdata->check_adc_max); + if (ret) + pr_info("%s : Check adc max is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,check_adc_min", + &pdata->check_adc_min); + if (ret) + pr_info("%s : Check adc min is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,ovp_uvlo_check_type", + &pdata->ovp_uvlo_check_type); + if (ret) + pr_info("%s : Ovp Uvlo check type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_check_type", + &pdata->temp_check_type); + if (ret) + pr_info("%s : Temp check type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_check_count", + &pdata->temp_check_count); + if (ret) + pr_info("%s : Temp check count is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_highlimit_threshold_normal", + &temp); + pdata->temp_highlimit_threshold_normal = (int)temp; + if (ret) + pr_info("%s : Temp highlimit threshold normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_highlimit_recovery_normal", + &temp); + pdata->temp_highlimit_recovery_normal = (int)temp; + if (ret) + pr_info("%s : Temp highlimit recovery normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_high_threshold_normal", + &temp); + pdata->temp_high_threshold_normal = (int)temp; + if (ret) + pr_info("%s : Temp high threshold normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_high_recovery_normal", + &temp); + pdata->temp_high_recovery_normal = (int)temp; + if (ret) + pr_info("%s : Temp high recovery normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_low_threshold_normal", + &temp); + pdata->temp_low_threshold_normal = (int)temp; + if (ret) + pr_info("%s : Temp low threshold normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_low_recovery_normal", + &temp); + pdata->temp_low_recovery_normal = (int)temp; + if (ret) + pr_info("%s : Temp low recovery normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_highlimit_threshold_lpm", + &temp); + pdata->temp_highlimit_threshold_lpm = (int)temp; + if (ret) + pr_info("%s : Temp highlimit threshold lpm is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_highlimit_recovery_lpm", + &temp); + pdata->temp_highlimit_recovery_lpm = (int)temp; + if (ret) + pr_info("%s : Temp highlimit recovery lpm is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_high_threshold_lpm", + &temp); + pdata->temp_high_threshold_lpm = (int)temp; + if (ret) + pr_info("%s : Temp high threshold lpm is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_high_recovery_lpm", + &temp); + pdata->temp_high_recovery_lpm = (int)temp; + if (ret) + pr_info("%s : Temp high recovery lpm is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_low_threshold_lpm", + &temp); + pdata->temp_low_threshold_lpm = (int)temp; + if (ret) + pr_info("%s : Temp low threshold lpm is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,temp_low_recovery_lpm", + &temp); + pdata->temp_low_recovery_lpm = (int)temp; + if (ret) + pr_info("%s : Temp low recovery lpm is Empty\n", __func__); + + pr_info("%s : HIGHLIMIT_THRESHOLD_NOLMAL(%d), HIGHLIMIT_RECOVERY_NORMAL(%d)\n" + "HIGH_THRESHOLD_NORMAL(%d), HIGH_RECOVERY_NORMAL(%d) LOW_THRESHOLD_NORMAL(%d), LOW_RECOVERY_NORMAL(%d)\n" + "HIGHLIMIT_THRESHOLD_LPM(%d), HIGHLIMIT_RECOVERY_LPM(%d)\n" + "HIGH_THRESHOLD_LPM(%d), HIGH_RECOVERY_LPM(%d) LOW_THRESHOLD_LPM(%d), LOW_RECOVERY_LPM(%d)\n", + __func__, + pdata->temp_highlimit_threshold_normal, pdata->temp_highlimit_recovery_normal, + pdata->temp_high_threshold_normal, pdata->temp_high_recovery_normal, + pdata->temp_low_threshold_normal, pdata->temp_low_recovery_normal, + pdata->temp_highlimit_threshold_lpm, pdata->temp_highlimit_recovery_lpm, + pdata->temp_high_threshold_lpm, pdata->temp_high_recovery_lpm, + pdata->temp_low_threshold_lpm, pdata->temp_low_recovery_lpm); + + ret = of_property_read_u32(np, "battery,wpc_high_threshold_normal", + &temp); + pdata->wpc_high_threshold_normal = (int)temp; + if (ret) + pr_info("%s : wpc_high_threshold_normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_high_recovery_normal", + &temp); + pdata->wpc_high_recovery_normal = (int)temp; + if (ret) + pr_info("%s : wpc_high_recovery_normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_low_threshold_normal", + &temp); + pdata->wpc_low_threshold_normal = (int)temp; + if (ret) + pr_info("%s : wpc_low_threshold_normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,wpc_low_recovery_normal", + &temp); + pdata->wpc_low_recovery_normal = (int)temp; + if (ret) + pr_info("%s : wpc_low_recovery_normal is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,full_check_type", + &pdata->full_check_type); + if (ret) + pr_info("%s : Full check type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,full_check_type_2nd", + &pdata->full_check_type_2nd); + if (ret) + pr_info("%s : Full check type 2nd is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,full_check_count", + &pdata->full_check_count); + if (ret) + pr_info("%s : Full check count is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_gpio_full_check", + &pdata->chg_gpio_full_check); + if (ret) + pr_info("%s : Chg gpio full check is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_polarity_full_check", + &pdata->chg_polarity_full_check); + if (ret) + pr_info("%s : Chg polarity full check is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,full_condition_type", + &pdata->full_condition_type); + if (ret) + pr_info("%s : Full condition type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,full_condition_soc", + &pdata->full_condition_soc); + if (ret) + pr_info("%s : Full condition soc is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,full_condition_vcell", + &pdata->full_condition_vcell); + if (ret) + pr_info("%s : Full condition vcell is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,recharge_check_count", + &pdata->recharge_check_count); + if (ret) + pr_info("%s : Recharge check count is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,recharge_condition_type", + &pdata->recharge_condition_type); + if (ret) + pr_info("%s : Recharge condition type is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,recharge_condition_soc", + &pdata->recharge_condition_soc); + if (ret) + pr_info("%s : Recharge condition soc is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,recharge_condition_vcell", + &pdata->recharge_condition_vcell); + if (ret) + pr_info("%s : Recharge condition vcell is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,charging_reset_time", + (unsigned int *)&pdata->charging_reset_time); + if (ret) + pr_info("%s : Charging reset time is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_float_voltage", + (unsigned int *)&pdata->chg_float_voltage); + if (ret) { + pr_info("%s: chg_float_voltage is Empty\n", __func__); + pdata->chg_float_voltage = 43500; + } + + pdata->is_b2b_model = of_property_read_bool(np, + "battery,is_b2b_model"); + + ret = of_property_read_u32(np, "battery,chg_float_voltage_conv", + &pdata->chg_float_voltage_conv); + if (ret) { + pr_info("%s: chg_float_voltage_conv is Empty\n", __func__); + pdata->chg_float_voltage_conv = 1; + } +#if defined(CONFIG_BATTERY_SWELLING) + ret = of_property_read_u32(np, "battery,chg_float_voltage", + (unsigned int *)&pdata->swelling_normal_float_voltage); + if (ret) + pr_info("%s: chg_float_voltage is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_high_temp_block", + &temp); + pdata->swelling_high_temp_block = (int)temp; + if (ret) + pr_info("%s: swelling high temp block is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_high_temp_recov", + &temp); + pdata->swelling_high_temp_recov = (int)temp; + if (ret) + pr_info("%s: swelling high temp recovery is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_low_temp_block_1st", + &temp); + pdata->swelling_low_temp_block_1st = (int)temp; + if (ret) + pr_info("%s: swelling low temp block is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_low_temp_recov_1st", + &temp); + pdata->swelling_low_temp_recov_1st = (int)temp; + if (ret) + pr_info("%s: swelling low temp recovery is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_low_temp_block_2nd", + &temp); + pdata->swelling_low_temp_block_2nd = (int)temp; + if (ret) + pr_info("%s: swelling low temp block is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_low_temp_recov_2nd", + &temp); + pdata->swelling_low_temp_recov_2nd = (int)temp; + if (ret) + pr_info("%s: swelling low temp recovery 2nd is Empty\n", __func__); + + ret = of_property_read_u32(np, "battery,swelling_low_temp_current", + &pdata->swelling_low_temp_current); + if (ret) { + pr_info("%s: swelling_low_temp_current is Empty, Defualt value 600mA \n", __func__); + pdata->swelling_low_temp_current = 600; + } + + ret = of_property_read_u32(np, "battery,swelling_low_temp_topoff", + &pdata->swelling_low_temp_topoff); + if (ret) { + pr_info("%s: swelling_low_temp_topoff is Empty, Defualt value 200mA \n", __func__); + pdata->swelling_low_temp_topoff = 200; + } + + ret = of_property_read_u32(np, "battery,swelling_high_temp_current", + &pdata->swelling_high_temp_current); + if (ret) { + pr_info("%s: swelling_high_temp_current is Empty, Defualt value 1300mA \n", __func__); + pdata->swelling_high_temp_current = 1300; + } + + ret = of_property_read_u32(np, "battery,swelling_high_temp_topoff", + &pdata->swelling_high_temp_topoff); + if (ret) { + pr_info("%s: swelling_high_temp_topoff is Empty, Defualt value 200mA \n", __func__); + pdata->swelling_high_temp_topoff = 200; + } + + ret = of_property_read_u32(np, "battery,swelling_wc_high_temp_current", + &pdata->swelling_wc_high_temp_current); + if (ret) { + pr_info("%s: swelling_wc_high_temp_current is Empty, Defualt value 600mA \n", __func__); + pdata->swelling_wc_high_temp_current = 600; + } + + ret = of_property_read_u32(np, "battery,swelling_wc_low_temp_current", + &pdata->swelling_wc_low_temp_current); + if (ret) { + pr_info("%s: swelling_wc_low_temp_current is Empty, Defualt value 600mA \n", __func__); + pdata->swelling_wc_low_temp_current = 600; + } + + ret = of_property_read_u32(np, "battery,swelling_drop_float_voltage", + (unsigned int *)&pdata->swelling_drop_float_voltage); + if (ret) { + pr_info("%s: swelling drop float voltage is Empty, Default value 4250mV \n", __func__); + pdata->swelling_drop_float_voltage = 4250; + pdata->swelling_drop_voltage_condition = 4250; + } else { + pdata->swelling_drop_voltage_condition = (pdata->swelling_drop_float_voltage > 10000) ? + (pdata->swelling_drop_float_voltage / 10) : (pdata->swelling_drop_float_voltage); + pr_info("%s : swelling drop voltage(set : %d, condition : %d)\n", __func__, + pdata->swelling_drop_float_voltage, pdata->swelling_drop_voltage_condition); + } + + ret = of_property_read_u32(np, "battery,swelling_high_rechg_voltage", + (unsigned int *)&pdata->swelling_high_rechg_voltage); + if (ret) { + pr_info("%s: swelling_high_rechg_voltage is Empty\n", __func__); + pdata->swelling_high_rechg_voltage = 4150; + } + + ret = of_property_read_u32(np, "battery,swelling_low_rechg_voltage", + (unsigned int *)&pdata->swelling_low_rechg_voltage); + if (ret) { + pr_info("%s: swelling_low_rechg_voltage is Empty\n", __func__); + pdata->swelling_low_rechg_voltage = 4000; + } + + pr_info("%s : SWELLING_HIGH_TEMP(%d) SWELLING_HIGH_TEMP_RECOVERY(%d)\n" + "SWELLING_LOW_TEMP_1st(%d) SWELLING_LOW_TEMP_RECOVERY_1st(%d) " + "SWELLING_LOW_TEMP_2nd(%d) SWELLING_LOW_TEMP_RECOVERY_2nd(%d) " + "SWELLING_LOW_CURRENT(%d, %d), SWELLING_HIGH_CURRENT(%d, %d)\n" + "SWELLING_LOW_RCHG_VOL(%d), SWELLING_HIGH_RCHG_VOL(%d)\n", + __func__, pdata->swelling_high_temp_block, pdata->swelling_high_temp_recov, + pdata->swelling_low_temp_block_1st, pdata->swelling_low_temp_recov_1st, + pdata->swelling_low_temp_block_2nd, pdata->swelling_low_temp_recov_2nd, + pdata->swelling_low_temp_current, pdata->swelling_low_temp_topoff, + pdata->swelling_high_temp_current, pdata->swelling_high_temp_topoff, + pdata->swelling_low_rechg_voltage, pdata->swelling_high_rechg_voltage); +#endif + +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + /* wpc_det */ + ret = pdata->wpc_det = of_get_named_gpio(np, "battery,wpc_det", 0); + if (ret < 0) { + pr_info("%s : can't get wpc_det\n", __func__); + } +#endif + + /* wpc_en */ + ret = pdata->wpc_en = of_get_named_gpio(np, "battery,wpc_en", 0); + if (ret < 0) { + pr_info("%s : can't get wpc_en\n", __func__); + pdata->wpc_en = 0; + } +#if defined(CONFIG_BATTERY_AGE_FORECAST) + p = of_get_property(np, "battery,age_data", &len); + if (p) { + battery->pdata->num_age_step = len / sizeof(sec_age_data_t); + battery->pdata->age_data = kzalloc(len, GFP_KERNEL); + ret = of_property_read_u32_array(np, "battery,age_data", + (u32 *)battery->pdata->age_data, len/sizeof(u32)); + if (ret) { + pr_err("%s failed to read battery->pdata->age_data: %d\n", + __func__, ret); + kfree(battery->pdata->age_data); + battery->pdata->age_data = NULL; + battery->pdata->num_age_step = 0; + } + pr_err("%s num_age_step : %d\n", __func__, battery->pdata->num_age_step); + for (len = 0; len < battery->pdata->num_age_step; ++len) { + pr_err("[%d/%d]cycle:%d, float:%d, full_v:%d, recharge_v:%d, soc:%d\n", + len, battery->pdata->num_age_step-1, + battery->pdata->age_data[len].cycle, + battery->pdata->age_data[len].float_voltage, + battery->pdata->age_data[len].full_condition_vcell, + battery->pdata->age_data[len].recharge_condition_vcell, + battery->pdata->age_data[len].full_condition_soc); + } + } else { + battery->pdata->num_age_step = 0; + pr_err("%s there is not age_data\n", __func__); + } +#endif + + ret = of_property_read_u32(np, "battery,siop_event_check_type", + &pdata->siop_event_check_type); + ret = of_property_read_u32(np, "battery,siop_call_cc_current", + &pdata->siop_call_cc_current); + ret = of_property_read_u32(np, "battery,siop_call_cv_current", + &pdata->siop_call_cv_current); + + ret = of_property_read_u32(np, "battery,siop_input_limit_current", + &pdata->siop_input_limit_current); + if (ret) + pdata->siop_input_limit_current = SIOP_INPUT_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_charging_limit_current", + &pdata->siop_charging_limit_current); + if (ret) + pdata->siop_charging_limit_current = SIOP_CHARGING_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_hv_12v_input_limit_current", + &pdata->siop_hv_12v_input_limit_current); + if (ret) + pdata->siop_hv_12v_input_limit_current = SIOP_HV_12V_INPUT_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_hv_12v_charging_limit_current", + &pdata->siop_hv_12v_charging_limit_current); + if (ret) + pdata->siop_hv_12v_charging_limit_current = SIOP_HV_12V_CHARGING_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_hv_input_limit_current", + &pdata->siop_hv_input_limit_current); + if (ret) + pdata->siop_hv_input_limit_current = SIOP_HV_INPUT_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_hv_charging_limit_current", + &pdata->siop_hv_charging_limit_current); + if (ret) + pdata->siop_hv_charging_limit_current = SIOP_HV_CHARGING_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_wireless_input_limit_current", + &pdata->siop_wireless_input_limit_current); + if (ret) + pdata->siop_wireless_input_limit_current = SIOP_WIRELESS_INPUT_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_wireless_charging_limit_current", + &pdata->siop_wireless_charging_limit_current); + if (ret) + pdata->siop_wireless_charging_limit_current = SIOP_WIRELESS_CHARGING_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_hv_wireless_input_limit_current", + &pdata->siop_hv_wireless_input_limit_current); + if (ret) + pdata->siop_hv_wireless_input_limit_current = SIOP_HV_WIRELESS_INPUT_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,siop_hv_wireless_charging_limit_current", + &pdata->siop_hv_wireless_charging_limit_current); + if (ret) + pdata->siop_hv_wireless_charging_limit_current = SIOP_HV_WIRELESS_CHARGING_LIMIT_CURRENT; + + ret = of_property_read_u32(np, "battery,max_input_voltage", + &pdata->max_input_voltage); + if (ret) + pdata->max_input_voltage = 12000; + + ret = of_property_read_u32(np, "battery,max_input_current", + &pdata->max_input_current); + if (ret) + pdata->max_input_current = 3000; + + ret = of_property_read_u32(np, "battery,pd_charging_charge_power", + &pdata->pd_charging_charge_power); + if (ret) { + pr_err("%s: pd_charging_charge_power is Empty\n", __func__); + pdata->pd_charging_charge_power = 15000; + } + + ret = of_property_read_u32(np, "battery,nv_charge_power", + &pdata->nv_charge_power); + if (ret) { + pr_err("%s: nv_charge_power is Empty\n", __func__); + pdata->nv_charge_power = + SEC_INPUT_VOLTAGE_5V * pdata->default_input_current; + } + ret = of_property_read_u32(np, "battery,siop_max_charging_current_0", + &pdata->siop_max_charging_current_0); + if (ret) { + pr_err("%s: siop_max_charging_current_0 is Empty\n", __func__); + pdata->siop_max_charging_current_0 = 0; + } + ret = of_property_read_u32(np, "battery,siop_max_charging_current_10", + &pdata->siop_max_charging_current_10); + if (ret) { + pr_err("%s: siop_max_charging_current_10 is Empty\n", __func__); + pdata->siop_max_charging_current_10 = 500; + } + ret = of_property_read_u32(np, "battery,siop_max_charging_current_70", + &pdata->siop_max_charging_current_70); + if (ret) { + pr_err("%s: siop_max_charging_current_70 is Empty\n", __func__); + pdata->siop_max_charging_current_70 = 1800; + } + ret = of_property_read_u32(np, "battery,siop_max_input_power", + &pdata->siop_max_input_power); + if (ret) { + pr_err("%s: siop_max_input_power is Empty\n", __func__); + pdata->siop_max_input_power = 6000; + } + + pr_info("%s: vendor : %s, technology : %d, cable_check_type : %d\n" + "cable_source_type : %d, event_waiting_time : %d\n" + "polling_type : %d, initial_count : %d, check_count : %d\n" + "check_adc_max : %d, check_adc_min : %d\n" + "ovp_uvlo_check_type : %d, thermal_source : %d\n" + "temp_check_type : %d, temp_check_count : %d, nv_charge_power : %d\n", + __func__, + pdata->vendor, pdata->technology,pdata->cable_check_type, + pdata->cable_source_type, pdata->event_waiting_time, + pdata->polling_type, pdata->monitor_initial_count, + pdata->check_count, pdata->check_adc_max, pdata->check_adc_min, + pdata->ovp_uvlo_check_type, pdata->thermal_source, + pdata->temp_check_type, pdata->temp_check_count, pdata->nv_charge_power); + + ret = of_property_read_u32(np, "battery,max_charging_current", + &pdata->max_charging_current); + if (ret) { + pr_err("%s: max_charging_current is Empty\n", __func__); + pdata->max_charging_current = 3000; + } + +#if defined(CONFIG_BATTERY_CISD) + p = of_get_property(np, "battery,ignore_cisd_index", &len); + pdata->ignore_cisd_index = kzalloc(sizeof(*pdata->ignore_cisd_index) * 2, GFP_KERNEL); + if (p) { + len = len / sizeof(u32); + ret = of_property_read_u32_array(np, "battery,ignore_cisd_index", + pdata->ignore_cisd_index, len); + } else { + pr_info("%s : battery,ignore_cisd_index is Empty\n", __func__); + } + + p = of_get_property(np, "battery,ignore_cisd_index_d", &len); + pdata->ignore_cisd_index_d = kzalloc(sizeof(*pdata->ignore_cisd_index_d) * 2, GFP_KERNEL); + if (p) { + len = len / sizeof(u32); + ret = of_property_read_u32_array(np, "battery,ignore_cisd_index_d", + pdata->ignore_cisd_index_d, len); + } else { + pr_info("%s : battery,ignore_cisd_index_d is Empty\n", __func__); + } +#endif + + return 0; +} + +static void sec_bat_parse_mode_dt(struct sec_battery_info *battery) +{ + struct device_node *np; + sec_battery_platform_data_t *pdata = battery->pdata; + int ret = 0; + u32 temp = 0; + + np = of_find_node_by_name(NULL, "battery"); + if (!np) { + pr_err("%s np NULL\n", __func__); + return; + } + + if (battery->store_mode) { + ret = of_property_read_u32(np, "battery,store_mode_afc_input_current", + &pdata->store_mode_afc_input_current); + if (ret) { + pr_info("%s : store_mode_afc_input_current is Empty\n", __func__); + pdata->store_mode_afc_input_current = 440; + } + + ret = of_property_read_u32(np, "battery,store_mode_hv_wireless_input_current", + &pdata->store_mode_hv_wireless_input_current); + if (ret) { + pr_info("%s : store_mode_hv_wireless_input_current is Empty\n", __func__); + pdata->store_mode_hv_wireless_input_current = 400; + } + + if (pdata->wpc_temp_check) { + ret = of_property_read_u32(np, "battery,wpc_store_high_temp", + &temp); + if (!ret) + pdata->wpc_high_temp = temp; + + ret = of_property_read_u32(np, "battery,wpc_store_high_temp_recovery", + &temp); + if (!ret) + pdata->wpc_high_temp_recovery = temp; + + ret = of_property_read_u32(np, "battery,wpc_store_charging_limit_current", + &temp); + if (!ret) + pdata->wpc_charging_limit_current = temp; + + ret = of_property_read_u32(np, "battery,wpc_store_lcd_on_high_temp", + &temp); + if (!ret) + pdata->wpc_lcd_on_high_temp = (int)temp; + + ret = of_property_read_u32(np, "battery,wpc_store_lcd_on_high_temp_rec", + &temp); + if (!ret) + pdata->wpc_lcd_on_high_temp_rec = (int)temp; + + pr_info("%s: update store_mode - wpc high_temp(t:%d, r:%d), lcd_on_high_temp(t:%d, r:%d), curr(%d)\n", + __func__, + pdata->wpc_high_temp, pdata->wpc_high_temp_recovery, + pdata->wpc_lcd_on_high_temp, pdata->wpc_lcd_on_high_temp_rec, + pdata->wpc_charging_limit_current); + } + + ret = of_property_read_u32(np, "battery,siop_store_hv_wireless_input_limit_current", + &temp); + if (!ret) + pdata->siop_hv_wireless_input_limit_current = temp; + else + pdata->siop_hv_wireless_input_limit_current = SIOP_STORE_HV_WIRELESS_CHARGING_LIMIT_CURRENT; + pr_info("%s: update siop_hv_wireless_input_limit_current(%d)\n", + __func__, pdata->siop_hv_wireless_input_limit_current); + } +} + +static void sec_bat_parse_mode_dt_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, parse_mode_dt_work.work); + + sec_bat_parse_mode_dt(battery); + + if (is_hv_wire_type(battery->cable_type) || + is_hv_wireless_type(battery->cable_type)) { + sec_bat_set_charging_current(battery, 0); + } + + wake_unlock(&battery->parse_mode_dt_wake_lock); +} +#endif + + +#if !defined(CONFIG_MUIC_NOTIFIER) +void cable_initial_check(struct sec_battery_info *battery) +{ + union power_supply_propval value; + + pr_info("%s : current_cable_type : (%d)\n", __func__, battery->cable_type); + + if (SEC_BATTERY_CABLE_NONE != battery->cable_type) { + if (battery->cable_type == SEC_BATTERY_CABLE_POWER_SHARING) { + value.intval = battery->cable_type; + psy_do_property("ps", set, + POWER_SUPPLY_PROP_ONLINE, value); + } else { + value.intval = battery->cable_type; + psy_do_property("battery", set, + POWER_SUPPLY_PROP_ONLINE, value); + } + } else { + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_ONLINE, value); + if (value.intval == SEC_BATTERY_CABLE_WIRELESS) { + value.intval = 1; + psy_do_property("wireless", set, + POWER_SUPPLY_PROP_ONLINE, value); + } + } +} +#endif + +static void sec_bat_init_chg_work(struct work_struct *work) +{ + struct sec_battery_info *battery = container_of(work, + struct sec_battery_info, init_chg_work.work); + + if (battery->cable_type == SEC_BATTERY_CABLE_NONE && + !(battery->misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE | + BATT_MISC_EVENT_HICCUP_TYPE))) { + pr_info("%s: disable charging\n", __func__); + sec_bat_set_charge(battery, SEC_BATTERY_CHARGING_NONE); + } +} + +static const struct power_supply_desc battery_power_supply_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = sec_battery_props, + .num_properties = ARRAY_SIZE(sec_battery_props), + .get_property = sec_bat_get_property, + .set_property = sec_bat_set_property, +}; + +static const struct power_supply_desc usb_power_supply_desc = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = sec_power_props, + .num_properties = ARRAY_SIZE(sec_power_props), + .get_property = sec_usb_get_property, +}; + +static const struct power_supply_desc ac_power_supply_desc = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = sec_ac_props, + .num_properties = ARRAY_SIZE(sec_ac_props), + .get_property = sec_ac_get_property, +}; + +static const struct power_supply_desc wireless_power_supply_desc = { + .name = "wireless", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .properties = sec_wireless_props, + .num_properties = ARRAY_SIZE(sec_wireless_props), + .get_property = sec_wireless_get_property, + .set_property = sec_wireless_set_property, +}; + +static const struct power_supply_desc ps_power_supply_desc = { + .name = "ps", + .type = POWER_SUPPLY_TYPE_POWER_SHARING, + .properties = sec_ps_props, + .num_properties = ARRAY_SIZE(sec_ps_props), + .get_property = sec_ps_get_property, + .set_property = sec_ps_set_property, +}; + +#if defined(CONFIG_USE_POGO) +static const struct power_supply_desc pogo_power_supply_desc = { + .name = "pogo", + .type = POWER_SUPPLY_TYPE_POGO, + .properties = sec_power_props, + .num_properties = ARRAY_SIZE(sec_power_props), + .get_property = sec_pogo_get_property, + .set_property = sec_pogo_set_property, +}; +#endif + +static int sec_battery_probe(struct platform_device *pdev) +{ + sec_battery_platform_data_t *pdata = NULL; + struct sec_battery_info *battery; + struct power_supply_config battery_cfg = {}; + + int ret = 0; +#ifndef CONFIG_OF + int i = 0; +#endif + + union power_supply_propval value = {0, }; + + dev_info(&pdev->dev, + "%s: SEC Battery Driver Loading\n", __func__); + + battery = kzalloc(sizeof(*battery), GFP_KERNEL); + if (!battery) + return -ENOMEM; + + if (pdev->dev.of_node) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(sec_battery_platform_data_t), + GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_bat_free; + } + + battery->pdata = pdata; + + if (sec_bat_parse_dt(&pdev->dev, battery)) { + dev_err(&pdev->dev, + "%s: Failed to get battery dt\n", __func__); + ret = -EINVAL; + goto err_bat_free; + } + } else { + pdata = dev_get_platdata(&pdev->dev); + battery->pdata = pdata; + } + + platform_set_drvdata(pdev, battery); + + battery->dev = &pdev->dev; + + mutex_init(&battery->adclock); + mutex_init(&battery->iolock); + mutex_init(&battery->misclock); + mutex_init(&battery->batt_handlelock); + mutex_init(&battery->current_eventlock); + mutex_init(&battery->typec_notylock); + mutex_init(&battery->wclock); + + dev_dbg(battery->dev, "%s: ADC init\n", __func__); + +#ifdef CONFIG_OF + adc_init(pdev, battery); +#else + for (i = 0; i < SEC_BAT_ADC_CHANNEL_NUM; i++) + adc_init(pdev, pdata, i); +#endif + wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-monitor"); + wake_lock_init(&battery->cable_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-cable"); + wake_lock_init(&battery->vbus_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-vbus"); + wake_lock_init(&battery->afc_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-afc"); + wake_lock_init(&battery->siop_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-siop"); + wake_lock_init(&battery->siop_level_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-siop_level"); + wake_lock_init(&battery->siop_event_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-siop_event"); + wake_lock_init(&battery->wc_headroom_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-wc_headroom"); +#if defined(CONFIG_UPDATE_BATTERY_DATA) + wake_lock_init(&battery->batt_data_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-update-data"); +#endif + wake_lock_init(&battery->misc_event_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-misc-event"); +#ifdef CONFIG_OF + wake_lock_init(&battery->parse_mode_dt_wake_lock, WAKE_LOCK_SUSPEND, + "sec-battery-parse_mode_dt"); +#endif + + /* initialization of battery info */ + sec_bat_set_charging_status(battery, + POWER_SUPPLY_STATUS_DISCHARGING); + battery->health = POWER_SUPPLY_HEALTH_GOOD; + battery->present = true; + battery->is_jig_on = false; + battery->wdt_kick_disable = 0; + + battery->polling_count = 1; /* initial value = 1 */ + battery->polling_time = pdata->polling_time[ + SEC_BATTERY_POLLING_TIME_DISCHARGING]; + battery->polling_in_sleep = false; + battery->polling_short = false; + + battery->check_count = 0; + battery->check_adc_count = 0; + battery->check_adc_value = 0; + + battery->input_current = 0; + battery->charging_current = 0; + battery->topoff_current = 0; + battery->wpc_vout_level = WIRELESS_VOUT_10V; + battery->charging_start_time = 0; + battery->charging_passed_time = 0; + battery->wc_heating_start_time = 0; + battery->wc_heating_passed_time = 0; + battery->charging_next_time = 0; + battery->charging_fullcharged_time = 0; + battery->siop_level = 100; + battery->siop_event = 0; + battery->wc_enable = 1; + battery->wc_enable_cnt = 0; + battery->wc_enable_cnt_value = 3; +#if defined(CONFIG_ENG_BATTERY_CONCEPT) + battery->stability_test = 0; + battery->eng_not_full_status = 0; + battery->temperature_test_battery = 0x7FFF; + battery->temperature_test_usb = 0x7FFF; + battery->temperature_test_wpc = 0x7FFF; + battery->temperature_test_chg = 0x7FFF; + battery->temperature_test_blkt = 0x7FFF; +#endif + battery->ps_enable = false; + battery->wc_status = SEC_WIRELESS_PAD_NONE; + battery->wc_cv_mode = false; + battery->wire_status = SEC_BATTERY_CABLE_NONE; + +#if defined(CONFIG_BATTERY_SWELLING) + battery->swelling_mode = SWELLING_MODE_NONE; +#endif + battery->chg_limit = false; + battery->mix_limit = false; + battery->vbus_limit = false; + battery->vbus_chg_by_siop = false; + battery->vbus_chg_by_full = false; + battery->usb_temp = 0; +#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY) + battery->cooldown_mode = true; +#endif + battery->skip_swelling = false; + battery->led_cover = 0; + battery->hiccup_status = 0; + battery->update_pd_list = false; + + sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_100MA, SEC_BAT_CURRENT_EVENT_USB_100MA); + + if (lpcharge) { + battery->temp_highlimit_threshold = + battery->pdata->temp_highlimit_threshold_lpm; + battery->temp_highlimit_recovery = + battery->pdata->temp_highlimit_recovery_lpm; + battery->temp_high_threshold = + battery->pdata->temp_high_threshold_lpm; + battery->temp_high_recovery = + battery->pdata->temp_high_recovery_lpm; + battery->temp_low_recovery = + battery->pdata->temp_low_recovery_lpm; + battery->temp_low_threshold = + battery->pdata->temp_low_threshold_lpm; + } else { + battery->temp_highlimit_threshold = + battery->pdata->temp_highlimit_threshold_normal; + battery->temp_highlimit_recovery = + battery->pdata->temp_highlimit_recovery_normal; + battery->temp_high_threshold = + battery->pdata->temp_high_threshold_normal; + battery->temp_high_recovery = + battery->pdata->temp_high_recovery_normal; + battery->temp_low_recovery = + battery->pdata->temp_low_recovery_normal; + battery->temp_low_threshold = + battery->pdata->temp_low_threshold_normal; + } + + battery->charging_mode = SEC_BATTERY_CHARGING_NONE; + battery->is_recharging = false; + battery->cable_type = SEC_BATTERY_CABLE_NONE; + battery->test_mode = 0; + battery->factory_mode = false; + battery->store_mode = false; + battery->is_hc_usb = false; + battery->is_sysovlo = false; + battery->is_vbatovlo = false; + + battery->safety_timer_set = true; + battery->stop_timer = false; + battery->prev_safety_time = 0; + battery->lcd_status = false; + +#if defined(CONFIG_BATTERY_CISD) + battery->skip_cisd = false; + battery->usb_overheat_check = false; +#endif + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + battery->batt_cycle = -1; + battery->pdata->age_step = 0; +#endif + + battery->health_change = false; + + /* Check High Voltage charging option for wireless charging */ + /* '1' means disabling High Voltage charging */ + if(charging_night_mode == '1') + sleep_mode = true; + else + sleep_mode = false; + + /* Check High Voltage charging option for wired charging */ + if (get_afc_mode() == CH_MODE_AFC_DISABLE_VAL) { + pr_info("HV wired charging mode is disabled\n"); + sec_bat_set_current_event(battery, + SEC_BAT_CURRENT_EVENT_HV_DISABLE, SEC_BAT_CURRENT_EVENT_HV_DISABLE); + } + + battery->pdata->store_mode_charging_max = STORE_MODE_CHARGING_MAX; + battery->pdata->store_mode_charging_min = STORE_MODE_CHARGING_MIN; + +#if !defined(CONFIG_SEC_FACTORY) + if (sales_code_is("VZW")) { + dev_err(battery->dev, "%s: Sales is VZW\n", __func__); + battery->pdata->store_mode_charging_max = STORE_MODE_CHARGING_MAX_VZW; + battery->pdata->store_mode_charging_min = STORE_MODE_CHARGING_MIN_VZW; + } +#endif + + + if (battery->pdata->charger_name == NULL) + battery->pdata->charger_name = "sec-charger"; + if (battery->pdata->fuelgauge_name == NULL) + battery->pdata->fuelgauge_name = "sec-fuelgauge"; + + /* create work queue */ + battery->monitor_wqueue = + create_singlethread_workqueue(dev_name(&pdev->dev)); + if (!battery->monitor_wqueue) { + dev_err(battery->dev, + "%s: Fail to Create Workqueue\n", __func__); + goto err_irq; + } + ttf_init(battery); + + INIT_DELAYED_WORK(&battery->monitor_work, sec_bat_monitor_work); + INIT_DELAYED_WORK(&battery->cable_work, sec_bat_cable_work); +#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED) + INIT_DELAYED_WORK(&battery->slowcharging_work, sec_bat_check_slowcharging_work); +#endif + INIT_DELAYED_WORK(&battery->afc_work, sec_bat_afc_work); + INIT_DELAYED_WORK(&battery->siop_work, sec_bat_siop_work); + INIT_DELAYED_WORK(&battery->siop_event_work, sec_bat_siop_event_work); + INIT_DELAYED_WORK(&battery->siop_level_work, sec_bat_siop_level_work); + INIT_DELAYED_WORK(&battery->wc_headroom_work, sec_bat_wc_headroom_work); +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + INIT_DELAYED_WORK(&battery->fw_init_work, sec_bat_fw_init_work); +#endif +#if defined(CONFIG_UPDATE_BATTERY_DATA) + INIT_DELAYED_WORK(&battery->batt_data_work, sec_bat_update_data_work); +#endif + INIT_DELAYED_WORK(&battery->misc_event_work, sec_bat_misc_event_work); +#ifdef CONFIG_OF + INIT_DELAYED_WORK(&battery->parse_mode_dt_work, sec_bat_parse_mode_dt_work); +#endif + INIT_DELAYED_WORK(&battery->init_chg_work, sec_bat_init_chg_work); + + switch (pdata->polling_type) { + case SEC_BATTERY_MONITOR_WORKQUEUE: + INIT_DELAYED_WORK(&battery->polling_work, + sec_bat_polling_work); + break; + case SEC_BATTERY_MONITOR_ALARM: + battery->last_poll_time = ktime_get_boottime(); + alarm_init(&battery->polling_alarm, ALARM_BOOTTIME, + sec_bat_alarm); + break; + default: + break; + } + +#if defined(CONFIG_BATTERY_CISD) + sec_battery_cisd_init(battery); +#endif + + battery_cfg.drv_data = battery; + + /* init power supplier framework */ + battery->psy_ps = power_supply_register(&pdev->dev, &ps_power_supply_desc, &battery_cfg); + if (!battery->psy_ps) { + dev_err(battery->dev, + "%s: Failed to Register psy_ps\n", __func__); + goto err_workqueue; + } + battery->psy_ps->supplied_to = supply_list; + battery->psy_ps->num_supplicants = ARRAY_SIZE(supply_list); + + battery->psy_wireless = power_supply_register(&pdev->dev, &wireless_power_supply_desc, &battery_cfg); + if (!battery->psy_wireless) { + dev_err(battery->dev, + "%s: Failed to Register psy_wireless\n", __func__); + goto err_supply_unreg_ps; + } + battery->psy_wireless->supplied_to = supply_list; + battery->psy_wireless->num_supplicants = ARRAY_SIZE(supply_list); + + battery->psy_usb = power_supply_register(&pdev->dev, &usb_power_supply_desc, &battery_cfg); + if (!battery->psy_usb) { + dev_err(battery->dev, + "%s: Failed to Register psy_usb\n", __func__); + goto err_supply_unreg_wireless; + } + battery->psy_usb->supplied_to = supply_list; + battery->psy_usb->num_supplicants = ARRAY_SIZE(supply_list); + + battery->psy_ac = power_supply_register(&pdev->dev, &ac_power_supply_desc, &battery_cfg); + if (!battery->psy_ac) { + dev_err(battery->dev, + "%s: Failed to Register psy_ac\n", __func__); + goto err_supply_unreg_usb; + } + battery->psy_ac->supplied_to = supply_list; + battery->psy_ac->num_supplicants = ARRAY_SIZE(supply_list); + + battery->psy_bat = power_supply_register(&pdev->dev, &battery_power_supply_desc, &battery_cfg); + if (!battery->psy_bat) { + dev_err(battery->dev, + "%s: Failed to Register psy_bat\n", __func__); + goto err_supply_unreg_ac; + } + +#if defined(CONFIG_USE_POGO) + battery->psy_pogo = power_supply_register(&pdev->dev, &pogo_power_supply_desc, &battery_cfg); + if (!battery->psy_pogo) { + dev_err(battery->dev, + "%s: Failed to Register psy_pogo\n", __func__); + goto err_supply_unreg_pogo; + } +#endif + + + ret = sec_bat_create_attrs(&battery->psy_bat->dev); + if (ret) { + dev_err(battery->dev, + "%s : Failed to create_attrs\n", __func__); + goto err_req_irq; + } + + /* initialize battery level*/ + value.intval = 0; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + battery->capacity = value.intval; + +#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE) + /* queue_delayed_work(battery->monitor_wqueue, &battery->fw_init_work, 0); */ +#endif + + /* notify wireless charger driver when sec_battery probe is done, + if wireless charging is possible, POWER_SUPPLY_PROP_ONLINE of wireless property will be called. */ + value.intval = 0; + psy_do_property(battery->pdata->wireless_charger_name, set, + POWER_SUPPLY_PROP_CHARGE_TYPE, value); + +#if defined(CONFIG_STORE_MODE) && !defined(CONFIG_SEC_FACTORY) + battery->store_mode = true; + sec_bat_parse_mode_dt(battery); +#endif + +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + battery->pdic_info.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE; + manager_notifier_register(&battery->usb_typec_nb, + usb_typec_handle_notification, MANAGER_NOTIFY_CCIC_BATTERY); +#else +#if defined(CONFIG_MUIC_NOTIFIER) + muic_notifier_register(&battery->batt_nb, + batt_handle_notification, MUIC_NOTIFY_DEV_CHARGER); +#else + cable_initial_check(battery); +#endif +#if defined(CONFIG_CCIC_NOTIFIER) + pr_info("%s: Registering PDIC_NOTIFY.\n", __func__); + pdic_notifier_register(&battery->pdic_nb, + batt_pdic_handle_notification, PDIC_NOTIFY_DEV_BATTERY); +#endif +#endif +#if defined(CONFIG_VBUS_NOTIFIER) + vbus_notifier_register(&battery->vbus_nb, + vbus_handle_notification, VBUS_NOTIFY_DEV_CHARGER); +#endif + + value.intval = true; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, value); + + /* make fg_reset true again for actual normal booting after recovery kernel is done */ + if (fg_reset && is_boot_recovery()) { + psy_do_property(battery->pdata->fuelgauge_name, set, + POWER_SUPPLY_PROP_ENERGY_NOW, value); + pr_info("%s: make fg_reset true again for actual normal booting\n", __func__); + } + + if ((battery->cable_type == SEC_BATTERY_CABLE_NONE) || + (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_TA)) { + queue_delayed_work(battery->monitor_wqueue, &battery->init_chg_work, 0); + + dev_info(&pdev->dev, + "%s: SEC Battery Driver Monitorwork\n", __func__); + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0); + } + + if (battery->pdata->check_battery_callback) + battery->present = battery->pdata->check_battery_callback(); + + dev_info(battery->dev, + "%s: SEC Battery Driver Loaded\n", __func__); + return 0; + +err_req_irq: + power_supply_unregister(battery->psy_bat); +err_supply_unreg_ac: + power_supply_unregister(battery->psy_ac); +err_supply_unreg_usb: + power_supply_unregister(battery->psy_usb); +err_supply_unreg_wireless: + power_supply_unregister(battery->psy_wireless); +err_supply_unreg_ps: + power_supply_unregister(battery->psy_ps); +#if defined(CONFIG_USE_POGO) +err_supply_unreg_pogo: + power_supply_unregister(battery->psy_pogo); +#endif +err_workqueue: + destroy_workqueue(battery->monitor_wqueue); +err_irq: + wake_lock_destroy(&battery->monitor_wake_lock); + wake_lock_destroy(&battery->cable_wake_lock); + wake_lock_destroy(&battery->vbus_wake_lock); + wake_lock_destroy(&battery->afc_wake_lock); + wake_lock_destroy(&battery->siop_wake_lock); + wake_lock_destroy(&battery->siop_level_wake_lock); + wake_lock_destroy(&battery->siop_event_wake_lock); + wake_lock_destroy(&battery->wc_headroom_wake_lock); +#if defined(CONFIG_UPDATE_BATTERY_DATA) + wake_lock_destroy(&battery->batt_data_wake_lock); +#endif + wake_lock_destroy(&battery->misc_event_wake_lock); +#ifdef CONFIG_OF + wake_lock_destroy(&battery->parse_mode_dt_wake_lock); +#endif + mutex_destroy(&battery->adclock); + mutex_destroy(&battery->iolock); + mutex_destroy(&battery->misclock); + mutex_destroy(&battery->batt_handlelock); + mutex_destroy(&battery->current_eventlock); + mutex_destroy(&battery->typec_notylock); + mutex_destroy(&battery->wclock); + kfree(pdata); +err_bat_free: + kfree(battery); + + return ret; +} + +static int sec_battery_remove(struct platform_device *pdev) +{ + struct sec_battery_info *battery = platform_get_drvdata(pdev); +#ifndef CONFIG_OF + int i; +#endif + + dev_dbg(battery->dev, "%s: Start\n", __func__); + + switch (battery->pdata->polling_type) { + case SEC_BATTERY_MONITOR_WORKQUEUE: + cancel_delayed_work(&battery->polling_work); + break; + case SEC_BATTERY_MONITOR_ALARM: + alarm_cancel(&battery->polling_alarm); + break; + default: + break; + } + + flush_workqueue(battery->monitor_wqueue); + destroy_workqueue(battery->monitor_wqueue); + wake_lock_destroy(&battery->monitor_wake_lock); + wake_lock_destroy(&battery->cable_wake_lock); + wake_lock_destroy(&battery->vbus_wake_lock); + wake_lock_destroy(&battery->afc_wake_lock); + wake_lock_destroy(&battery->siop_wake_lock); + wake_lock_destroy(&battery->siop_level_wake_lock); + wake_lock_destroy(&battery->siop_event_wake_lock); + wake_lock_destroy(&battery->misc_event_wake_lock); + mutex_destroy(&battery->adclock); + mutex_destroy(&battery->iolock); + mutex_destroy(&battery->misclock); + mutex_destroy(&battery->batt_handlelock); + mutex_destroy(&battery->current_eventlock); + mutex_destroy(&battery->typec_notylock); + mutex_destroy(&battery->wclock); +#ifdef CONFIG_OF + adc_exit(battery); +#else + for (i = 0; i < SEC_BAT_ADC_CHANNEL_NUM; i++) + adc_exit(battery->pdata, i); +#endif + power_supply_unregister(battery->psy_ps); + power_supply_unregister(battery->psy_wireless); + power_supply_unregister(battery->psy_ac); + power_supply_unregister(battery->psy_usb); + power_supply_unregister(battery->psy_bat); + + dev_dbg(battery->dev, "%s: End\n", __func__); + kfree(battery); + + return 0; +} + +static int sec_battery_prepare(struct device *dev) +{ + struct sec_battery_info *battery + = dev_get_drvdata(dev); + + dev_info(battery->dev, "%s: Start\n", __func__); + + switch (battery->pdata->polling_type) { + case SEC_BATTERY_MONITOR_WORKQUEUE: + cancel_delayed_work(&battery->polling_work); + break; + case SEC_BATTERY_MONITOR_ALARM: + alarm_cancel(&battery->polling_alarm); + break; + default: + break; + } + + /* monitor_wake_lock should be unlocked before cancle monitor_work */ + wake_unlock(&battery->monitor_wake_lock); + cancel_delayed_work_sync(&battery->monitor_work); + + battery->polling_in_sleep = true; + + sec_bat_set_polling(battery); + + /* cancel work for polling + * that is set in sec_bat_set_polling() + * no need for polling in sleep + */ + if (battery->pdata->polling_type == + SEC_BATTERY_MONITOR_WORKQUEUE) + cancel_delayed_work(&battery->polling_work); + + dev_info(battery->dev, "%s: End\n", __func__); + + return 0; +} + +static int sec_battery_suspend(struct device *dev) +{ + return 0; +} + +static int sec_battery_resume(struct device *dev) +{ + return 0; +} + +static void sec_battery_complete(struct device *dev) +{ + struct sec_battery_info *battery + = dev_get_drvdata(dev); + + dev_info(battery->dev, "%s: Start\n", __func__); + + /* cancel current alarm and reset after monitor work */ + if (battery->pdata->polling_type == SEC_BATTERY_MONITOR_ALARM) + alarm_cancel(&battery->polling_alarm); + + wake_lock(&battery->monitor_wake_lock); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, 0); + + dev_info(battery->dev, "%s: End\n", __func__); + + return; +} + +static void sec_battery_shutdown(struct platform_device *pdev) +{ + struct sec_battery_info *battery + = platform_get_drvdata(pdev); + + switch (battery->pdata->polling_type) { + case SEC_BATTERY_MONITOR_WORKQUEUE: + cancel_delayed_work(&battery->polling_work); + break; + case SEC_BATTERY_MONITOR_ALARM: + alarm_cancel(&battery->polling_alarm); + break; + default: + break; + } +} + +#ifdef CONFIG_OF +static struct of_device_id sec_battery_dt_ids[] = { + { .compatible = "samsung,sec-battery" }, + { } +}; +MODULE_DEVICE_TABLE(of, sec_battery_dt_ids); +#endif /* CONFIG_OF */ + +static const struct dev_pm_ops sec_battery_pm_ops = { + .prepare = sec_battery_prepare, + .suspend = sec_battery_suspend, + .resume = sec_battery_resume, + .complete = sec_battery_complete, +}; + +static struct platform_driver sec_battery_driver = { + .driver = { + .name = "sec-battery", + .owner = THIS_MODULE, + .pm = &sec_battery_pm_ops, +#ifdef CONFIG_OF + .of_match_table = sec_battery_dt_ids, +#endif + }, + .probe = sec_battery_probe, + .remove = sec_battery_remove, + .shutdown = sec_battery_shutdown, +}; + +static int sec_battery_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sec_battery_driver); +} + +static void sec_battery_exit(void) +{ + platform_driver_unregister(&sec_battery_driver); +} + +late_initcall(sec_battery_init); +module_exit(sec_battery_exit); + +MODULE_DESCRIPTION("Samsung Battery Driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/battery_v2/sec_battery_ttf.c b/drivers/battery_v2/sec_battery_ttf.c new file mode 100644 index 000000000000..249ddc5b733e --- /dev/null +++ b/drivers/battery_v2/sec_battery_ttf.c @@ -0,0 +1,312 @@ +/* + * sec_battery_ttf.c + * Samsung Mobile Battery 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. + */ +#include "include/sec_battery.h" +#include "include/sec_battery_ttf.h" + +#if IS_ENABLED(CONFIG_CALC_TIME_TO_FULL) +int sec_calc_ttf(struct sec_battery_info *battery, unsigned int ttf_curr) +{ + struct sec_cv_slope *cv_data = battery->ttf_d->cv_data; + int i, cc_time = 0, cv_time = 0; + int soc = battery->capacity; + int charge_current = ttf_curr; + int design_cap = battery->ttf_d->ttf_capacity; + union power_supply_propval value = {0, }; + + value.intval = SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + soc = value.intval; + + if (!cv_data || (ttf_curr <= 0)) { + pr_info("%s: no cv_data or val: %d\n", __func__, ttf_curr); + return -1; + } + for (i = 0; i < battery->ttf_d->cv_data_length; i++) { + if (charge_current >= cv_data[i].fg_current) + break; + } + i = i >= battery->ttf_d->cv_data_length ? battery->ttf_d->cv_data_length - 1 : i; + if (cv_data[i].soc < soc) { + for (i = 0; i < battery->ttf_d->cv_data_length; i++) { + if (soc <= cv_data[i].soc) + break; + } + cv_time = + ((cv_data[i - 1].time - cv_data[i].time) * (cv_data[i].soc - soc) + / (cv_data[i].soc - cv_data[i - 1].soc)) + cv_data[i].time; + } else { /* CC mode || NONE */ + cv_time = cv_data[i].time; + cc_time = + design_cap * (cv_data[i].soc - soc) / ttf_curr * 3600 / 1000; + pr_debug("%s: cc_time: %d\n", __func__, cc_time); + if (cc_time < 0) + cc_time = 0; + } + + pr_info("%s: cap: %d, soc: %4d, T: %6d, avg: %4d, cv soc: %4d, i: %4d, val: %d\n", + __func__, design_cap, soc, cv_time + cc_time, + battery->current_avg, cv_data[i].soc, i, ttf_curr); + + if (cv_time + cc_time >= 0) + return cv_time + cc_time + 60; + else + return 60; /* minimum 1minutes */ +} + +void sec_bat_calc_time_to_full(struct sec_battery_info * battery) +{ + if (delayed_work_pending(&battery->ttf_d->timetofull_work)) { + pr_info("%s: keep time_to_full(%5d sec)\n", __func__, battery->ttf_d->timetofull); + } else if (battery->status == POWER_SUPPLY_STATUS_CHARGING || + (battery->status == POWER_SUPPLY_STATUS_FULL && battery->capacity != 100)) { + + int charge = 0; + + if (is_hv_wire_12v_type(battery->cable_type) || + battery->max_charge_power >= (battery->ttf_d->pd_charging_charge_power + 5000)) { /* 20000mW */ + charge = battery->ttf_d->ttf_hv_12v_charge_current; + } else if (is_hv_wire_type(battery->cable_type) || (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_TA) || + /* if max_charge_power could support over than max_charging_current, calculate based on ttf_hv_charge_current */ + battery->max_charge_power >= (battery->ttf_d->max_charging_current * 5)) { + charge = battery->ttf_d->ttf_hv_charge_current; + } else if (is_hv_wireless_type(battery->cable_type) || + battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) { + if (sec_bat_hv_wc_normal_mode_check(battery)) + charge = battery->ttf_d->ttf_wireless_charge_current; + else + charge = battery->ttf_d->ttf_hv_wireless_charge_current; + } else if (is_nv_wireless_type(battery->cable_type)) { + charge = battery->ttf_d->ttf_wireless_charge_current; +#if defined(CONFIG_USE_POGO) + } else if (battery->cable_type == SEC_BATTERY_CABLE_POGO) { + charge = battery->ttf_d->ttf_pogo_charge_current; +#endif + } else { + charge = (battery->max_charge_power / 5) > battery->pdata->charging_current[battery->cable_type].fast_charging_current ? + battery->pdata->charging_current[battery->cable_type].fast_charging_current : (battery->max_charge_power / 5); + } + battery->ttf_d->timetofull = sec_calc_ttf(battery, charge); + dev_info(battery->dev, "%s: T: %5d sec, passed time: %5ld, current: %d\n", + __func__, battery->ttf_d->timetofull, battery->charging_passed_time, charge); + } else { + battery->ttf_d->timetofull = -1; + } +} + +#ifdef CONFIG_OF +int sec_ttf_parse_dt(struct sec_battery_info *battery) +{ + struct device_node *np; + struct sec_ttf_data *pdata = battery->ttf_d; + sec_battery_platform_data_t *bpdata = battery->pdata; + int ret = 0, len = 0; + const u32 *p; + + pdata->pdev = battery; + np = of_find_node_by_name(NULL, "battery"); + if (!np) { + pr_info("%s: np NULL\n", __func__); + return 1; + } + + ret = of_property_read_u32(np, "battery,ttf_hv_12v_charge_current", + &pdata->ttf_hv_12v_charge_current); + if (ret) { + pdata->ttf_hv_12v_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_12V_TA].fast_charging_current; + pr_info("%s: ttf_hv_12v_charge_current is Empty, Defualt value %d \n", + __func__, pdata->ttf_hv_12v_charge_current); + } + ret = of_property_read_u32(np, "battery,ttf_hv_charge_current", + &pdata->ttf_hv_charge_current); + if (ret) { + pdata->ttf_hv_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_9V_TA].fast_charging_current; + pr_info("%s: ttf_hv_charge_current is Empty, Defualt value %d \n", + __func__, pdata->ttf_hv_charge_current); + } + + ret = of_property_read_u32(np, "battery,ttf_hv_wireless_charge_current", + &pdata->ttf_hv_wireless_charge_current); + if (ret) { + pr_info("%s: ttf_hv_wireless_charge_current is Empty, Defualt value 0 \n", __func__); + pdata->ttf_hv_wireless_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_HV_WIRELESS].fast_charging_current - 300; + } + + ret = of_property_read_u32(np, "battery,ttf_wireless_charge_current", + &pdata->ttf_wireless_charge_current); + if (ret) { + pr_info("%s: ttf_wireless_charge_current is Empty, Defualt value 0 \n", __func__); + pdata->ttf_wireless_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_WIRELESS].input_current_limit; + } + +#if defined(CONFIG_USE_POGO) + ret = of_property_read_u32(np, "battery,ttf_pogo_charge_current", + &pdata->ttf_pogo_charge_current); + if (ret) { + pr_info("%s: ttf_pogo_charge_current is Empty, default value 0 \n", __func__); + pdata->ttf_pogo_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_POGO].input_current_limit; + } +#endif + + ret = of_property_read_u32(np, "battery,pd_charging_charge_power", + &pdata->pd_charging_charge_power); + if (ret) { + pr_err("%s: pd_charging_charge_power is Empty\n", __func__); + pdata->pd_charging_charge_power = 15000; + } + + ret = of_property_read_u32(np, "battery,max_charging_current", + &pdata->max_charging_current); + if (ret) { + pr_err("%s: max_charging_current is Empty\n", __func__); + pdata->max_charging_current = 3000; + } + /* temporary dt setting */ + ret = of_property_read_u32(np, "battery,ttf_predict_wc20_charge_current", + &pdata->ttf_predict_wc20_charge_current); + if (ret) { + pr_info("%s: ttf_predict_wc20_charge_current is Empty, Default value 0\n", __func__); + pdata->ttf_predict_wc20_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_WIRELESS].input_current_limit; + } + + ret = of_property_read_u32(np, "battery,ttf_dc25_charge_current", + &pdata->ttf_dc25_charge_current); + if (ret) { + pr_info("%s: ttf_dc25_charge_current is Empty, Default value 0 \n", __func__); + pdata->ttf_dc25_charge_current = + bpdata->charging_current[SEC_BATTERY_CABLE_9V_TA].fast_charging_current; + } + + ret = of_property_read_u32(np, "battery,ttf_dc45_charge_current", + &pdata->ttf_dc45_charge_current); + if (ret) { + pr_info("%s: ttf_dc45_charge_current is Empty, Default value 0 \n", __func__); + pdata->ttf_dc45_charge_current = pdata->ttf_dc25_charge_current; + } + + ret = of_property_read_u32(np, "battery,ttf_capacity", + &pdata->ttf_capacity); + if (ret < 0) { + pr_err("%s error reading capacity_calculation_type %d\n", __func__, ret); + pdata->ttf_capacity = bpdata->battery_full_capacity; + } + + p = of_get_property(np, "battery,cv_data", &len); + if (p) { + pdata->cv_data = kzalloc(len, GFP_KERNEL); + pdata->cv_data_length = len / sizeof(struct sec_cv_slope); + pr_err("%s: len= %ld, length= %d, %d\n", __func__, + sizeof(int) * len, len, pdata->cv_data_length); + ret = of_property_read_u32_array(np, "battery,cv_data", + (u32 *)pdata->cv_data, len / sizeof(u32)); + if (ret) { + pr_err("%s: failed to read battery->cv_data: %d\n", + __func__, ret); + kfree(pdata->cv_data); + pdata->cv_data = NULL; + } + } else { + pr_err("%s: there is not cv_data\n", __func__); + } + return 0; +} +#endif + +void sec_bat_time_to_full_work(struct work_struct *work) +{ + struct sec_ttf_data *dev = container_of(work, + struct sec_ttf_data, timetofull_work.work); + struct sec_battery_info *battery = dev->pdev; + union power_supply_propval value = {0, }; + + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CURRENT_MAX, value); + battery->current_max = value.intval; + + value.intval = SEC_BATTERY_CURRENT_MA; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CURRENT_NOW, value); + battery->current_now = value.intval; + + value.intval = SEC_BATTERY_CURRENT_MA; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CURRENT_AVG, value); + battery->current_avg = value.intval; + + sec_bat_calc_time_to_full(battery); + dev_info(battery->dev, "%s:\n", __func__); + if (battery->voltage_now > 0) + battery->voltage_now--; + + power_supply_changed(battery->psy_bat); +} + +void ttf_work_start(struct sec_battery_info *battery) +{ + if (lpcharge) { + cancel_delayed_work(&battery->ttf_d->timetofull_work); + if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) { + int work_delay = 0; + + if (!is_wireless_type(battery->cable_type)) { + work_delay = battery->pdata->pre_afc_work_delay; + } else { + work_delay = battery->pdata->pre_wc_afc_work_delay; + } + queue_delayed_work(battery->monitor_wqueue, + &battery->ttf_d->timetofull_work, msecs_to_jiffies(work_delay)); + } + } +} + +int ttf_display(struct sec_battery_info *battery) +{ + if (battery->capacity == 100) + return 0; + + if (((battery->status == POWER_SUPPLY_STATUS_CHARGING) || + (battery->status == POWER_SUPPLY_STATUS_FULL && battery->capacity != 100)) && + !battery->swelling_mode) + return battery->ttf_d->timetofull; + else + return 0; +} + +void ttf_init(struct sec_battery_info *battery) +{ + battery->ttf_d = kzalloc(sizeof(struct sec_ttf_data), + GFP_KERNEL); + if (!battery->ttf_d) { + pr_err("Failed to allocate memory\n"); + } + sec_ttf_parse_dt(battery); + battery->ttf_d->timetofull = -1; + + INIT_DELAYED_WORK(&battery->ttf_d->timetofull_work, sec_bat_time_to_full_work); +} +#else +int sec_calc_ttf(struct sec_battery_info *battery, unsigned int ttf_curr) { return -ENODEV; } +void sec_bat_calc_time_to_full(struct sec_battery_info *battery) { } +void sec_bat_time_to_full_work(struct work_struct *work) { } +void ttf_init(struct sec_battery_info *battery) { } +void ttf_work_start(struct sec_battery_info *battery) { } +int ttf_display(struct sec_battery_info *battery) { return 0; } +#ifdef CONFIG_OF +int sec_ttf_parse_dt(struct sec_battery_info *battery) { return -ENODEV; } +#endif +#endif diff --git a/drivers/battery_v2/sec_cisd.c b/drivers/battery_v2/sec_cisd.c new file mode 100644 index 000000000000..8ee1ae793a96 --- /dev/null +++ b/drivers/battery_v2/sec_cisd.c @@ -0,0 +1,482 @@ +/* + * sec_cisd.c + * Samsung Mobile Battery Driver + * + * Copyright (C) 2012 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 "include/sec_battery.h" +#include "include/sec_cisd.h" + +#if defined(CONFIG_SEC_ABC) +#include +#endif + +const char* cisd_data_str[] ={ + "RESET_ALG", "ALG_INDEX", "FULL_CNT", "CAP_MAX", "CAP_MIN", "RECHARGING_CNT", "VALERT_CNT", + "BATT_CYCLE", "WIRE_CNT", "WIRELESS_CNT", "HIGH_SWELLING_CNT", "LOW_SWELLING_CNT", + "SWELLING_CHARGING", "SWELLING_FULL_CNT", "SWELLING_RECOVERY_CNT", "AICL_CNT", "BATT_THM_MAX", + "BATT_THM_MIN", "CHG_THM_MAX", "CHG_THM_MIN", "WPC_THM_MAX", "WPC_THM_MIN", "USB_THM_MAX", "USB_THM_MIN", + "CHG_BATT_THM_MAX", "CHG_BATT_THM_MIN", "CHG_CHG_THM_MAX", "CHG_CHG_THM_MIN", "CHG_WPC_THM_MAX", + "CHG_WPC_THM_MIN", "CHG_USB_THM_MAX", "CHG_USB_THM_MIN", "USB_OVERHEAT_CHARGING", "UNSAFETY_VOLT", + "UNSAFETY_TEMP", "SAFETY_TIMER", "VSYS_OVP", "VBAT_OVP", "AFC_FAIL", "BUCK_OFF", "WATER_DET", "DROP_SENSOR" +}; +const char* cisd_data_str_d[] = { + "FULL_CNT_D", "CAP_MAX_D", "CAP_MIN_D", "RECHARGING_CNT_D", "VALERT_CNT_D", "WIRE_CNT_D", "WIRELESS_CNT_D", + "HIGH_SWELLING_CNT_D", "LOW_SWELLING_CNT_D", "SWELLING_CHARGING_D", "SWELLING_FULL_CNT_D", + "SWELLING_RECOVERY_CNT_D", "AICL_CNT_D", "BATT_THM_MAX_D", "BATT_THM_MIN_D", "CHG_THM_MAX_D", + "CHG_THM_MIN_D", "WPC_THM_MAX_D", "WPC_THM_MIN_D", "USB_THM_MAX_D", "USB_THM_MIN_D", + "CHG_BATT_THM_MAX_D", "CHG_BATT_THM_MIN_D", "CHG_CHG_THM_MAX_D", "CHG_CHG_THM_MIN_D", + "CHG_WPC_THM_MAX_D", "CHG_WPC_THM_MIN_D", "CHG_USB_THM_MAX_D", "CHG_USB_THM_MIN_D", + "USB_OVERHEAT_CHARGING_D", "UNSAFETY_VOLT_D", "UNSAFETY_TEMP_D", "SAFETY_TIMER_D", "VSYS_OVP_D", + "VBAT_OVP_D", "AFC_FAIL_D", "BUCK_OFF_D", "WATER_DET_D", "DROP_SENSOR_D" +}; + +bool sec_bat_cisd_check(struct sec_battery_info *battery) +{ + union power_supply_propval incur_val = {0, }; + union power_supply_propval chgcur_val = {0, }; + union power_supply_propval capcurr_val = {0, }; + union power_supply_propval vbat_val = {0, }; + struct cisd *pcisd = &battery->cisd; + bool ret = false; + + if (battery->factory_mode || battery->is_jig_on || battery->skip_cisd) { + dev_dbg(battery->dev, "%s: No need to check in factory mode\n", + __func__); + return ret; + } + + if ((battery->status == POWER_SUPPLY_STATUS_CHARGING) || + (battery->status == POWER_SUPPLY_STATUS_FULL)) { + + /* check abnormal vbat */ + pcisd->ab_vbat_check_count = battery->voltage_now > pcisd->max_voltage_thr ? + pcisd->ab_vbat_check_count + 1 : 0; + + if ((pcisd->ab_vbat_check_count >= pcisd->ab_vbat_max_count) && + !(pcisd->state & CISD_STATE_OVER_VOLTAGE)) { + dev_info(battery->dev, "%s : [CISD] Battery Over Voltage Protction !! vbat(%d)mV\n", + __func__, battery->voltage_now); + vbat_val.intval = true; + psy_do_property("battery", set, (enum power_supply_property) POWER_SUPPLY_EXT_PROP_VBAT_OVP , + vbat_val); + pcisd->data[CISD_DATA_VBAT_OVP]++; + pcisd->data[CISD_DATA_VBAT_OVP_PER_DAY]++; + pcisd->state |= CISD_STATE_OVER_VOLTAGE; +#if defined(CONFIG_SEC_ABC) + sec_abc_send_event("MODULE=battery@ERROR=over_voltage"); +#endif + } + + /* get actual input current */ + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CURRENT_AVG, incur_val); + + /* get actual charging current */ + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, chgcur_val); + + if (battery->temperature > pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX]) + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX] = battery->temperature; + if (battery->temperature < pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN]) + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN] = battery->temperature; + + if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX]) + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX] = battery->chg_temp; + if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN]) + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN] = battery->chg_temp; + + if (battery->wpc_temp > pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX]) + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX] = battery->wpc_temp; + if (battery->wpc_temp < pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN]) + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN] = battery->wpc_temp; + + if (battery->usb_temp > pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX]) + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX] = battery->usb_temp; + if (battery->usb_temp < pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN]) + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN] = battery->usb_temp; + + if (battery->temperature > pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = battery->temperature; + if (battery->temperature < pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = battery->temperature; + + if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = battery->chg_temp; + if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = battery->chg_temp; + + if (battery->wpc_temp > pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = battery->wpc_temp; + if (battery->wpc_temp < pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = battery->wpc_temp; + + if (battery->usb_temp > pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY] = battery->usb_temp; + if (battery->usb_temp < pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY] = battery->usb_temp; + + if (battery->usb_temp > 800 && !battery->usb_overheat_check) { + battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING]++; + battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY]++; + battery->usb_overheat_check = true; + } + + dev_info(battery->dev, "%s: [CISD] iavg: %d, incur: %d, chgcur: %d,\n" + "cc_T: %ld, lcd_off_T: %ld, passed_T: %ld, full_T: %ld, chg_end_T: %ld, cisd: 0x%x\n",__func__, + battery->current_avg, incur_val.intval, chgcur_val.intval, + pcisd->cc_start_time, pcisd->lcd_off_start_time, battery->charging_passed_time, + battery->charging_fullcharged_time, pcisd->charging_end_time, pcisd->state); + } else { + /* discharging */ + if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) { + /* check abnormal vbat */ + pcisd->ab_vbat_check_count = battery->voltage_now > pcisd->max_voltage_thr ? + pcisd->ab_vbat_check_count + 1 : 0; + + if ((pcisd->ab_vbat_check_count >= pcisd->ab_vbat_max_count) && + !(pcisd->state & CISD_STATE_OVER_VOLTAGE)) { + pcisd->data[CISD_DATA_VBAT_OVP]++; + pcisd->data[CISD_DATA_VBAT_OVP_PER_DAY]++; + pcisd->state |= CISD_STATE_OVER_VOLTAGE; +#if defined(CONFIG_SEC_ABC) + sec_abc_send_event("MODULE=battery@ERROR=over_voltage"); +#endif + } + } + + capcurr_val.intval = SEC_BATTERY_CAPACITY_FULL; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, capcurr_val); + if (capcurr_val.intval == -1) { + dev_info(battery->dev, "%s: [CISD] FG I2C fail. skip cisd check \n", __func__); + return ret; + } + + if (capcurr_val.intval > pcisd->data[CISD_DATA_CAP_MAX]) + pcisd->data[CISD_DATA_CAP_MAX] = capcurr_val.intval; + if (capcurr_val.intval < pcisd->data[CISD_DATA_CAP_MIN]) + pcisd->data[CISD_DATA_CAP_MIN] = capcurr_val.intval; + + if (capcurr_val.intval > pcisd->data[CISD_DATA_CAP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_CAP_MAX_PER_DAY] = capcurr_val.intval; + if (capcurr_val.intval < pcisd->data[CISD_DATA_CAP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_CAP_MIN_PER_DAY] = capcurr_val.intval; + } + + if (battery->temperature > pcisd->data[CISD_DATA_BATT_TEMP_MAX]) + pcisd->data[CISD_DATA_BATT_TEMP_MAX] = battery->temperature; + if (battery->temperature < battery->cisd.data[CISD_DATA_BATT_TEMP_MIN]) + pcisd->data[CISD_DATA_BATT_TEMP_MIN] = battery->temperature; + + if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_TEMP_MAX]) + pcisd->data[CISD_DATA_CHG_TEMP_MAX] = battery->chg_temp; + if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_TEMP_MIN]) + pcisd->data[CISD_DATA_CHG_TEMP_MIN] = battery->chg_temp; + + if (battery->wpc_temp > pcisd->data[CISD_DATA_WPC_TEMP_MAX]) + pcisd->data[CISD_DATA_WPC_TEMP_MAX] = battery->wpc_temp; + if (battery->wpc_temp < battery->cisd.data[CISD_DATA_WPC_TEMP_MIN]) + pcisd->data[CISD_DATA_WPC_TEMP_MIN] = battery->wpc_temp; + + if (battery->usb_temp > pcisd->data[CISD_DATA_USB_TEMP_MAX]) + pcisd->data[CISD_DATA_USB_TEMP_MAX] = battery->usb_temp; + if (battery->usb_temp < pcisd->data[CISD_DATA_USB_TEMP_MIN]) + pcisd->data[CISD_DATA_USB_TEMP_MIN] = battery->usb_temp; + + if (battery->temperature > pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = battery->temperature; + if (battery->temperature < pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = battery->temperature; + + if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = battery->chg_temp; + if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = battery->chg_temp; + + if (battery->wpc_temp > pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = battery->wpc_temp; + if (battery->wpc_temp < pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = battery->wpc_temp; + + if (battery->usb_temp > pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY]) + pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY] = battery->usb_temp; + if (battery->usb_temp < pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY]) + pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY] = battery->usb_temp; + + return ret; +} + +struct cisd *gcisd; +void sec_battery_cisd_init(struct sec_battery_info *battery) +{ + union power_supply_propval capfull_val; + + battery->cisd.state = CISD_STATE_NONE; + + battery->cisd.delay_time = 600; /* 10 min */ + battery->cisd.diff_volt_now = 40; + battery->cisd.diff_cap_now = 5; + + capfull_val.intval = SEC_BATTERY_CAPACITY_FULL; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_NOW, capfull_val); + battery->cisd.curr_cap_max = capfull_val.intval; + battery->cisd.err_cap_high_thr = battery->pdata->cisd_cap_high_thr; + battery->cisd.err_cap_low_thr = battery->pdata->cisd_cap_low_thr; + battery->cisd.cc_delay_time = 3600; /* 60 min */ + battery->cisd.lcd_off_delay_time = 10200; /* 170 min */ + battery->cisd.full_delay_time = 3600; /* 60 min */ + battery->cisd.recharge_delay_time = 9000; /* 150 min */ + battery->cisd.cc_start_time = 0; + battery->cisd.full_start_time = 0; + battery->cisd.lcd_off_start_time = 0; + battery->cisd.overflow_start_time = 0; + battery->cisd.charging_end_time = 0; + battery->cisd.charging_end_time_2 = 0; + battery->cisd.recharge_count = 0; + battery->cisd.recharge_count_2 = 0; + battery->cisd.recharge_count_thres = 2; + battery->cisd.leakage_e_time = 3600; /* 60 min */ + battery->cisd.leakage_f_time = 7200; /* 120 min */ + battery->cisd.leakage_g_time = 14400; /* 240 min */ + battery->cisd.current_max_thres = 1600; + battery->cisd.charging_current_thres = 1000; + battery->cisd.current_avg_thres = 1000; + + battery->cisd.data[CISD_DATA_ALG_INDEX] = battery->pdata->cisd_alg_index; + battery->cisd.data[CISD_DATA_FULL_COUNT] = 1; + battery->cisd.data[CISD_DATA_BATT_TEMP_MAX] = -300; + battery->cisd.data[CISD_DATA_CHG_TEMP_MAX] = -300; + battery->cisd.data[CISD_DATA_WPC_TEMP_MAX] = -300; + battery->cisd.data[CISD_DATA_BATT_TEMP_MIN] = 1000; + battery->cisd.data[CISD_DATA_CHG_TEMP_MIN] = 1000; + battery->cisd.data[CISD_DATA_WPC_TEMP_MIN] = 1000; + battery->cisd.data[CISD_DATA_CAP_MIN] = 0xFFFF; + + battery->cisd.data[CISD_DATA_FULL_COUNT_PER_DAY] = 1; + battery->cisd.data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = -300; + battery->cisd.data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = -300; + battery->cisd.data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = -300; + battery->cisd.data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = 1000; + battery->cisd.data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = 1000; + battery->cisd.data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = 1000; + battery->cisd.data[CISD_DATA_CAP_MIN] = 0xFFFF; + + battery->cisd.data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = -300; + battery->cisd.data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = -300; + battery->cisd.data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = -300; + battery->cisd.data[CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY] = 1000; + battery->cisd.data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = 1000; + battery->cisd.data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = 1000; + + battery->cisd.capacity_now = capfull_val.intval; + battery->cisd.overflow_cap_thr = capfull_val.intval > battery->pdata->cisd_cap_limit ? + capfull_val.intval : battery->pdata->cisd_cap_limit; + + battery->cisd.ab_vbat_max_count = 2; /* should be 1 */ + battery->cisd.ab_vbat_check_count = 0; + battery->cisd.max_voltage_thr = battery->pdata->max_voltage_thr; + battery->cisd.cisd_alg_index = 6; + pr_info("%s: cisd.err_cap_high_thr:%d, cisd.err_cap_low_thr:%d, cisd.overflow_cap_thr:%d\n", __func__, + battery->cisd.err_cap_high_thr, battery->cisd.err_cap_low_thr, battery->cisd.overflow_cap_thr); + + /* set cisd pointer */ + gcisd = &battery->cisd; + + /* initialize pad data */ + mutex_init(&battery->cisd.padlock); + init_cisd_pad_data(&battery->cisd); +} + +static struct pad_data* create_pad_data(unsigned int pad_id, unsigned int pad_count) +{ + struct pad_data* temp_data; + + temp_data = kzalloc(sizeof(struct pad_data), GFP_KERNEL); + if (temp_data == NULL) + return NULL; + + temp_data->id = pad_id; + temp_data->count = pad_count; + temp_data->prev = temp_data->next = NULL; + + return temp_data; +} + +static struct pad_data* find_pad_data_by_id(struct cisd* cisd, unsigned int pad_id) +{ + struct pad_data* temp_data = cisd->pad_array->next; + + if (cisd->pad_count <= 0 || temp_data == NULL) + return NULL; + + while ((temp_data->id != pad_id) && + ((temp_data = temp_data->next) != NULL)); + + return temp_data; +} + +static void add_pad_data(struct cisd* cisd, unsigned int pad_id, unsigned int pad_count) +{ + struct pad_data* temp_data = cisd->pad_array->next; + struct pad_data* pad_data; + + if (pad_id == 0 || pad_id >= MAX_PAD_ID) + return; + + pad_data = create_pad_data(pad_id, pad_count); + if (pad_data == NULL) + return; + + pr_info("%s: id(0x%x), count(%d)\n", __func__, pad_id, pad_count); + while (temp_data) { + if (temp_data->id > pad_id) { + temp_data->prev->next = pad_data; + pad_data->prev = temp_data->prev; + pad_data->next = temp_data; + temp_data->prev = pad_data; + cisd->pad_count++; + return; + } + temp_data = temp_data->next; + } + + pr_info("%s: failed to add pad_data(%d, %d)\n", + __func__, pad_id, pad_count); + kfree(pad_data); +} + +void init_cisd_pad_data(struct cisd* cisd) +{ + struct pad_data* temp_data = cisd->pad_array; + + mutex_lock(&cisd->padlock); + while (temp_data) { + struct pad_data* next_data = temp_data->next; + + kfree(temp_data); + temp_data = next_data; + } + + /* create dummy data */ + cisd->pad_count = 0; + cisd->pad_array = create_pad_data(0, 0); + if (cisd->pad_array == NULL) + return; + temp_data = create_pad_data(MAX_PAD_ID, 0); + if (temp_data == NULL) { + kfree(cisd->pad_array); + return; + } + cisd->pad_array->next = temp_data; + temp_data->prev = cisd->pad_array; + mutex_unlock(&cisd->padlock); +} + +void count_cisd_pad_data(struct cisd* cisd, unsigned int pad_id) +{ + struct pad_data* pad_data; + + mutex_lock(&cisd->padlock); + if ((pad_data = find_pad_data_by_id(cisd, pad_id)) != NULL) + pad_data->count++; + else + add_pad_data(cisd, pad_id, 1); + mutex_unlock(&cisd->padlock); +} + +static unsigned int convert_wc_index_to_pad_id(unsigned int wc_index) +{ + switch (wc_index) { + case WC_SNGL_NOBLE: + return WC_PAD_ID_SNGL_NOBLE; + case WC_SNGL_VEHICLE: + return WC_PAD_ID_SNGL_VEHICLE; + case WC_SNGL_MINI: + return WC_PAD_ID_SNGL_MINI; + case WC_SNGL_ZERO: + return WC_PAD_ID_SNGL_ZERO; + case WC_SNGL_DREAM: + return WC_PAD_ID_SNGL_DREAM; + case WC_STAND_HERO: + return WC_PAD_ID_STAND_HERO; + case WC_STAND_DREAM: + return WC_PAD_ID_STAND_DREAM; + case WC_EXT_PACK: + return WC_PAD_ID_EXT_BATT_PACK; + case WC_EXT_PACK_TA: + return WC_PAD_ID_EXT_BATT_PACK_TA; + default: + break; + } + + return 0; +} + +void set_cisd_pad_data(struct sec_battery_info *battery, const char* buf) +{ + struct cisd* pcisd = &battery->cisd; + unsigned int pad_index, pad_total_count, pad_id, pad_count; + struct pad_data* pad_data; + int i, x; + + pr_info("%s: %s\n", __func__, buf); + if (sscanf(buf, "%10d%n", &pad_index, &x) <= 0) { + pr_info("%s: failed to read pad index\n", __func__); + return; + } + buf += (size_t)x; + pr_info("%s: stored pad_index(%d)\n", __func__, pad_index); + + if (pcisd->pad_count > 0) + init_cisd_pad_data(pcisd); + + if (!pad_index) { + for (i = WC_DATA_INDEX + 1; i < WC_DATA_MAX; i++) { + if (sscanf(buf, "%10d%n", &pad_count, &x) <= 0) + break; + buf += (size_t)x; + + if (pad_count > 0) { + pad_id = convert_wc_index_to_pad_id(i); + + mutex_lock(&pcisd->padlock); + if ((pad_data = find_pad_data_by_id(pcisd, pad_id)) != NULL) + pad_data->count = pad_count; + else + add_pad_data(pcisd, pad_id, pad_count); + mutex_unlock(&pcisd->padlock); + } + } + } else { + if ((sscanf(buf + 1, "%10d%n", &pad_total_count, &x) <= 0) || + (pad_total_count >= MAX_PAD_ID)) + return; + buf += (size_t)(x + 1); + + pr_info("%s: add pad data(count: %d)\n", __func__, pad_total_count); + for (i = 0; i < pad_total_count; i++) { + if (sscanf(buf, " 0x%02x:%10d%n", &pad_id, &pad_count, &x) != 2) { + pr_info("%s: failed to read pad data(0x%x, %d, %d)!!!re-init pad data\n", + __func__, pad_id, pad_count, x); + init_cisd_pad_data(pcisd); + break; + } + buf += (size_t)x; + + mutex_lock(&pcisd->padlock); + if ((pad_data = find_pad_data_by_id(pcisd, pad_id)) != NULL) + pad_data->count = pad_count; + else + add_pad_data(pcisd, pad_id, pad_count); + mutex_unlock(&pcisd->padlock); + } + } +} + diff --git a/drivers/battery_v2/sm5705_charger.c b/drivers/battery_v2/sm5705_charger.c new file mode 100644 index 000000000000..7d6778cdadbc --- /dev/null +++ b/drivers/battery_v2/sm5705_charger.c @@ -0,0 +1,2013 @@ + +/* + * /drivers/battery/sm5705_charger.c + * + * SM5705 Charger driver for SEC_BATTERY Flatform support + * + * Copyright (C) 2015 Silicon Mitus, + * + * + * 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. + * + */ +#define pr_fmt(fmt) "sm5705-charger: %s: " fmt, __func__ +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_HOST_NOTIFY +#include +#endif +#if defined(CONFIG_VBUS_NOTIFIER) +#include +#endif +#include +#include "include/charger/sm5705_charger.h" +#include "include/charger/sm5705_charger_oper.h" +//#define SM5705_CHG_FULL_DEBUG 1 + +#define ENABLE 1 +#define DISABLE 0 + +#if defined(CONFIG_SEC_FACTORY) +extern int factory_mode; +#endif + +static enum power_supply_property sm5705_charger_props[] = { +}; +static struct device_attribute sm5705_charger_attrs[] = { + SM5705_CHARGER_ATTR(chip_id), + SM5705_CHARGER_ATTR(charger_op_mode), + SM5705_CHARGER_ATTR(data), +}; + +/** + * SM5705 Charger device register control functions + */ + +static bool sm5705_CHG_get_INT_STATUS(struct sm5705_charger_data *charger, + unsigned char index, unsigned char offset) +{ + unsigned char reg_val; + int ret; + + ret = sm5705_read_reg(charger->i2c, SM5705_REG_STATUS1 + index, ®_val); + if (ret < 0) { + pr_err("fail to I2C read REG:SM5705_REG_INT%d\n", 1 + index); + return 0; + } + reg_val = (reg_val & (1 << offset)) >> offset; + + return reg_val; +} +static int sm5705_CHG_set_TOPOFF_TMR(struct sm5705_charger_data *charger, + unsigned char topoff_timer) +{ + sm5705_update_reg(charger->i2c, + SM5705_REG_CHGCNTL8, ((topoff_timer & 0x3) << 3), (0x3 << 3)); + pr_info("TOPOFF_TMR set (timer=%d)\n", topoff_timer); + + return 0; +} +static int sm5705_CHG_enable_AUTOSTOP(struct sm5705_charger_data *charger, bool enable) +{ + int ret; + + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL4, (enable << 6), (1 << 6)); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL4 in AUTOSTOP[6]\n"); + return ret; + } + + return 0; +} +static unsigned char _calc_BATREG_offset_to_float_mV(unsigned short mV) +{ + unsigned char offset; + + if (mV < 4000) + offset = 0x0; /* BATREG = 3.8V */ + else if (mV < 4010) + offset = 0x1; /* BATREG = 4.0V */ + else if (mV < 4630) + offset = (((mV - 4010) / 10) + 2); /* BATREG = 4.01 ~ 4.62 */ + else +#if 0 + offset = 0x15; /* default Offset : 4.2V */ +#else + offset = 0x14; /* default Offset : 4.19V */ +#endif + + return offset; +} +static int sm5705_CHG_set_BATREG(struct sm5705_charger_data *charger, + unsigned short float_mV) +{ + unsigned char offset; + int ret; + +#if defined(CONFIG_SEC_FACTORY) + if (factory_mode) { + pr_info("%s: Factory Mode Skip BATREG Control\n", __func__); + return 0; + } +#endif + + + offset= _calc_BATREG_offset_to_float_mV(float_mV); + pr_info("set BATREG voltage(%dmV - offset=0x%x)\n", float_mV, offset); + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL4, offset, 0x3F); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL4 in BATREG[5:0]\n"); + return ret; + } + + return 0; +} +static unsigned short _calc_float_mV_to_BATREG_offset(unsigned char offset) +{ + return ((offset - 2) * 10) + 4010; +} +static unsigned short sm5705_CHG_get_BATREG(struct sm5705_charger_data *charger) +{ + unsigned char offset; + int ret; + + ret = sm5705_read_reg(charger->i2c, SM5705_REG_CHGCNTL4, &offset); + if (ret < 0) { + pr_err("fail to read REG:SM5705_REG_CHGCNTL4\n"); + return 0; + } + + return _calc_float_mV_to_BATREG_offset(offset & 0x3F); +} +static unsigned char _calc_TOPOFF_offset_to_topoff_mA(unsigned short mA) +{ + unsigned char offset; + + if (mA < 100) + offset = 0x0; + else if (mA < 480) + offset = (mA - 100) / 25; + else + offset = 0xF; + + return offset; +} +static int sm5705_CHG_set_TOPOFF(struct sm5705_charger_data *charger, + unsigned short topoff_mA) +{ + unsigned char offset = _calc_TOPOFF_offset_to_topoff_mA(topoff_mA); + int ret; + + pr_info("set TOP-OFF current(%dmA - offset=0x%x)\n", topoff_mA, offset); + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL5, offset, 0xF); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL5 in TOPOFF[3:0]\n"); + return ret; + } + + return 0; +} +static int sm5705_CHG_set_FREQSEL(struct sm5705_charger_data *charger, unsigned char freq_index) +{ + int ret; + + pr_info("set BUCK&BOOST freq=0x%x\n", freq_index); + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL5, ((freq_index & 0x3) << 4), (0x3 << 4)); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL5 in FREQSEL[5:4]\n"); + return ret; + } + return 0; +} +static unsigned char _calc_AICL_threshold_offset_to_mV(unsigned short aiclth_mV) +{ + unsigned char offset; + + if (aiclth_mV < 4500) + offset = 0x0; + else if (aiclth_mV < 4900) + offset = (aiclth_mV - 4500) / 100; + else + offset = 0x3; + + return offset; +} +static int sm5705_CHG_set_AICLTH(struct sm5705_charger_data *charger, + unsigned short aiclth_mV) +{ + unsigned char offset = _calc_AICL_threshold_offset_to_mV(aiclth_mV); + int ret; + + pr_info("set AICL threshold (%dmV - offset=0x%x)\n", aiclth_mV, offset); + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL7, (offset << 6), 0xC0); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL7 in AICLTH[7:6]\n"); + return ret; + } + + return 0; +} +static int sm5705_CHG_set_OVPSEL(struct sm5705_charger_data *charger, bool enable) +{ + int ret; + + pr_info("set OVPSEL=%d\n", enable); + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL7, (enable << 2), (1 << 2)); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL7 in OVPSEL[2]\n"); + return ret; + } + + return 0; +} +static int sm5705_CHG_enable_AICL(struct sm5705_charger_data *charger, bool enable) +{ + int ret; + +#if defined(CONFIG_SEC_FACTORY) + if (factory_mode) { + pr_info("%s: Factory Mode Skip AICL enable \n", __func__); + return 0; + } +#endif + + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL7, (enable << 5), (1 << 5)); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL7 in AICLEN[5]\n"); + return ret; + } + + return 0; +} +static int sm5705_CHG_set_BST_IQ3LIMIT(struct sm5705_charger_data *charger, + unsigned char index) +{ + if (index > SM5705_CHG_BST_IQ3LIMIT_4_0A) { + pr_err("invalid limit current index (index=0x%x)\n", index); + return -EINVAL; + } + sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL6, ((index & 0x3)), 0x3); + pr_info("BST IQ3LIMIT set (index=0x%x)\n", index); + + return 0; +} +#if defined(SM5705_I2C_RESET_ACTIVATE) +static int sm5705_CHG_set_ENI2CRESET(struct sm5705_charger_data *charger, bool enable) +{ + sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL6, (enable << 4), (0x1 << 4)); + pr_info("ENI2CRESET set (enable=%d)\n", enable); + + return 0; +} +#endif +#if defined(SM5705_MANUAL_RESET_ACTIVATE) +static int sm5705_CHG_set_ENMRSTB(struct sm5705_charger_data *charger, unsigned char timer) +{ + sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL8, (timer & 0x3), 0x3); + pr_info("ENMRSTB set (timer=%d)\n", timer); + + return 0; +} +#endif +static int sm5705_CHG_enable_AUTOSET(struct sm5705_charger_data *charger, bool enable) +{ + int ret; + + ret = sm5705_update_reg(charger->i2c, SM5705_REG_CHGCNTL7, (enable << 1), (1 << 1)); + if (ret < 0) { + pr_err("fail to update REG:SM5705_REG_CHGCNTL7 in AUTOSET[1]\n"); + return ret; + } + + return 0; +} +static unsigned char _calc_FASTCHG_current_offset_to_mA(unsigned short mA) +{ + unsigned char offset; + + if (mA < 200) + offset = 0x02; + else { + mA = (mA > 3250) ? 3250 : mA; + offset = ((mA - 100) / 50) & 0x3F; + } + + return offset; +} +static int sm5705_CHG_set_FASTCHG(struct sm5705_charger_data *charger, + unsigned char index, unsigned short FASTCHG_mA) +{ + unsigned char offset = _calc_FASTCHG_current_offset_to_mA(FASTCHG_mA); + + pr_info("FASTCHG src=%d, current=%dmA offset=0x%x\n", index, FASTCHG_mA, offset); + if (index > SM5705_CHG_SRC_WPC) + return -EINVAL; + sm5705_write_reg(charger->i2c, SM5705_REG_CHGCNTL2 + index, offset); + + return 0; +} +static unsigned char _calc_INPUT_LIMIT_current_offset_to_mA(unsigned char index, + unsigned short mA) +{ + unsigned char offset; + + if (mA < 100) + offset = 0x10; + else { + if (index == SM5705_CHG_SRC_VBUS) { + mA = (mA > 3275) ? 3275 : mA; + offset = ((mA - 100) / 25) & 0x7F; /* max = 3275mA */ + } else { + mA = (mA > 1650) ? 1650 : mA; + offset = ((mA - 100) / 25) & 0x3F; /* max = 1650mA */ + } + } + + return offset; +} +static int sm5705_CHG_set_INPUT_LIMIT(struct sm5705_charger_data *charger, + unsigned char index, unsigned short LIMIT_mA) +{ + unsigned char offset = _calc_INPUT_LIMIT_current_offset_to_mA(index, LIMIT_mA); + + pr_info("set Input LIMIT src=%d, current=%dmA offset=0x%x\n", index, LIMIT_mA, offset); + if (index > SM5705_CHG_SRC_WPC) + return -EINVAL; + sm5705_write_reg(charger->i2c, SM5705_REG_VBUSCNTL + index, offset); + + return 0; +} +static unsigned short _calc_INPUT_LIMIT_current_mA_to_offset(unsigned char index, + unsigned char offset) +{ + return (offset * 25) + 100; +} +static unsigned short sm5705_CHG_get_INPUT_LIMIT(struct sm5705_charger_data *charger, + unsigned char index) +{ + unsigned short LIMIT_mA; + unsigned char offset; + + if (index > SM5705_CHG_SRC_WPC) { + pr_err("invalid charger source index = %d\n", index); + return 0; + } + sm5705_read_reg(charger->i2c, SM5705_REG_VBUSCNTL + index, &offset); + LIMIT_mA = _calc_INPUT_LIMIT_current_mA_to_offset(index, offset); +#ifdef SM5705_CHG_FULL_DEBUG + pr_info("get INPUT LIMIT src=%d, offset=0x%x, current=%dmA\n", index, offset, LIMIT_mA); +#endif + + return LIMIT_mA; +} +static unsigned short _calc_FASTCHG_current_mA_to_offset(unsigned char index, + unsigned char offset) +{ + return (offset * 50) + 100; +} +static unsigned short sm5705_CHG_get_FASTCHG(struct sm5705_charger_data *charger, + unsigned char index) +{ + unsigned short FASTCHG_mA; + unsigned char offset; + + if (index > SM5705_CHG_SRC_WPC) { + pr_err("invalid charger source index = %d\n", index); + return 0; + } + sm5705_read_reg(charger->i2c, SM5705_REG_CHGCNTL2 + index, &offset); + FASTCHG_mA = _calc_FASTCHG_current_mA_to_offset(index, offset); + pr_info("get FASTCHG src=%d, offset=0x%x, current=%dmA\n", index, offset, FASTCHG_mA); + + return FASTCHG_mA; +} +/* monitering REG_MAP */ +static unsigned char sm5705_CHG_read_reg(struct sm5705_charger_data *charger, unsigned char reg) +{ + unsigned char reg_val = 0x0; + + sm5705_read_reg(charger->i2c, reg, ®_val); + + return reg_val; +} +static void sm5705_chg_test_read(struct sm5705_charger_data *charger) +{ + char str[1000] = {0,}; + int i, gpio; + + gpio = gpio_get_value(charger->pdata->chg_gpio_en); + + for (i = SM5705_REG_INTMSK1; i <= SM5705_REG_FLED1CNTL1; i++) + sprintf(str+strlen(str), "0x%02X:0x%02x, ", i, sm5705_CHG_read_reg(charger, i)); + sprintf(str+strlen(str), "0x%02X:0x%02x, ", SM5705_REG_FLEDCNTL6, + sm5705_CHG_read_reg(charger, SM5705_REG_FLEDCNTL6)); + sprintf(str+strlen(str), "0x%02X:0x%02x, ", SM5705_REG_SBPSCNTL, + sm5705_CHG_read_reg(charger, SM5705_REG_SBPSCNTL)); + pr_info("sm5705-charger: chg_en(%d) %s\n", gpio, str); +} + +/** + * SM5705 Charger Driver support functions + */ + +static bool sm5705_charger_check_oper_otg_mode_on(void) +{ + unsigned char current_status = sm5705_charger_oper_get_current_status(); + bool ret; + + if (current_status & (1 << SM5705_CHARGER_OP_EVENT_OTG)) + ret = true; + else + ret = false; + + return ret; +} +static bool sm5705_charger_get_charging_on_status(struct sm5705_charger_data *charger) +{ + return sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS2, SM5705_INT_STATUS2_CHGON); +} +static bool sm5705_charger_get_power_source_status(struct sm5705_charger_data *charger) +{ + int gpio = gpio_get_value(charger->pdata->chg_gpio_en); + + return ((gpio & 0x1) == 0); /* charging pin active LOW */ +} +static int sm5705_get_input_current(struct sm5705_charger_data *charger) +{ + int get_current; + + +#if defined(CONFIG_USE_POGO) + if (!is_not_wireless_type(charger->cable_type) || + charger->cable_type == SEC_BATTERY_CABLE_POGO) +#else + if (!is_not_wireless_type(charger->cable_type)) +#endif + get_current = sm5705_CHG_get_INPUT_LIMIT(charger, SM5705_CHG_SRC_WPC); + else + get_current = sm5705_CHG_get_INPUT_LIMIT(charger, SM5705_CHG_SRC_VBUS); +#ifdef SM5705_CHG_FULL_DEBUG + pr_info("src_type=%d, current=%d\n", is_not_wireless_type(charger->cable_type), get_current); +#endif + + return get_current; +} +static int sm5705_get_charge_current(struct sm5705_charger_data *charger) +{ + int get_current; + +#if defined(CONFIG_USE_POGO) + if (!(is_not_wireless_type(charger->cable_type)) || + charger->cable_type == SEC_BATTERY_CABLE_POGO) +#else + if (!(is_not_wireless_type(charger->cable_type))) +#endif + get_current = sm5705_CHG_get_FASTCHG(charger, SM5705_CHG_SRC_WPC); + else + get_current = sm5705_CHG_get_FASTCHG(charger, SM5705_CHG_SRC_VBUS); + pr_info("src_type=%d, current=%d\n", is_not_wireless_type(charger->cable_type), get_current); + + return get_current; +} +static void sm5705_enable_charging_on_switch(struct sm5705_charger_data *charger, bool enable) +{ + if ((enable == 0) & sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS1, SM5705_INT_STATUS1_VBUSPOK)) + sm5705_CHG_set_FREQSEL(charger, SM5705_BUCK_BOOST_FREQ_1_5MHz); + else + sm5705_CHG_set_FREQSEL(charger, SM5705_BUCK_BOOST_FREQ_3MHz); + gpio_direction_output(charger->pdata->chg_gpio_en, !(enable)); + if (!enable) { + /* SET: charging-off condition */ + charger->charging_current = 0; + charger->topoff_pending = 0; + } + pr_info("turn-%s Charging enable pin\n", enable ? "ON" : "OFF"); +} +static int sm5705_set_charge_current(struct sm5705_charger_data *charger, + unsigned short charge_current) +{ +#if defined(CONFIG_SEC_FACTORY) + if (factory_mode) { + pr_info("%s: Factory Mode Skip CC Control\n", __func__); + return 0; + } +#endif +#if defined(CONFIG_USE_POGO) + if (!(is_not_wireless_type(charger->cable_type)) || + charger->cable_type == SEC_BATTERY_CABLE_POGO) +#else + if (!(is_not_wireless_type(charger->cable_type))) +#endif + sm5705_CHG_set_FASTCHG(charger, SM5705_CHG_SRC_WPC, charge_current); + else + sm5705_CHG_set_FASTCHG(charger, SM5705_CHG_SRC_VBUS, charge_current); + + return 0; +} +static int sm5705_set_input_current(struct sm5705_charger_data *charger, + unsigned short input_current) +{ + if (!input_current) { + pr_info("skip process, input_current = 0\n"); + return 0; + } +#if defined(CONFIG_SEC_FACTORY) + if (factory_mode) { + pr_info("%s: Factory Mode Skip IC Control\n", __func__); + return 0; + } +#endif +#if defined(CONFIG_USE_POGO) + if (!is_not_wireless_type(charger->cable_type) || + charger->cable_type == SEC_BATTERY_CABLE_POGO) +#else + if (!is_not_wireless_type(charger->cable_type)) +#endif + sm5705_CHG_set_INPUT_LIMIT(charger, SM5705_CHG_SRC_WPC, input_current); + else + sm5705_CHG_set_INPUT_LIMIT(charger, SM5705_CHG_SRC_VBUS, input_current); + + return 0; +} +#if 0 +static void sm5705_charger_set_TOPOFF_current(struct sm5705_charger_data *charger) +{ + union power_supply_propval chg_mode; + union power_supply_propval swelling_state; + unsigned int topoff; + + if (charger->pdata->full_check_type_2nd == SEC_BATTERY_FULLCHARGED_CHGPSY) { + psy_do_property("battery", get, POWER_SUPPLY_PROP_CHARGE_NOW, chg_mode); +#if defined(CONFIG_BATTERY_SWELLING) + psy_do_property("battery", get, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, swelling_state); +#else + swelling_state.intval = 0; +#endif + if (chg_mode.intval == SEC_BATTERY_CHARGING_2ND || swelling_state.intval) { + topoff = charger->pdata->charging_current[charger->cable_type].full_check_current_2nd; + } else { + topoff = charger->pdata->charging_current[charger->cable_type].full_check_current_1st; + } + pr_info("full_check_type_2nd=%d, chg_mode=%d, swelling_state=%d\n", + charger->pdata->full_check_type_2nd, chg_mode.intval, swelling_state.intval); + } else { + topoff = charger->pdata->charging_current[charger->cable_type].full_check_current_1st; + pr_info("full_check_type_2nd=%d\n", charger->pdata->full_check_type_2nd); + } + + sm5705_CHG_set_TOPOFF(charger, topoff); +} + +/** + * SM5705 Power-supply class management functions + */ +static void sm5705_configure_charger(struct sm5705_charger_data *charger) +{ + sm5705_CHG_set_BATREG(charger, charger->pdata->chg_float_voltage); + sm5705_set_current(charger); + sm5705_charger_set_TOPOFF_current(charger); +} +#endif +static void psy_chg_set_charging_enabled(struct sm5705_charger_data *charger, int charge_mode) +{ + int buck_state = true; + +#if defined(CONFIG_SEC_FACTORY) + if (factory_mode) { + pr_info("%s: Factory Mode Skip CHG_EN Control\n", __func__); + return; + } +#endif + dev_info(charger->dev, "charger_mode changed [%d] -> [%d]\n", charger->charge_mode, charge_mode); + charger->charge_mode = charge_mode; + switch (charger->charge_mode) { + case SEC_BAT_CHG_MODE_BUCK_OFF: + buck_state = false; + case SEC_BAT_CHG_MODE_CHARGING_OFF: + charger->is_charging = false; + break; + case SEC_BAT_CHG_MODE_CHARGING: + charger->is_charging = true; + break; + } + if (buck_state == DISABLE) + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_SUSPEND_MODE, true); + else + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_SUSPEND_MODE, false); + sm5705_enable_charging_on_switch(charger, charger->is_charging); +} +static void psy_chg_set_cable_online(struct sm5705_charger_data *charger, int cable_type) +{ + int prev_cable_type = charger->cable_type; + union power_supply_propval value; + + charger->slow_late_chg_mode = false; + charger->cable_type = cable_type; + pr_info("[start] prev_cable_type(%d), cable_type(%d), op_mode(%d), op_status(0x%x)", + prev_cable_type, charger->cable_type, + sm5705_charger_oper_get_current_op_mode(), + sm5705_charger_oper_get_current_status()); + + if (charger->cable_type == SEC_BATTERY_CABLE_POWER_SHARING) { + charger->is_charging = false; + psy_do_property("ps", get, POWER_SUPPLY_PROP_STATUS, value); + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_PWR_SHAR, value.intval); + } else if (charger->cable_type == SEC_BATTERY_CABLE_OTG) { + charger->is_charging = false; +#if 0//defined(CONFIG_SM5705_SUPPORT_GAMEPAD) + if (prev_cable_type == POWER_SUPPLY_TYPE_MDOCK_TA) + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_VBUS, 0); +#endif + pr_info("OTG enable, cable(%d)\n", charger->cable_type); + } else if (charger->cable_type == SEC_BATTERY_CABLE_NONE) { + /* set default value */ + charger->is_charging = false; + charger->aicl_on = false; + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_VBUS, 0); + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_PWR_SHAR, 0); +#if defined(CONFIG_USE_POGO) + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_WPC, 0); +#endif + cancel_delayed_work(&charger->aicl_work); + /* set default input current */ + psy_do_property("battery", get, POWER_SUPPLY_PROP_HEALTH, value); + if ((charger->status == POWER_SUPPLY_STATUS_DISCHARGING) || + (value.intval == POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) || + (value.intval == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) { + charger->input_current = ((value.intval == POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) || \ + (value.intval == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) ? + 0 : charger->pdata->charging_current[SEC_BATTERY_CABLE_USB].input_current_limit; + } + } else { + charger->input_current = charger->pdata->charging_current[charger->cable_type].input_current_limit; +#if defined(CONFIG_USE_POGO) + if(charger->cable_type == SEC_BATTERY_CABLE_POGO) + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_WPC, 1); + else +#endif + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_VBUS, 1); + pr_info("request aicl work for 5V-TA\n"); + queue_delayed_work(charger->wqueue, &charger->aicl_work, msecs_to_jiffies(3000)); + + charger->aicl_on = false; + cancel_delayed_work(&charger->slow_chg_work); + queue_delayed_work(charger->wqueue, + &charger->slow_chg_work, msecs_to_jiffies(3000)); + } + pr_info("[end] is_charging=%d(%d), fc = %d, il = %d, cable = %d," + "op_mode(%d), op_status(0x%x)\n", charger->is_charging, sm5705_charger_get_power_source_status(charger), + charger->charging_current, charger->input_current, charger->cable_type, + sm5705_charger_oper_get_current_op_mode(), + sm5705_charger_oper_get_current_status()); +} +static void psy_chg_set_usb_hc(struct sm5705_charger_data *charger, int value) +{ + if (value) { + /* set input/charging current for usb up to TA's current */ + charger->pdata->charging_current[SEC_BATTERY_CABLE_USB].fast_charging_current = + charger->pdata->charging_current[SEC_BATTERY_CABLE_TA].fast_charging_current; + charger->pdata->charging_current[SEC_BATTERY_CABLE_USB].input_current_limit = + charger->pdata->charging_current[SEC_BATTERY_CABLE_TA].input_current_limit; + } else { + /* restore input/charging current for usb */ + charger->pdata->charging_current[SEC_BATTERY_CABLE_USB].fast_charging_current = + charger->pdata->charging_current[SEC_BATTERY_CABLE_NONE].input_current_limit; + charger->pdata->charging_current[SEC_BATTERY_CABLE_USB].input_current_limit = + charger->pdata->charging_current[SEC_BATTERY_CABLE_NONE].input_current_limit; + } +} +#if defined(SM5705_SUPPORT_OTG_CONTROL) +static void psy_chg_set_charge_otg_control(struct sm5705_charger_data *charger, int otg_en) +{ + union power_supply_propval value; + + psy_do_property("wireless", get, POWER_SUPPLY_PROP_ONLINE, value); + +#if defined(CONFIG_USE_POGO) + if (otg_en) { +#else + if (otg_en && !value.intval) { +#endif + /* OTG - Enable */ + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_OTG, true); + pr_info("OTG enable, cable(%d)\n", charger->cable_type); + } else { + /* OTG - Disable */ + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_OTG, false); + pr_info("OTG disable, cable(%d)\n", charger->cable_type); + } +} +#endif +#if defined(SM5705_SUPPORT_AICL_CONTROL) +static void psy_chg_set_aicl_control(struct sm5705_charger_data *charger, int aicl_en) +{ + if (aicl_en) { + sm5705_CHG_enable_AICL(charger, 1); + pr_info("CHGIN AICL ENABLE\n"); + } else { + sm5705_CHG_enable_AICL(charger, 0); + pr_info("CHGIN AICL DISABLE\n"); + } +} +#endif +#if defined(CONFIG_AFC_CHARGER_MODE) +extern void muic_charger_init(void); +static void psy_chg_set_afc_charger_mode(struct sm5705_charger_data *charger, int afc_mode) +{ + pr_info("afc_charger_mode value = %d\n", afc_mode); +#if defined(CONFIG_MUIC_UNIVERSAL_SM5705) + muic_charger_init(); +#endif +} +#endif +static int sm5705_chg_set_property(struct power_supply *psy, enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sm5705_charger_data *charger = power_supply_get_drvdata(psy); + enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + dev_info(charger->dev, "CHG:POWER_SUPPLY_PROP_STATUS - status=%d\n", val->intval); + charger->status = val->intval; + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + dev_info(charger->dev, "CHG:POWER_SUPPLY_PROP_CHARGING_ENABLED (val=%d)\n", val->intval); + psy_chg_set_charging_enabled(charger, val->intval); + sm5705_chg_test_read(charger); + break; + case POWER_SUPPLY_PROP_ONLINE: + psy_chg_set_cable_online(charger, val->intval); + break; + /* set input current */ + case POWER_SUPPLY_PROP_CURRENT_MAX: + dev_info(charger->dev, "CHG:POWER_SUPPLY_PROP_CURRENT_MAX (val=%d)\n", val->intval); + charger->input_current = val->intval; + sm5705_set_input_current(charger, charger->input_current); + break; + /* set charge current */ + case POWER_SUPPLY_PROP_CURRENT_AVG: + dev_info(charger->dev, "CHG:POWER_SUPPLY_PROP_CURRENT_AVG (val=%d)\n", val->intval); + charger->charging_current = val->intval; + sm5705_set_charge_current(charger, charger->charging_current); + break; + /* set charge current but not for wireless */ + case POWER_SUPPLY_PROP_CURRENT_NOW: + dev_info(charger->dev, "CHG:POWER_SUPPLY_PROP_CURRENT_NOW (val=%d)\n", val->intval); + if (!is_wireless_type(charger->cable_type)) { + charger->charging_current = val->intval; + sm5705_set_charge_current(charger, charger->charging_current); + } + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + break; +#if defined(CONFIG_BATTERY_SWELLING) + case POWER_SUPPLY_PROP_VOLTAGE_MAX: +#if defined(CONFIG_SEC_FACTORY) + // in case of open the batt therm on sub pcb, keep the default float voltage + dev_info(charger->dev, "keep the default float voltage\n"); + break; +#else + dev_info(charger->dev, "CHG:POWER_SUPPLY_PROP_VOLTAGE_MAX (val=%d)\n", val->intval); + charger->pdata->chg_float_voltage = val->intval; + sm5705_CHG_set_BATREG(charger, val->intval); + break; +#endif +#endif + case POWER_SUPPLY_PROP_USB_HC: + pr_info("POWER_SUPPLY_PROP_USB_HC - value=%d\n", val->intval); + psy_chg_set_usb_hc(charger, val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + return -EINVAL; +#if defined(SM5705_SUPPORT_AICL_CONTROL) + case POWER_SUPPLY_PROP_CHARGE_AICL_CONTROL: + pr_info("POWER_SUPPLY_PROP_CHARGE_AICL_CONTROL - aicl_en=%d\n", val->intval); + psy_chg_set_aicl_control(charger, val->intval); + break; +#endif + case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: + return -EINVAL; + case POWER_SUPPLY_PROP_ENERGY_NOW: + /* WA: abnormal swiching case in JIG cable */ + if (sm5705_call_fg_device_id() <= 2 && !charger->is_rev2_wa_done) { + if (val->intval) { + pr_info("queue_delayed_work, op_mode_switch_work\n"); + cancel_delayed_work(&charger->op_mode_switch_work); + queue_delayed_work(charger->wqueue, &charger->op_mode_switch_work, + msecs_to_jiffies(8000)); /* delay 8sec */ + } else + cancel_delayed_work(&charger->op_mode_switch_work); + } + break; +#if defined(CONFIG_USE_POGO) + case POWER_SUPPLY_PROP_CHARGE_TYPE: + pr_info("POWER_SUPPLY_PROP_CHARGE_TYPE- pogo_work=%d \n", val->intval); + wake_lock(&charger->wpc_wake_lock); + cancel_delayed_work(&charger->pogo_work); + queue_delayed_work(charger->wqueue, &charger->pogo_work, 0); + break; +#endif + case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: + switch (ext_psp) { + case POWER_SUPPLY_EXT_PROP_CURRENT_FULL: + dev_info(charger->dev, "CHG:POWER_SUPPLY_EXT_PROP_CURRENT_FULL (val=%d)\n", val->intval); + sm5705_CHG_set_TOPOFF(charger, val->intval); + break; +#if defined(CONFIG_AFC_CHARGER_MODE) + case POWER_SUPPLY_EXT_PROP_AFC_CHARGER_MODE: + psy_chg_set_afc_charger_mode(charger, val->intval); + break; +#endif +#if defined(SM5705_SUPPORT_OTG_CONTROL) + case POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL: + pr_info("POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL - otg_en=%d\n", val->intval); + psy_chg_set_charge_otg_control(charger, val->intval); + break; +#endif + default: + return -EINVAL; + } + break; + default: + pr_err("un-known Power-supply property type (psp=%d)\n", psp); + return -EINVAL; + } + + return 0; +} +static int psy_chg_get_online(struct sm5705_charger_data *charger) +{ + int src_type; + + if (sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS1, SM5705_INT_STATUS1_VBUSPOK)) + src_type = SEC_BATTERY_CABLE_TA; + else if (sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS1, SM5705_INT_STATUS1_WPCINPOK)) + src_type = SEC_BATTERY_CABLE_WIRELESS; + else + src_type = SEC_BATTERY_CABLE_NONE; + + return src_type; +} +static bool _decide_charge_full_status(struct sm5705_charger_data *charger) +{ + if ((sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS2, SM5705_INT_STATUS2_TOPOFF)) || + (sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS2, SM5705_INT_STATUS2_DONE))) + return charger->topoff_pending; + + return false; +} +static int psy_chg_get_charger_state(struct sm5705_charger_data *charger) +{ + int status = POWER_SUPPLY_STATUS_UNKNOWN; + + if (_decide_charge_full_status(charger)) + status = POWER_SUPPLY_STATUS_FULL; + else if(sm5705_charger_get_charging_on_status(charger)) + status = POWER_SUPPLY_STATUS_CHARGING; + else { + if (sm5705_charger_get_power_source_status(charger)) + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + return status; +} + +static int psy_chg_get_charge_type(struct sm5705_charger_data *charger) +{ + int charge_type; + + if (sm5705_charger_get_charging_on_status(charger)) { + if(charger->aicl_on==true) + charge_type = POWER_SUPPLY_CHARGE_TYPE_SLOW; + else + charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; + } else { + charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE; + } + + pr_info("%s charge_type(%d)\n",__func__,charge_type); + return charge_type; +} + +static int psy_chg_get_charging_health(struct sm5705_charger_data *charger) +{ + int state; + unsigned char reg_data; + + sm5705_read_reg(charger->i2c, SM5705_REG_STATUS1, ®_data); +#if !defined(CONFIG_USE_POGO) + if (charger->cable_type != SEC_BATTERY_CABLE_WIRELESS) { +#else + if (charger->cable_type != SEC_BATTERY_CABLE_POGO) { +#endif + if (reg_data & (1 << SM5705_INT_STATUS1_VBUSPOK)) + state = POWER_SUPPLY_HEALTH_GOOD; + else if (reg_data & (1 << SM5705_INT_STATUS1_VBUSOVP)) + state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (reg_data & (1 << SM5705_INT_STATUS1_VBUSUVLO)) + state = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + else + state = POWER_SUPPLY_HEALTH_UNKNOWN; + } else { + if (reg_data & (1 << SM5705_INT_STATUS1_WPCINPOK)) + state = POWER_SUPPLY_HEALTH_GOOD; + else if (reg_data & (1 << SM5705_INT_STATUS1_WPCINOVP)) + state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (reg_data & (1 << SM5705_INT_STATUS1_WPCINUVLO)) + state = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + else + state = POWER_SUPPLY_HEALTH_UNKNOWN; + } + + return (int)state; +} +static int sm5705_chg_create_attrs(struct device *dev) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(sm5705_charger_attrs); i++) { + rc = device_create_file(dev, &sm5705_charger_attrs[i]); + if (rc) + goto create_attrs_failed; + } + + return rc; + +create_attrs_failed: + pr_err("failed (%d)\n", rc); + while (i--) + device_remove_file(dev, &sm5705_charger_attrs[i]); + + return rc; +} +ssize_t sm5705_chg_show_attrs(struct device *dev, struct device_attribute *attr, char *buf) +{ + const ptrdiff_t offset = attr - sm5705_charger_attrs; + struct power_supply *psy = dev_get_drvdata(dev); + struct sm5705_charger_data *charger = power_supply_get_drvdata(psy); + int i = 0; + unsigned char reg_data; + u8 addr, data; + + switch (offset) { + case CHIP_ID: + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", "SM5705"); + break; + case CHARGER_OP_MODE: + sm5705_read_reg(charger->i2c, SM5705_REG_CNTL, ®_data); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",reg_data); + break; + case DATA: + for (addr = SM5705_REG_STATUS1; addr <= SM5705_REG_CHGCNTL8; addr++) { + sm5705_read_reg(charger->i2c, addr, &data); + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%02x : 0x%02x\n", addr, data); + } + break; + default: + return -EINVAL; + } + + return i; +} +ssize_t sm5705_chg_store_attrs(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + const ptrdiff_t offset = attr - sm5705_charger_attrs; + int ret = 0; + int x,y; + struct power_supply *psy = dev_get_drvdata(dev); + struct sm5705_charger_data *charger = power_supply_get_drvdata(psy); + + switch(offset) { + case CHIP_ID: + ret = count; + break; + case CHARGER_OP_MODE: + if (sscanf(buf, "%10d\n", &x) == 1) { + if (x == 2) + sm5705_update_reg(charger->i2c, SM5705_REG_CNTL, SM5705_CHARGER_OP_MODE_SUSPEND, 0x07); + else if(x == 1) + sm5705_update_reg(charger->i2c, SM5705_REG_CNTL, SM5705_CHARGER_OP_MODE_CHG_ON, 0x07); + else { + pr_info("change charger op mode fail\n"); + return -EINVAL; + } + ret = count; + } + break; + case DATA: + if (sscanf(buf, "0x%8x 0x%8x", &x, &y) == 2) { + if (x >= SM5705_REG_STATUS1 && x <= SM5705_REG_CHGCNTL8) { + u8 addr = x; + u8 data = y; + if (sm5705_write_reg(charger->i2c, addr, data) < 0) + dev_info(charger->dev, "%s: addr: 0x%x write fail\n", __func__, addr); + } else + dev_info(charger->dev, "%s: addr: 0x%x is wrong\n", __func__, x); + } + ret = count; + break; + default: + ret = -EINVAL; + } + return ret; +} +static int sm5705_chg_get_property(struct power_supply *psy, enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sm5705_charger_data *charger = power_supply_get_drvdata(psy); + enum power_supply_ext_property ext_psp = (enum power_supply_ext_property)psp; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = psy_chg_get_online(charger); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS2, SM5705_INT_STATUS2_NOBAT); + break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = psy_chg_get_charger_state(charger); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = psy_chg_get_charge_type(charger); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = psy_chg_get_charging_health(charger); + sm5705_chg_test_read(charger); + break; + /* get input current which was set */ + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = charger->input_current; + break; + /* get input current which was read */ + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = sm5705_get_input_current(charger); + break; + /* get charge current which was set */ + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = charger->charging_current; + break; + /* get charge current which was read */ + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + val->intval = sm5705_get_charge_current(charger); + break; +#if defined(CONFIG_BATTERY_SWELLING) + /* get float voltage */ + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = sm5705_CHG_get_BATREG(charger); + break; +#endif + case POWER_SUPPLY_PROP_USB_HC: + return -ENODATA; + /* get cv mode */ + case POWER_SUPPLY_PROP_CHARGE_NOW: + return -ENODATA; +#if defined(SM5705_SUPPORT_AICL_CONTROL) + case POWER_SUPPLY_PROP_CHARGE_AICL_CONTROL: + return -ENODATA; +#endif + case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: + return -ENODATA; + case POWER_SUPPLY_PROP_ENERGY_NOW: + return -ENODATA; + case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: + switch (ext_psp) { + case POWER_SUPPLY_EXT_PROP_CHIP_ID: + { + u8 reg_data; + val->intval =(sm5705_read_reg(charger->i2c, SM5705_REG_CHGCNTL4, ®_data) == 0); + } + break; +#if defined(CONFIG_AFC_CHARGER_MODE) + case POWER_SUPPLY_EXT_PROP_AFC_CHARGER_MODE: + return -ENODATA; +#endif +#if defined(SM5705_SUPPORT_OTG_CONTROL) + case POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL: + val->intval = sm5705_charger_check_oper_otg_mode_on(); + //val->intval = 0; // disable new otg ui concept. + break; +#endif + default: + return -EINVAL; + } + break; + default: + pr_err("un-known Power-supply property type (psp=%d)\n", psp); + return -EINVAL; + } + + return 0; +} +static int sm5705_otg_get_property(struct power_supply *psy, enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = sm5705_charger_check_oper_otg_mode_on(); + //val->intval = 0; // disable new otg ui concept. + pr_info("POWER_SUPPLY_PROP_ONLINE - %s\n", (val->intval) ? "ON" : "OFF"); + break; + default: + return -EINVAL; + } + + return 0; +} +static int sm5705_otg_set_property(struct power_supply *psy, enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sm5705_charger_data *charger = power_supply_get_drvdata(psy); + union power_supply_propval value; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + pr_info("POWER_SUPPLY_PROP_ONLINE - %s\n", (val->intval) ? "ON" : "OFF"); +#if defined(SM5705_SUPPORT_OTG_CONTROL) + value.intval = val->intval; + psy_do_property("sm5705-charger", set, (enum power_supply_property) POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value); +#else + if (val->intval) + value.intval = SEC_BATTERY_CABLE_OTG; + else + value.intval = SEC_BATTERY_CABLE_NONE; + psy_do_property("sm5705-charger", set, POWER_SUPPLY_PROP_ONLINE, value); +#endif + power_supply_changed(charger->psy_otg); + break; + default: + return -EINVAL; + } + + return 0; +} +static enum power_supply_property sm5705_otg_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +/** + * SM5705 Charger IRQ & Work-queue service management functions + */ + + static void sm5705_op_mode_switch_work(struct work_struct *work) +{ + struct sm5705_charger_data *charger = + container_of(work, struct sm5705_charger_data, op_mode_switch_work.work); + + pr_info("schedule work start.\n"); + charger->is_rev2_wa_done = true; + /* OP Mode switch : CHG_ON(init) -> USB_OTG -> CHG_ON -> FLASH_BOOST -> CHG_ON */ + sm5705_update_reg(charger->i2c, SM5705_REG_CNTL, SM5705_CHARGER_OP_MODE_USB_OTG, 0x07); + msleep(2000); + sm5705_update_reg(charger->i2c, SM5705_REG_CNTL, SM5705_CHARGER_OP_MODE_CHG_ON, 0x07); + msleep(3000); + sm5705_update_reg(charger->i2c, SM5705_REG_CNTL, SM5705_CHARGER_OP_MODE_FLASH_BOOST, 0x07); + msleep(2000); + sm5705_update_reg(charger->i2c, SM5705_REG_CNTL, SM5705_CHARGER_OP_MODE_CHG_ON, 0x07); + pr_info("schedule work done.\n"); +} + +#if defined(CONFIG_USE_POGO) +static void sm5705_pogo_work(struct work_struct *work) +{ + struct sm5705_charger_data *charger = + container_of(work, struct sm5705_charger_data, pogo_work.work); + union power_supply_propval value; + int wpcin_state; + unsigned char reg_data; + + sm5705_read_reg(charger->i2c, SM5705_REG_STATUS1, ®_data); + pr_info("%s: REG_STATUS1=0x%x\n", __func__, reg_data); + + wpcin_state = (reg_data & SM5705_STATUS1_WPCINPOK) ? 1 : 0; + + pr_info("irq_wpcin_state = %d, wpcin_state = %d \n", charger->irq_wpcin_state, wpcin_state); + + if ((charger->irq_wpcin_state == 0) && (wpcin_state == 1)) { + value.intval = 1; + psy_do_property("pogo", set, POWER_SUPPLY_PROP_ONLINE, value); + + pr_info("%s: pogo activated\n", __func__); + } else if ((charger->irq_wpcin_state == 1) && (wpcin_state == 0)) { + value.intval = 0; + psy_do_property("pogo", set, POWER_SUPPLY_PROP_ONLINE, value); + pr_info("%s: pogo de-activated\n", __func__); + + if (sm5705_charger_check_oper_otg_mode_on()) { + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_VBUS, DISABLE); + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_WPC, DISABLE); + sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_OTG, ENABLE); + pr_info("update op_mode : SM5705_CHARGER_OP_MODE_OTG_ON\n"); + } + } + + pr_info("w(%d to %d)\n", charger->irq_wpcin_state, wpcin_state); + + charger->irq_wpcin_state = wpcin_state; + + wake_unlock(&charger->wpc_wake_lock); + pr_info("schedule pogo work done.\n"); +} +#endif /* CONFIG_USE_POGO */ + +static void sm5705_topoff_work(struct work_struct *work) +{ + struct sm5705_charger_data *charger = + container_of(work, struct sm5705_charger_data, topoff_work.work); + bool topoff = 1; + int i; + + pr_info("schedule work start.\n"); + for (i=0; i < 3; ++i) { + topoff &= sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS2, SM5705_INT_STATUS2_TOPOFF); + msleep(150); + pr_info("%dth Check TOP-OFF state=%d\n", i, topoff); + } + charger->topoff_pending = topoff; + pr_info("schedule work done.\n"); +} +static void _reduce_input_limit_current(struct sm5705_charger_data *charger, int cur) +{ + unsigned short vbus_limit_current = sm5705_get_input_current(charger); + + if ((vbus_limit_current <= MINIMUM_INPUT_CURRENT) || (vbus_limit_current <= cur)) + return; + vbus_limit_current = ((vbus_limit_current - cur) < MINIMUM_INPUT_CURRENT) ? + MINIMUM_INPUT_CURRENT : vbus_limit_current - cur; + sm5705_set_input_current(charger, vbus_limit_current); + charger->input_current = sm5705_get_input_current(charger); + dev_info(charger->dev, "vbus_limit_current=%d, input_current=%d\n", + vbus_limit_current, charger->input_current); +} +static void _check_slow_charging(struct sm5705_charger_data *charger) +{ + union power_supply_propval value; + + /* under 400mA considered as slow charging concept for VZW */ + if (charger->input_current <= SLOW_CHARGING_CURRENT_STANDARD && + charger->cable_type != SEC_BATTERY_CABLE_NONE) { + dev_info(charger->dev, "slow-rate charging on : input current(%dmA), cable-type(%d)\n", + charger->input_current, charger->cable_type); + charger->slow_late_chg_mode = true; + value.intval = POWER_SUPPLY_CHARGE_TYPE_SLOW; + charger->aicl_on = true; + psy_do_property("battery", set, POWER_SUPPLY_PROP_CHARGE_TYPE, value); + } +} +static bool _check_aicl_state(struct sm5705_charger_data *charger) +{ + return sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS2, SM5705_INT_STATUS2_AICL); +} +static void sm5705_aicl_work(struct work_struct *work) +{ + struct sm5705_charger_data *charger = + container_of(work, struct sm5705_charger_data, aicl_work.work); + int prev_current_max, max_count, now_count = 0; + + if (sm5705_call_fg_device_id() <= 2) { + pr_info("don't support AICL work at REV.2\n"); + return; + } + if (!sm5705_charger_get_charging_on_status(charger) || + is_hv_wire_type(charger->cable_type)) { + pr_info("don't need AICL work\n"); + return; + } +#if defined(CONFIG_SEC_FACTORY) + if (factory_mode) { + pr_info("%s: Factory Mode Skip AICL Control\n", __func__); + return; + } +#endif + dev_info(charger->dev, "%s - start\n", __func__); + /* Reduce input limit current */ + max_count = charger->input_current / REDUCE_CURRENT_STEP; + prev_current_max = charger->input_current; + while (_check_aicl_state(charger) && (now_count++ < max_count)) { + _reduce_input_limit_current(charger, REDUCE_CURRENT_STEP); + msleep(AICL_VALID_CHECK_DELAY_TIME); + } + if (_check_aicl_state(charger)) { + union power_supply_propval value; + value.intval = sm5705_get_input_current(charger); + psy_do_property("battery", set, (enum power_supply_property) POWER_SUPPLY_EXT_PROP_AICL_CURRENT, value); + } + if (prev_current_max > charger->input_current) { + pr_info("input_current(%d --> %d)\n", prev_current_max, charger->input_current); + _check_slow_charging(charger); + } + dev_info(charger->dev, "%s - done\n", __func__); +} + +static void slow_chg_detect_work(struct work_struct *work) +{ + struct sm5705_charger_data *charger = + container_of(work, struct sm5705_charger_data, slow_chg_work.work); + union power_supply_propval value; + + if (sm5705_get_input_current(charger) <= SLOW_CHARGING_CURRENT_STANDARD){ + if(charger->aicl_on != true){ + charger->aicl_on = true; + value.intval = POWER_SUPPLY_CHARGE_TYPE_SLOW; + psy_do_property("battery", set, POWER_SUPPLY_PROP_CHARGE_TYPE, value); + } + } else + charger->aicl_on = false; + + pr_info("%s aicl_on(%d)\n",__func__,charger->aicl_on); +} + +#if 0 +static void wpc_detect_work(struct work_struct *work) +{ + struct sm5705_charger_data *charger = container_of(work, struct sm5705_charger_data, wpc_work.work); + union power_supply_propval value; + int wpcin_state; + + pr_info("schedule work start.\n"); + + wpcin_state = !gpio_get_value(charger->pdata->irq_gpio); + + pr_info("wc_w_state = %d \n", wpcin_state); + + if ((charger->irq_wpcin_state == 0) && (wpcin_state == 1)) { + value.intval = 1; + psy_do_property("wireless", set, POWER_SUPPLY_PROP_ONLINE, value); + value.intval = SEC_BATTERY_CABLE_WIRELESS; + psy_do_property(charger->pdata->wireless_charger_name, set, POWER_SUPPLY_PROP_ONLINE, value); + + pr_info("wpc activated, set V_INT as PN\n"); + } else if ((charger->irq_wpcin_state == 1) && (wpcin_state == 0)) { + value.intval = 0; + psy_do_property("wireless", set, POWER_SUPPLY_PROP_ONLINE, value); + + pr_info("wpc deactivated, set V_INT as PD\n"); + } + + pr_info("w(%d to %d)\n", charger->irq_wpcin_state, wpcin_state); + + charger->irq_wpcin_state = wpcin_state; + + wake_unlock(&charger->wpc_wake_lock); + + pr_info("wpc detect schedule work done.\n"); +} +#endif +static unsigned char _get_valid_vbus_status(struct sm5705_charger_data *charger) +{ + unsigned char vbusin, prev_vbusin = 0xff; + int stable_count = 0; + + while (1) { + sm5705_read_reg(charger->i2c, SM5705_REG_STATUS1, &vbusin); + vbusin &= 0xF; + if (prev_vbusin == vbusin) + stable_count++; + else { + pr_info("VBUS status mismatch (0x%x / 0x%x), Reset stable count\n", + vbusin, prev_vbusin); + stable_count = 0; + } + if (stable_count == 10) + break; + prev_vbusin = vbusin; + msleep(10); + } + + return vbusin; +} +static int _check_vbus_power_supply_status(struct sm5705_charger_data *charger, + unsigned char vbus_status, int prev_battery_health) +{ + int battery_health = prev_battery_health; + + if (vbus_status & (1 << SM5705_INT_STATUS1_VBUSPOK)) { + if (prev_battery_health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) { + pr_info("overvoltage->normal\n"); + battery_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (prev_battery_health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE) { + pr_info("undervoltage->normal\n"); + battery_health = POWER_SUPPLY_HEALTH_GOOD; + } + } else { + if ((vbus_status & (1 << SM5705_INT_STATUS1_VBUSOVP)) && + (prev_battery_health != POWER_SUPPLY_HEALTH_OVERVOLTAGE)) { + pr_info("charger is over voltage\n"); + battery_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + } else if ((vbus_status & (1 << SM5705_INT_STATUS1_VBUSUVLO)) && + (prev_battery_health != POWER_SUPPLY_HEALTH_UNDERVOLTAGE) && + is_not_wireless_type(charger->cable_type) && +#if defined(CONFIG_USE_POGO) + charger->cable_type != SEC_BATTERY_CABLE_POGO && +#endif + (charger->cable_type != SEC_BATTERY_CABLE_NONE)) { + pr_info("vBus is undervoltage\n"); + battery_health = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + } + } + + return battery_health; +} +static irqreturn_t sm5705_chg_vbus_in_isr(int irq, void *data) +{ + struct sm5705_charger_data *charger = data; + union power_supply_propval value; + unsigned char vbus_status; + int prev_battery_health; + + pr_info("start.\n"); + vbus_status = _get_valid_vbus_status(charger); + psy_do_property("battery", get,POWER_SUPPLY_PROP_HEALTH, value); + prev_battery_health = value.intval; + value.intval = _check_vbus_power_supply_status(charger, vbus_status, prev_battery_health); + if (prev_battery_health != value.intval) + psy_do_property("battery", set, POWER_SUPPLY_PROP_HEALTH, value); + pr_info("battery change status [%d] -> [%d] (VBUS_REG:0x%x)\n", + prev_battery_health, value.intval, vbus_status); + /* + if (prev_battery_health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE && + value.intval == POWER_SUPPLY_HEALTH_GOOD) { + sm5705_set_input_current(charger, charger->input_current); + } + */ + pr_info("done.\n"); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_USE_POGO) +static irqreturn_t sm5705_chg_wpcin_pok_pogo_isr(int irq, void *data) +{ + struct sm5705_charger_data *charger = data; + unsigned long delay; + + delay = msecs_to_jiffies(0); + pr_info("POGO IRQ=%d delay = %ld\n", irq, delay); + + wake_lock(&charger->wpc_wake_lock); + cancel_delayed_work(&charger->pogo_work); + queue_delayed_work(charger->wqueue, &charger->pogo_work, delay); + + return IRQ_HANDLED; +} +#endif + +#if 0 +static irqreturn_t sm5705_chg_topoff_isr(int irq, void *data) +{ + struct sm5705_charger_data *charger = data; + + pr_info("IRQ=%d\n", irq); + + charger->topoff_pending = 0; + queue_delayed_work(charger->wqueue, &charger->topoff_work, msecs_to_jiffies(500)); + + return IRQ_HANDLED; +} +#endif +static irqreturn_t sm5705_chg_done_isr(int irq, void *data) +{ + struct sm5705_charger_data *charger = data; + + pr_info("%s: start.\n", __func__); + /* nCHG pin toggle */ + gpio_direction_output(charger->pdata->chg_gpio_en, charger->is_charging); + msleep(10); + //gpio_direction_output(charger->pdata->chg_gpio_en, !(charger->is_charging)); + + return IRQ_HANDLED; +} +static irqreturn_t sm5705_chg_otg_fail_isr(int irq, void *data) +{ + struct sm5705_charger_data *charger = data; + union power_supply_propval value = {0, }; + u8 otg_state = 0; +#ifdef CONFIG_USB_HOST_NOTIFY + struct otg_notify *o_notify; + + o_notify = get_otg_notify(); +#endif + pr_info("%s: start.\n", __func__); + otg_state = sm5705_CHG_get_INT_STATUS(charger, SM5705_INT_STATUS3, SM5705_INT_STATUS3_OTGFAIL); + if(otg_state) { + dev_info(charger->dev, "%s OTG fail !! \n", __func__); +#ifdef CONFIG_USB_HOST_NOTIFY + send_otg_notify(o_notify, NOTIFY_EVENT_OVERCURRENT, 0); +#endif + /* disable the register values just related to OTG and + keep the values about the charging */ + value.intval = 0; + psy_do_property("otg", set, POWER_SUPPLY_PROP_ONLINE, value); + } + + return IRQ_HANDLED; +} +#if 0 +static irqreturn_t sm5705_chg_wpcin_pok_isr(int irq, void *data) +{ + struct sm5705_charger_data *charger = data; + unsigned long delay; + +#ifdef CONFIG_SAMSUNG_BATTERY_FACTORY + delay = msecs_to_jiffies(0); +#else + if (charger->irq_wpcin_state) + delay = msecs_to_jiffies(500); + else + delay = msecs_to_jiffies(0); +#endif + pr_info("IRQ=%d delay = %ld\n", irq, delay); + + wake_lock(&charger->wpc_wake_lock); + queue_delayed_work(charger->wqueue, &charger->wpc_work, delay); + + return IRQ_HANDLED; +} +#endif + +/** + * SM5705 Charger driver management functions + **/ + +#if 0 +static int _get_of_charging_current_table_max_size(struct device *dev, struct device_node *np) +{ + const unsigned int *propertys; + int len; + + propertys = of_get_property(np, "battery,input_current_limit", &len); + if (unlikely(!propertys)) { + dev_err(dev, "%s: can't parsing dt:battery,input_current_limit\n", __func__); + } else { + dev_info(dev, "%s: dt:battery,input_current_limit length=%d\n", __func__, len); + } + + return len / sizeof(unsigned int); +} +#endif +#ifdef CONFIG_OF +static int _parse_sm5705_charger_node_propertys(struct device *dev, struct device_node *np, + sec_charger_platform_data_t *pdata) +{ + pdata->chg_gpio_en = of_get_named_gpio(np, "battery,chg_gpio_en", 0); //nCHGEN + if (IS_ERR_VALUE(pdata->chg_gpio_en)) { + pr_info("can't parsing dt:battery,chg_gpio_en\n"); + return -ENOENT; + } + pr_info("battery charge enable pin = %d\n", pdata->chg_gpio_en); + + return 0; +} +static int _parse_battery_node_propertys(struct device *dev, struct device_node *np, + sec_charger_platform_data_t *pdata) +{ + int ret, i,len=0; + const u32 *p; + u32 temp = 0; + + ret = of_property_read_string(np,"battery,wirelss_charger_name", + (char const **)&pdata->wireless_charger_name); + if (IS_ERR_VALUE(ret)) + dev_err(dev, "%s: can't parsing dt:battery,wirelss_charger_name\n", __func__); + else + dev_info(dev, "%s: wireless charger name=%s\n", __func__, pdata->wireless_charger_name); + + ret = of_property_read_u32(np, "battery,full_check_type_2nd", &pdata->full_check_type_2nd); + if (IS_ERR_VALUE(ret)) + dev_err(dev, "%s: can't parsing dt:battery,full_check_type_2nd\n", __func__); + + ret = of_property_read_u32(np, "battery,chg_float_voltage", &pdata->chg_float_voltage); + if (IS_ERR_VALUE(ret)) + dev_err(dev, "%s: can't parsing dt:battery,chg_float_voltage\n", __func__); +#if 0 + array_max_size = _get_of_charging_current_table_max_size(dev, np); + if (array_max_size == 0) { + return -ENOENT; + } + pr_info("charging current table max size = %d\n", array_max_size); + + pdata->charging_current = kzalloc(sizeof(sec_charging_current_t) * array_max_size, GFP_KERNEL); + if (unlikely(!pdata->charging_current)) { + pr_err("fail to allocate memory for charging current table\n"); + return -ENOMEM; + } + + for(i = 0; i < array_max_size; ++i) { + of_property_read_u32_index(np, "battery,input_current_limit", + i, &pdata->charging_current[i].input_current_limit); + of_property_read_u32_index(np, "battery,fast_charging_current", + i, &pdata->charging_current[i].fast_charging_current); + } +#else + np = of_find_node_by_name(NULL, "cable-info"); + if (!np) + pr_err ("%s : np NULL\n", __func__); + else { + struct device_node *child; + u32 input_current = 0, charging_current = 0; + + ret = of_property_read_u32(np, "default_input_current", &input_current); + ret = of_property_read_u32(np, "default_charging_current", &charging_current); + + pdata->charging_current = + kzalloc(sizeof(sec_charging_current_t) * SEC_BATTERY_CABLE_MAX, GFP_KERNEL); + + for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++) { + pdata->charging_current[i].input_current_limit = (unsigned int)input_current; + pdata->charging_current[i].fast_charging_current = (unsigned int)charging_current; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_u32(child, "input_current", &input_current); + ret = of_property_read_u32(child, "charging_current", &charging_current); + + p = of_get_property(child, "cable_number", &len); + if (!p) + return 1; + len = len / sizeof(u32); + for (i = 0; i <= len; i++) { + ret = of_property_read_u32_index(child, "cable_number", i, &temp); + pdata->charging_current[temp].input_current_limit = (unsigned int)input_current; + pdata->charging_current[temp].fast_charging_current = (unsigned int)charging_current; + } + + } + for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++) { + pr_info("%s : CABLE_NUM(%d) INPUT(%d) CHARGING(%d)\n",__func__, i, + pdata->charging_current[i].input_current_limit, + pdata->charging_current[i].fast_charging_current); + } + } +#endif + pr_info("dt:battery node parse done.\n"); + + return 0; +} +static int sm5705_charger_parse_dt(struct sm5705_charger_data *charger, + struct sec_charger_platform_data *pdata) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_name(NULL, "sm5705-charger"); + if (np == NULL) { + pr_err("fail to find dt_node:sm5705-charger\n"); + return -ENOENT; + } else + ret = _parse_sm5705_charger_node_propertys(charger->dev, np, pdata); + + np = of_find_node_by_name(NULL, "battery"); + if (np == NULL) { + pr_err("fail to find dt_node:battery\n"); + return -ENOENT; + } else { + ret = _parse_battery_node_propertys(charger->dev, np, pdata); + if (IS_ERR_VALUE(ret)) + return ret; + } + + return ret; +} +#endif +static sec_charger_platform_data_t *_get_sm5705_charger_platform_data + (struct platform_device *pdev, struct sm5705_charger_data *charger) +{ +#ifdef CONFIG_OF + sec_charger_platform_data_t *pdata; + int ret; + + pdata = kzalloc(sizeof(sec_charger_platform_data_t), GFP_KERNEL); + if (!pdata) { + pr_err("fail to memory allocate for sec_charger_platform_data\n"); + return NULL; + } + + ret = sm5705_charger_parse_dt(charger, pdata); + if (IS_ERR_VALUE(ret)) { + pr_err("fail to parse sm5705 charger device tree (ret=%d)\n", ret); + kfree(pdata); + return NULL; + } +#else + struct sm5705_platform_data *sm5705_pdata = dev_get_platdata(sm5705->dev); + sec_charger_platform_data_t *pdata; + + pdata = sm5705_pdata->charger_data; + if (!pdata) { + pr_err("fail to get sm5705 charger platform data\n"); + return NULL; + } +#endif + pr_info("Get valid platform data done. (pdata=%p)\n", pdata); + + return pdata; +} +static int _init_sm5705_charger_info(struct platform_device *pdev, struct sm5705_dev *sm5705, + struct sm5705_charger_data *charger) +{ + struct sm5705_platform_data *pdata = dev_get_platdata(sm5705->dev); + int ret; + + mutex_init(&charger->charger_mutex); + if (pdata == NULL) { + pr_err("can't get sm5705_platform_data\n"); + return -EINVAL; + } + pr_info("init process start..\n"); + /* setup default charger configuration parameter & flagment */ + charger->input_current = 500; + charger->topoff_pending = false; + charger->is_charging = false; + charger->cable_type = SEC_BATTERY_CABLE_NONE; + charger->is_rev2_wa_done = false; + charger->slow_late_chg_mode = false; + + /* Request GPIO pin - CHG_IN */ + if (charger->pdata->chg_gpio_en) { + ret = gpio_request(charger->pdata->chg_gpio_en, "sm5705_nCHGEN"); + if (ret) { + pr_err("fail to request GPIO %u\n", charger->pdata->chg_gpio_en); + return ret; + } + } + + /* initialize delayed workqueue */ + charger->wqueue = create_singlethread_workqueue(dev_name(charger->dev)); + if (!charger->wqueue) { + pr_err("fail to Create Workqueue\n"); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&charger->slow_chg_work, slow_chg_detect_work); + //INIT_DELAYED_WORK(&charger->wpc_work, wpc_detect_work); +#if defined(CONFIG_USE_POGO) + INIT_DELAYED_WORK(&charger->pogo_work, sm5705_pogo_work); +#endif + INIT_DELAYED_WORK(&charger->aicl_work, sm5705_aicl_work); + INIT_DELAYED_WORK(&charger->topoff_work, sm5705_topoff_work); + INIT_DELAYED_WORK(&charger->op_mode_switch_work, sm5705_op_mode_switch_work); + +#if defined(SM5705_SW_SOFT_START) + wake_lock_init(&charger->softstart_wake_lock, WAKE_LOCK_SUSPEND, "charger-softstart"); +#endif + wake_lock_init(&charger->wpc_wake_lock, WAKE_LOCK_SUSPEND, "charger-wpc"); + wake_lock_init(&charger->afc_wake_lock, WAKE_LOCK_SUSPEND, "charger-afc"); + wake_lock_init(&charger->check_slow_wake_lock, WAKE_LOCK_SUSPEND, "charger-check-slow"); + wake_lock_init(&charger->aicl_wake_lock, WAKE_LOCK_SUSPEND, "charger-aicl"); + + /* Get IRQ service routine number */ + charger->irq_wpcin_pok = pdata->irq_base + SM5705_WPCINPOK_IRQ; + charger->irq_vbus_pok = pdata->irq_base + SM5705_VBUSPOK_IRQ; + charger->irq_aicl = pdata->irq_base + SM5705_AICL_IRQ; + charger->irq_topoff = pdata->irq_base + SM5705_TOPOFF_IRQ; + charger->irq_done = pdata->irq_base + SM5705_DONE_IRQ; + charger->irq_otgfail = pdata->irq_base + SM5705_OTGFAIL_IRQ; +#if defined(CONFIG_USE_POGO) + charger->irq_wpcin_pok_pogo = pdata->irq_base + SM5705_WPCINPOK_IRQ; + charger->irq_wpcin_uvlo_pogo = pdata->irq_base + SM5705_WPCINUVLO_IRQ; +#endif + pr_info("init process done..\n"); + + return 0; +} +static void sm5705_charger_initialize(struct sm5705_charger_data *charger) +{ + pr_info("charger initial hardware condition process start. (float_voltage=%d)\n", + charger->pdata->chg_float_voltage); + + /* Auto-Stop configuration for Emergency status */ + sm5705_CHG_set_TOPOFF(charger, 300); + sm5705_CHG_set_TOPOFF_TMR(charger, SM5705_TOPOFF_TIMER_45m); + sm5705_CHG_enable_AUTOSTOP(charger, 1); + sm5705_CHG_set_BATREG(charger, charger->pdata->chg_float_voltage); + sm5705_CHG_set_AICLTH(charger, 4500); + sm5705_CHG_enable_AICL(charger, 1); + sm5705_CHG_enable_AUTOSET(charger, 0); + sm5705_CHG_set_BST_IQ3LIMIT(charger, SM5705_CHG_BST_IQ3LIMIT_3_5A); + sm5705_CHG_set_OVPSEL(charger, 1); /* fix OVPSEL */ + /* SM5705 Charger Reset contdition initialize */ +#if defined(SM5705_I2C_RESET_ACTIVATE) + sm5705_CHG_set_ENI2CRESET(charger, 1); +#endif + +#if defined(SM5705_MANUAL_RESET_ACTIVATE) + sm5705_CHG_set_ENMRSTB(charger, SM5705_MANUAL_RESET_TIMER); +#endif +#if defined(CONFIG_SEC_FACTORY) + /* W/A for protect to FATORY-JIG download VBUS overshot */ + sm5705_update_reg(charger->i2c, SM5705_REG_FACTORY, (0 << 1), (1 << 1)); +#endif + charger->aicl_on=false; + + pr_info("charger initial hardware condition process done.\n"); +} +static const struct power_supply_desc sm5705_charger_power_supply_desc = { + .name = "sm5705-charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .properties = sm5705_charger_props, + .num_properties = ARRAY_SIZE(sm5705_charger_props), + .get_property = sm5705_chg_get_property, + .set_property = sm5705_chg_set_property, +}; +static const struct power_supply_desc otg_power_supply_desc = { + .name = "otg", + .type = POWER_SUPPLY_TYPE_OTG, + .properties = sm5705_otg_props, + .num_properties = ARRAY_SIZE(sm5705_otg_props), + .get_property = sm5705_otg_get_property, + .set_property = sm5705_otg_set_property, +}; + +static int sm5705_charger_probe(struct platform_device *pdev) +{ + struct sm5705_dev *sm5705 = dev_get_drvdata(pdev->dev.parent); + struct sm5705_charger_data *charger; + struct power_supply_config charger_cfg = {}; + int ret = 0; + + pr_info("Sm5705 Charger Driver Probing start\n"); + + charger = kzalloc(sizeof(struct sm5705_charger_data), GFP_KERNEL); + if (!charger) { + pr_err("fail to memory allocate for sm5705 charger handler\n"); + return -ENOMEM; + } + + charger->dev = &pdev->dev; + charger->i2c = sm5705->i2c; + charger->pdata = _get_sm5705_charger_platform_data(pdev, charger); + if (charger->pdata == NULL) { + pr_err("fail to get charger platform data\n"); + return -ENOENT; + } + + ret = _init_sm5705_charger_info(pdev, sm5705, charger); + if (IS_ERR_VALUE(ret)) { + pr_err("can't initailize sm5705 charger"); + goto err_free; + } + platform_set_drvdata(pdev, charger); + + charger->cable_type = SEC_BATTERY_CABLE_NONE; + sm5705_charger_initialize(charger); + sm5705_chg_test_read(charger); + + charger_cfg.drv_data = charger; + charger->psy_chg = power_supply_register(&pdev->dev, &sm5705_charger_power_supply_desc, &charger_cfg); + if (!charger->psy_chg) { + dev_err(&pdev->dev, "%s: fail to Register psy_chg\n", __func__); + goto err_power_supply_register; + } + + charger->psy_otg = power_supply_register(&pdev->dev, &otg_power_supply_desc, &charger_cfg); + if (!charger->psy_otg) { + dev_err(&pdev->dev, "%s: fail to Register psy_otg\n", __func__); + goto err_power_supply_register_chg; + } + /* Operation Mode Initialize */ + sm5705_charger_oper_table_init(charger->i2c); +#if defined(CONFIG_USE_POGO) + ret = request_threaded_irq(charger->irq_wpcin_pok_pogo, NULL, + sm5705_chg_wpcin_pok_pogo_isr, IRQF_TRIGGER_FALLING, "pogo-pok-int", charger); + if (ret) { + pr_err("fail to request wpcin pogo IRQ: %d: %d\n", charger->irq_wpcin_pok_pogo, ret); + goto err_power_supply_register_otg; + } + ret = request_threaded_irq(charger->irq_wpcin_uvlo_pogo, NULL, + sm5705_chg_wpcin_pok_pogo_isr, IRQF_TRIGGER_FALLING, "pogo-uvlo-int", charger); + if (ret) { + pr_err("fail to request wpcin pogo IRQ: %d: %d\n", charger->irq_wpcin_uvlo_pogo, ret); + goto err_power_supply_register_otg; + } +#endif /* CONFIG_USE_POGO */ + + ret = sm5705_chg_create_attrs(&charger->psy_chg->dev); + if (ret){ + pr_err("Failed to create_attrs\n"); + goto err_power_supply_register_otg; + } + + /* Request IRQ */ + ret = request_threaded_irq(charger->irq_vbus_pok, NULL, sm5705_chg_vbus_in_isr, 0, "chgin-irq", charger); + if (ret < 0) { + pr_err("fail to request chgin IRQ: %d: %d\n", charger->irq_vbus_pok, ret); + goto err_power_supply_register_otg; + } +#if 0 + ret = request_threaded_irq(charger->irq_topoff, NULL, + sm5705_chg_topoff_isr, 0, "topoff-irq", charger); + if (ret < 0) { + pr_err("fail to request topoff IRQ: %d: %d\n", charger->irq_topoff, ret); + goto err_power_supply_register_otg; + } +#endif + ret = request_threaded_irq(charger->irq_done, NULL, sm5705_chg_done_isr, 0, "done-irq", charger); + if (ret < 0) { + pr_err("fail to request chgin IRQ: %d: %d\n", charger->irq_done, ret); + goto err_power_supply_register_otg; + } + + ret = request_threaded_irq(charger->irq_otgfail, NULL, sm5705_chg_otg_fail_isr, 0, "otgfail-irq", charger); + if (ret < 0) { + pr_err("fail to request otgfail IRQ: %d: %d\n", charger->irq_otgfail, ret); + goto err_power_supply_register_otg; + } + + pr_info("SM5705 Charger Driver Loaded Done\n"); + + return 0; + +err_power_supply_register_otg: + power_supply_unregister(charger->psy_otg); +err_power_supply_register_chg: + power_supply_unregister(charger->psy_chg); +err_power_supply_register: + destroy_workqueue(charger->wqueue); + mutex_destroy(&charger->charger_mutex); +err_free: + kfree(charger); + + return ret; +} +static int sm5705_charger_remove(struct platform_device *pdev) +{ + struct sm5705_charger_data *charger = platform_get_drvdata(pdev); + + cancel_delayed_work(&charger->slow_chg_work); + //cancel_delayed_work(&charger->wpc_work); +#if defined(CONFIG_USE_POGO) + cancel_delayed_work(&charger->pogo_work); +#endif + cancel_delayed_work(&charger->aicl_work); + cancel_delayed_work(&charger->topoff_work); + cancel_delayed_work(&charger->op_mode_switch_work); + destroy_workqueue(charger->wqueue); +#if defined(CONFIG_USE_POGO) + free_irq(charger->irq_wpcin_pok_pogo, NULL); + free_irq(charger->irq_wpcin_uvlo_pogo, NULL); +#endif +// free_irq(charger->irq_wpcin_pok, NULL); + free_irq(charger->irq_vbus_pok, NULL); +// free_irq(charger->irq_topoff, NULL); + free_irq(charger->irq_done, NULL); + free_irq(charger->irq_otgfail, NULL); + power_supply_unregister(charger->psy_chg); + mutex_destroy(&charger->charger_mutex); + kfree(charger); + + return 0; +} +static void sm5705_charger_shutdown(struct device *dev) +{ + pr_info("call shutdown\n"); +} +#if defined CONFIG_PM +static int sm5705_charger_suspend(struct device *dev) +{ + pr_info("call suspend\n"); + + return 0; +} +static int sm5705_charger_resume(struct device *dev) +{ + pr_info("call resume\n"); + + return 0; +} +#else +#define sm5705_charger_suspend NULL +#define sm5705_charger_resume NULL +#endif + +static SIMPLE_DEV_PM_OPS(sm5705_charger_pm_ops, sm5705_charger_suspend, sm5705_charger_resume); +static struct platform_driver sm5705_charger_driver = { + .driver = { + .name = "sm5705-charger", + .owner = THIS_MODULE, + .pm = &sm5705_charger_pm_ops, + .shutdown = sm5705_charger_shutdown, + }, + .probe = sm5705_charger_probe, + .remove = sm5705_charger_remove, +}; +static int __init sm5705_charger_init(void) +{ + return platform_driver_register(&sm5705_charger_driver); +} +static void __exit sm5705_charger_exit(void) +{ + platform_driver_unregister(&sm5705_charger_driver); +} + +module_init(sm5705_charger_init); +module_exit(sm5705_charger_exit); + +MODULE_DESCRIPTION("SM5705 Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/battery_v2/sm5705_charger_oper.c b/drivers/battery_v2/sm5705_charger_oper.c new file mode 100644 index 000000000000..b6bc5ec05a77 --- /dev/null +++ b/drivers/battery_v2/sm5705_charger_oper.c @@ -0,0 +1,225 @@ +/* + * drivers/battery/sm5705_charger_oper.c + * + * SM5705 Charger Operation Mode controller + * + * Copyright (C) 2015 Siliconmitus Technology Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#include +#include "include/charger/sm5705_charger.h" +#include "include/charger/sm5705_charger_oper.h" + +enum { + BST_OUT_4000mV = 0x0, + BST_OUT_4100mV = 0x1, + BST_OUT_4200mV = 0x2, + BST_OUT_4300mV = 0x3, + BST_OUT_4400mV = 0x4, + BST_OUT_4500mV = 0x5, + BST_OUT_4600mV = 0x6, + BST_OUT_4700mV = 0x7, + BST_OUT_4800mV = 0x8, + BST_OUT_4900mV = 0x9, + BST_OUT_5000mV = 0xA, + BST_OUT_5100mV = 0xB, +}; +enum { + OTG_CURRENT_500mA = 0x0, + OTG_CURRENT_700mA = 0x1, + OTG_CURRENT_900mA = 0x2, + OTG_CURRENT_1500mA = 0x3, +}; +#define SM5705_OPERATION_MODE_MASK 0x07 +#define SM5705_BSTOUT_MASK 0x0F +#define SM5705_OTGCURRENT_MASK 0xC +struct sm5705_charger_oper_table_info { + unsigned char status; + unsigned char oper_mode; + unsigned char BST_OUT; + unsigned char OTG_CURRENT; +}; +struct sm5705_charger_oper_info { + struct i2c_client *i2c; + + bool suspend_mode; + int max_table_num; + struct sm5705_charger_oper_table_info current_table; +}; +static struct sm5705_charger_oper_info oper_info; +/** + * (VBUS in/out) (WPC in/out) (FLASH on/off) (TORCH on/off) (OTG cable in/out) (Power Sharing cable in/out) + **/ +static struct sm5705_charger_oper_table_info sm5705_charger_operation_mode_table[] = { + /* Charger mode : Charging ON */ + { make_OP_STATUS(0,0,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(1,0,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(1,1,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(1,0,0,1,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(1,1,0,1,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(0,1,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(1,0,0,0,1,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_1500mA}, + /* Charger mode : Flash Boost */ + { make_OP_STATUS(0,0,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(0,0,1,0,1,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_1500mA}, + { make_OP_STATUS(0,0,1,0,0,1), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA}, + { make_OP_STATUS(0,1,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(0,1,1,0,1,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_1500mA}, + { make_OP_STATUS(0,1,1,0,0,1), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA}, + { make_OP_STATUS(1,0,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(1,1,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(0,0,0,1,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA}, + /* Charger mode : Wireless OTG & Charger ON */ + { make_OP_STATUS(0,1,0,1,1,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_1500mA}, + { make_OP_STATUS(0,1,0,1,0,1), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA}, + { make_OP_STATUS(0,1,0,1,0,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA}, + { make_OP_STATUS(0,1,0,0,1,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_1500mA}, + { make_OP_STATUS(0,1,0,0,0,1), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA}, + /* Charger mode : USB OTG */ + { make_OP_STATUS(0,0,0,1,1,0), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_1500mA}, + { make_OP_STATUS(0,0,0,1,0,1), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA}, + { make_OP_STATUS(0,0,0,0,1,0), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_1500mA}, + { make_OP_STATUS(0,0,0,0,0,1), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA}, +}; + +/** + * SM5705 Charger operation mode controller relative I2C setup + */ + +static int sm5705_charger_oper_set_mode(struct i2c_client *i2c, unsigned char mode) +{ + return sm5705_update_reg(i2c, SM5705_REG_CNTL, mode, SM5705_OPERATION_MODE_MASK); +} +static int sm5705_charger_oper_set_BSTOUT(struct i2c_client *i2c, unsigned char BSTOUT) +{ + return sm5705_update_reg(i2c, SM5705_REG_FLEDCNTL6, BSTOUT, SM5705_BSTOUT_MASK); +} +static int sm5705_charger_oper_set_OTG_CURRENT(struct i2c_client *i2c, unsigned char OTG_CURRENT) +{ + return sm5705_update_reg(i2c, SM5705_REG_CHGCNTL6, OTG_CURRENT << 2, SM5705_OTGCURRENT_MASK); +} + +/** + * SM5705 Charger operation mode controller API functions. + */ + +static inline unsigned char _update_status(int event_type, bool enable) +{ + if (event_type > SM5705_CHARGER_OP_EVENT_VBUS) + return oper_info.current_table.status; + + if (enable) + return (oper_info.current_table.status | (1 << event_type)); + else + return (oper_info.current_table.status & ~(1 << event_type)); +} +static inline void sm5705_charger_oper_change_state(unsigned char new_status) +{ + int i; + + for (i=0; i < oper_info.max_table_num; ++i) { + if (new_status == sm5705_charger_operation_mode_table[i].status) + break; + } + if (i == oper_info.max_table_num) { + pr_err("sm5705-charger: %s: can't find matched Charger Operation Mode Table (status = 0x%x)\n", __func__, new_status); + return; + } + if (oper_info.suspend_mode) { + pr_info("sm5705-charger: %s: skip setting by suspend mode(MODE: %d)\n", + __func__, oper_info.current_table.oper_mode); + goto skip_oper_change_state; + } + if (sm5705_charger_operation_mode_table[i].BST_OUT != oper_info.current_table.BST_OUT) { + sm5705_charger_oper_set_BSTOUT(oper_info.i2c, sm5705_charger_operation_mode_table[i].BST_OUT); + oper_info.current_table.BST_OUT = sm5705_charger_operation_mode_table[i].BST_OUT; + } + if (sm5705_charger_operation_mode_table[i].OTG_CURRENT != oper_info.current_table.OTG_CURRENT) { + sm5705_charger_oper_set_OTG_CURRENT(oper_info.i2c, sm5705_charger_operation_mode_table[i].OTG_CURRENT); + oper_info.current_table.OTG_CURRENT = sm5705_charger_operation_mode_table[i].OTG_CURRENT; + } + if (sm5705_call_fg_device_id() < 5) { + /* USB_OTG to CHG_ON work-around for BAT_REG stabilize */ + if (oper_info.current_table.oper_mode == SM5705_CHARGER_OP_MODE_USB_OTG && \ + sm5705_charger_operation_mode_table[i].oper_mode == SM5705_CHARGER_OP_MODE_CHG_ON) { + pr_info("sm5705-charger: %s: trans op_mode:suspend for BAT_REG stabilize (time=100ms)\n", __func__); + sm5705_charger_oper_set_mode(oper_info.i2c, SM5705_CHARGER_OP_MODE_SUSPEND); + msleep(100); + } + } + if (sm5705_charger_operation_mode_table[i].oper_mode != oper_info.current_table.oper_mode) { + sm5705_charger_oper_set_mode(oper_info.i2c, sm5705_charger_operation_mode_table[i].oper_mode); + oper_info.current_table.oper_mode = sm5705_charger_operation_mode_table[i].oper_mode; + } +skip_oper_change_state: + oper_info.current_table.status = new_status; + pr_info("sm5705-charger: %s: New table[%d] info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x\n", \ + __func__, i, oper_info.current_table.status, oper_info.current_table.oper_mode, oper_info.current_table.BST_OUT, oper_info.current_table.OTG_CURRENT); +} + +int sm5705_charger_oper_push_event(int event_type, bool enable) +{ + unsigned char new_status; + + if (oper_info.i2c == NULL) { + pr_err("sm5705-charger: %s: required sm5705 charger operation table initialize\n", __func__); + return -ENOENT; + } + pr_info("sm5705-charger: %s: event_type=%d, enable=%d\n", __func__, event_type, enable); + if (event_type == SM5705_CHARGER_OP_EVENT_SUSPEND_MODE) { + oper_info.suspend_mode = enable; + if (oper_info.suspend_mode) { + oper_info.current_table.oper_mode = SM5705_CHARGER_OP_MODE_SUSPEND; + sm5705_charger_oper_set_mode(oper_info.i2c, SM5705_CHARGER_OP_MODE_SUSPEND); + } else if (oper_info.current_table.oper_mode == SM5705_CHARGER_OP_MODE_SUSPEND) + sm5705_charger_oper_change_state(oper_info.current_table.status); + goto out; + } + new_status = _update_status(event_type, enable); + if (new_status == oper_info.current_table.status) + goto out; + + sm5705_charger_oper_change_state(new_status); +out: + return 0; +} +EXPORT_SYMBOL(sm5705_charger_oper_push_event); +int sm5705_charger_oper_table_init(struct i2c_client *i2c) +{ + if (i2c == NULL) { + pr_err("sm5705-charger: %s: invalid i2c client handler=n", __func__); + return -EINVAL; + } + oper_info.i2c = i2c; + /* set default operation mode condition */ + oper_info.suspend_mode = false; + oper_info.max_table_num = ARRAY_SIZE(sm5705_charger_operation_mode_table); + oper_info.current_table.status = make_OP_STATUS(0, 0, 0, 0, 0, 0); + oper_info.current_table.oper_mode = SM5705_CHARGER_OP_MODE_CHG_ON; + oper_info.current_table.BST_OUT = BST_OUT_4500mV; + oper_info.current_table.OTG_CURRENT = OTG_CURRENT_500mA; + sm5705_charger_oper_set_mode(oper_info.i2c, oper_info.current_table.oper_mode); + sm5705_charger_oper_set_BSTOUT(oper_info.i2c, oper_info.current_table.BST_OUT); + sm5705_charger_oper_set_OTG_CURRENT(oper_info.i2c, oper_info.current_table.OTG_CURRENT); + pr_info("sm5705-charger: %s: current table info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x\n", \ + __func__, oper_info.current_table.status, oper_info.current_table.oper_mode, oper_info.current_table.BST_OUT, oper_info.current_table.OTG_CURRENT); + + return 0; +} +EXPORT_SYMBOL(sm5705_charger_oper_table_init); +int sm5705_charger_oper_get_current_status(void) +{ + return oper_info.current_table.status; +} +EXPORT_SYMBOL(sm5705_charger_oper_get_current_status); +int sm5705_charger_oper_get_current_op_mode(void) +{ + return oper_info.current_table.oper_mode; +} +EXPORT_SYMBOL(sm5705_charger_oper_get_current_op_mode); diff --git a/drivers/battery_v2/sm5705_fuelgauge.c b/drivers/battery_v2/sm5705_fuelgauge.c new file mode 100644 index 000000000000..db409b563eb3 --- /dev/null +++ b/drivers/battery_v2/sm5705_fuelgauge.c @@ -0,0 +1,2582 @@ +/* drivers/battery/sm5705_fuelgauge.c + * SM5705 Voltage Tracking Fuelgauge Driver + * + * Copyright (C) 2013 + * Author: Dongik Sin + * Modified by SW Jung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include "include/fuelgauge/sm5705_fuelgauge.h" +#include "include/fuelgauge/sm5705_fuelgauge_impl.h" +#include +#include +#include +#include +#include +#include +#define SM5705_FG_DEVICE_NAME "sm5705-fg" +#define ALIAS_NAME "sm5705-fuelgauge" +#define FG_DET_BAT_PRESENT 1 +#define MINVAL(a, b) ((a <= b) ? a : b) +#define MAXVAL(a, b) ((a > b) ? a : b) +#define LIMIT_N_CURR_MIXFACTOR -2000 +#define FG_ABNORMAL_RESET -1 +#define IGNORE_N_I_OFFSET 1 +#define ABSOLUTE_ERROR_OCV_MATCH 1 +//#define SM5705_FG_FULL_DEBUG 1 +enum battery_table_type { + DISCHARGE_TABLE = 0, + Q_TABLE, + TABLE_MAX, +}; +static int sm5705_device_id = -1; +static struct device_attribute sm5705_fg_attrs[] = { + SM5705_FG_ATTR(reg), + SM5705_FG_ATTR(data), + SM5705_FG_ATTR(regs), +}; +static enum power_supply_property sm5705_fuelgauge_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_AMBIENT, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, +}; +bool sm5705_fg_fuelalert_init(struct i2c_client *client, int soc); +static int sm5705_abnormal_reset_check(struct i2c_client *client); +static int sm5705_get_all_value(struct i2c_client *client); +static unsigned int sm5705_get_vbat(struct i2c_client *client); +static unsigned int sm5705_get_ocv(struct i2c_client *client); +static int sm5705_get_curr(struct i2c_client *client); +static int sm5705_get_temperature(struct i2c_client *client); +static unsigned int sm5705_get_soc(struct i2c_client *client); + +static inline int sm5705_fg_read_device(struct i2c_client *client, + int reg, int bytes, void *dest) +{ + int ret; + + if (bytes > 1) + ret = i2c_smbus_read_i2c_block_data(client, reg, bytes, dest); + else { + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + *(unsigned char *)dest = (unsigned char)ret; + } + + return ret; +} +static int32_t sm5705_fg_i2c_read_word(struct i2c_client *client, uint8_t reg_addr) +{ + uint16_t data = 0; + int ret; + + ret = sm5705_fg_read_device(client, reg_addr, 2, &data); + //pr_info("%s: ret = %d, addr = 0x%x, data = 0x%x\n", __func__, ret, reg_addr, data); + + if (ret < 0) + return ret; + else + return data; + // not use big endian + //return (int32_t)be16_to_cpu(data); +} +static int32_t sm5705_fg_i2c_write_word(struct i2c_client *client, + uint8_t reg_addr,uint16_t data) +{ + int ret; + + // not use big endian + //data = cpu_to_be16(data); + ret = i2c_smbus_write_i2c_block_data(client, reg_addr, 2, (uint8_t *)&data); + //pr_info("%s: ret = %d, addr = 0x%x, data = 0x%x\n", __func__, ret, reg_addr, data); + + return ret; +} +static int32_t sm5705_fg_i2c_verified_write_word(struct i2c_client *client, + uint8_t reg_addr,uint16_t data) +{ + int ret; + + // not use big endian + //data = cpu_to_be16(data); + ret = i2c_smbus_write_i2c_block_data(client, reg_addr, 2, (uint8_t *)&data); + if(ret<0) { + msleep(50); + pr_info("1st fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n", + __func__, ret, reg_addr, data); + ret = i2c_smbus_write_i2c_block_data(client, reg_addr, 2, (uint8_t *)&data); + if(ret<0) { + msleep(50); + pr_info("2nd fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n", + __func__, ret, reg_addr, data); + ret = i2c_smbus_write_i2c_block_data(client, reg_addr, 2, (uint8_t *)&data); + if(ret<0) + pr_info("3rd fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n", + __func__, ret, reg_addr, data); + } + } + //pr_info("%s: ret = %d, addr = 0x%x, data = 0x%x\n", __func__, ret, reg_addr, data); + + return ret; +} +#if 0 +static void sm5705_pr_ver_info(struct i2c_client *client) +{ + pr_info("SM5705 Fuel-Gauge Ver %s\n", FG_DRIVER_VER); +} +#endif +static unsigned int sm5705_get_ocv(struct i2c_client *client) +{ + int ret; + unsigned int ocv;// = 3500; /*3500 means 3500mV*/ + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_OCV); + if (ret<0) { + pr_err("%s: read ocv reg fail\n", __func__); + ocv = 4000; + } else { + ocv = ((ret&0x7800)>>11) * 1000; //integer; + ocv = ocv + (((ret&0x07ff)*1000)/2048); // integer + fractional + } + fuelgauge->info.batt_ocv = ocv; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: read = 0x%x, ocv = %d\n", __func__, ret, ocv); +#endif + + return ocv; +} +void sm5705_cal_avg_vbat(struct sec_fuelgauge_info *fuelgauge) +{ + if (fuelgauge->info.batt_avgvoltage == 0) + fuelgauge->info.batt_avgvoltage = fuelgauge->info.batt_voltage; + else if (fuelgauge->info.batt_voltage == 0 && fuelgauge->info.p_batt_voltage == 0) + fuelgauge->info.batt_avgvoltage = 3400; + else if(fuelgauge->info.batt_voltage == 0) + fuelgauge->info.batt_avgvoltage = + ((fuelgauge->info.batt_avgvoltage) + (fuelgauge->info.p_batt_voltage))/2; + else if(fuelgauge->info.p_batt_voltage == 0) + fuelgauge->info.batt_avgvoltage = + ((fuelgauge->info.batt_avgvoltage) + (fuelgauge->info.batt_voltage))/2; + else + fuelgauge->info.batt_avgvoltage = + ((fuelgauge->info.batt_avgvoltage*2) + + (fuelgauge->info.p_batt_voltage+fuelgauge->info.batt_voltage))/4; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: batt_avgvoltage = %d\n", __func__, fuelgauge->info.batt_avgvoltage); +#endif + + return; +} +static unsigned int sm5705_get_vbat(struct i2c_client *client) +{ + int ret; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + unsigned int vbat;/* = 3500; 3500 means 3500mV*/ + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_VOLTAGE); + if (ret<0) { + pr_err("%s: read vbat reg fail", __func__); + vbat = 4000; + } else { + vbat = ((ret&0x3800)>>11) * 1000; //integer; + vbat = vbat + (((ret&0x07ff)*1000)/2048); // integer + fractional + } + fuelgauge->info.batt_voltage = vbat; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: read = 0x%x, vbat = %d\n", __func__, ret, vbat); +#endif + sm5705_cal_avg_vbat(fuelgauge); + if ((fuelgauge->info.volt_alert_flag == true) && vbat > 3400) { + fuelgauge->info.volt_alert_flag = false; + if (fuelgauge->is_fuel_alerted) + wake_unlock(&fuelgauge->fuel_alert_wake_lock); + sm5705_fg_fuelalert_init(client, fuelgauge->pdata->fuel_alert_soc); + pr_info("%s : volt_alert_flag = %d \n", __func__, fuelgauge->info.volt_alert_flag); + } + + return vbat; +} +void sm5705_cal_avg_current(struct sec_fuelgauge_info *fuelgauge) +{ + if (fuelgauge->info.batt_avgcurrent == 0) + fuelgauge->info.batt_avgcurrent = fuelgauge->info.batt_current; + else if (fuelgauge->info.batt_avgcurrent == 0 && fuelgauge->info.p_batt_current == 0) + fuelgauge->info.batt_avgcurrent = fuelgauge->info.batt_current; + else if(fuelgauge->info.batt_current == 0) + fuelgauge->info.batt_avgcurrent = + ((fuelgauge->info.batt_avgcurrent) + (fuelgauge->info.p_batt_current))/2; + else if(fuelgauge->info.p_batt_current == 0) + fuelgauge->info.batt_avgcurrent = + ((fuelgauge->info.batt_avgcurrent) + (fuelgauge->info.batt_current))/2; + else + fuelgauge->info.batt_avgcurrent = + ((fuelgauge->info.batt_avgcurrent*2) + + (fuelgauge->info.p_batt_current+fuelgauge->info.batt_current))/4; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: batt_avgcurrent = %d\n", __func__, fuelgauge->info.batt_avgcurrent); +#endif + + return; +} +static int sm5705_get_curr(struct i2c_client *client) +{ + int ret, volt_slope, mohm_volt_cal; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + int curr;/* = 1000; 1000 means 1000mA*/ + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CURRENT); + if (ret<0) { + pr_err("%s: read curr reg fail", __func__); + curr = 0; + } else { + curr = ((ret&0x1800)>>11) * 1000; //integer; + curr = curr + (((ret&0x07ff)*1000)/2048); // integer + fractional + if(ret&0x8000) + curr *= -1; + } + fuelgauge->info.batt_current = curr; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: read = 0x%x, curr = %d\n", __func__, ret, curr); +#endif + //set vbat offset cancel start + volt_slope = sm5705_fg_i2c_read_word(client, SM5705_REG_VOLT_CAL); + volt_slope = volt_slope & 0xFF00; + mohm_volt_cal = fuelgauge->info.volt_cal & 0x00FF; + if(fuelgauge->info.enable_v_offset_cancel_p) { + if(fuelgauge->is_charging && (curr > fuelgauge->info.v_offset_cancel_level)) { + if(mohm_volt_cal & 0x0080) + mohm_volt_cal = -(mohm_volt_cal & 0x007F); + mohm_volt_cal = mohm_volt_cal - (curr/(fuelgauge->info.v_offset_cancel_mohm * 13)); // ((curr*0.001)*0.006)*2048 -> 6mohm + if(mohm_volt_cal < 0) { + mohm_volt_cal = -mohm_volt_cal; + mohm_volt_cal = mohm_volt_cal|0x0080; + } + } + } + if(fuelgauge->info.enable_v_offset_cancel_n) { + if(!(fuelgauge->is_charging) && (curr < -(fuelgauge->info.v_offset_cancel_level))) { + if(fuelgauge->info.volt_cal & 0x0080) + mohm_volt_cal = -(mohm_volt_cal & 0x007F); + mohm_volt_cal = mohm_volt_cal - (curr/(fuelgauge->info.v_offset_cancel_mohm * 13)); // ((curr*0.001)*0.006)*2048 -> 6mohm + if(mohm_volt_cal < 0) { + mohm_volt_cal = -mohm_volt_cal; + mohm_volt_cal = mohm_volt_cal|0x0080; + } + } + } + sm5705_fg_i2c_write_word(client, SM5705_REG_VOLT_CAL, (mohm_volt_cal | volt_slope)); + pr_info("%s: <%d %d %d %d> info.volt_cal = 0x%x, volt_slope = 0x%x, mohm_volt_cal = 0x%x\n", + __func__, fuelgauge->info.enable_v_offset_cancel_p, fuelgauge->info.enable_v_offset_cancel_n, + fuelgauge->info.v_offset_cancel_level, fuelgauge->info.v_offset_cancel_mohm, + fuelgauge->info.volt_cal, volt_slope, mohm_volt_cal); + //set vbat offset cancel end + sm5705_cal_avg_current(fuelgauge); + + return curr; +} +static int sm5705_get_temperature(struct i2c_client *client) +{ + int ret; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + int temp;/* = 250; 250 means 25.0oC*/ + //double temp_data; + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_TEMPERATURE); + if (ret<0) { + pr_err("%s: read temp reg fail", __func__); + temp = 0; + } else { + temp = ((ret&0x7F00)>>8) * 10; //integer bit + temp = temp + (((ret&0x00f0)*10)/256); // integer + fractional bit + if(ret&0x8000) + temp *= -1; + } + fuelgauge->info.temp_fg = temp; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: read = 0x%x, temp_fg = %d\n", __func__, ret, temp); +#endif + + return temp; +} +static int sm5705_get_soc_cycle(struct i2c_client *client) +{ + int ret; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + int cycle; + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_SOC_CYCLE); + if (ret<0) { + pr_err("%s: read cycle reg fail", __func__); + cycle = 0; + } else + cycle = ret&0x03FF; + fuelgauge->info.batt_soc_cycle = cycle; +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: read = 0x%x, soc_cycle = %d\n", __func__, ret, cycle); +#endif + + return cycle; +} +static void sm5705_fg_test_read(struct i2c_client *client) +{ + int ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9, ret10, ret11; + + ret0 = sm5705_fg_i2c_read_word(client, 0xA0); + ret1 = sm5705_fg_i2c_read_word(client, 0xAC); + ret2 = sm5705_fg_i2c_read_word(client, 0xAD); + ret3 = sm5705_fg_i2c_read_word(client, 0xAE); + ret4 = sm5705_fg_i2c_read_word(client, 0xAF); + ret5 = sm5705_fg_i2c_read_word(client, 0x28); + ret6 = sm5705_fg_i2c_read_word(client, 0x2F); + ret7 = sm5705_fg_i2c_read_word(client, 0x01); + pr_info("%s: 0xA0=0x%04x, 0xAC=0x%04x, 0xAD=0x%04x, 0xAE=0x%04x, 0xAF=0x%04x, 0x28=0x%04x, 0x2F=0x%04x, 0x01=0x%04x, SM5705_ID=0x%04x\n", + __func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7, sm5705_device_id); + ret0 = sm5705_fg_i2c_read_word(client, 0xB0); + ret1 = sm5705_fg_i2c_read_word(client, 0xBC); + ret2 = sm5705_fg_i2c_read_word(client, 0xBD); + ret3 = sm5705_fg_i2c_read_word(client, 0xBE); + ret4 = sm5705_fg_i2c_read_word(client, 0xBF); + ret5 = sm5705_fg_i2c_read_word(client, 0x85); + ret6 = sm5705_fg_i2c_read_word(client, 0x86); + ret7 = sm5705_fg_i2c_read_word(client, 0x87); + ret8 = sm5705_fg_i2c_read_word(client, 0x1F); + ret9 = sm5705_fg_i2c_read_word(client, 0x94); + ret10 = sm5705_fg_i2c_read_word(client, 0x13); + ret11 = sm5705_fg_i2c_read_word(client, 0x14); + pr_info("%s: 0xB0=0x%04x, 0xBC=0x%04x, 0xBD=0x%04x, 0xBE=0x%04x, 0xBF=0x%04x, 0x85=0x%04x, 0x86=0x%04x, 0x87=0x%04x, 0x1F=0x%04x, 0x94=0x%04x , 0x13=0x%04x, 0x14=0x%04x\n", + __func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9, ret10, ret11); + + return; +} +static unsigned int sm5705_get_device_id(struct i2c_client *client) +{ + int ret; + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_DEVICE_ID); + sm5705_device_id = ret; + pr_info("%s: SM5705 device_id = 0x%x\n", __func__, ret); + + return ret; +} +int sm5705_call_fg_device_id(void) +{ + pr_info("%s: extern call SM5705 fg_device_id = 0x%x\n", __func__, sm5705_device_id); + + return sm5705_device_id; +} +static bool sm5705_fg_check_reg_init_need(struct i2c_client *client) +{ + int ret; + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_FG_OP_STATUS); + if((ret & INIT_CHECK_MASK) == DISABLE_RE_INIT) { + pr_info("%s: SM5705_REG_FG_OP_STATUS : 0x%x , return 0\n", __func__, ret); + return 0; + } else { + pr_info("%s: SM5705_REG_FG_OP_STATUS : 0x%x , return 1\n", __func__, ret); + return 1; + } +} +unsigned int sm5705_get_soc(struct i2c_client *client) +{ + int ret; + unsigned int soc; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_SOC); + if (ret<0) { + pr_err("%s: Warning!!!! read soc reg fail\n", __func__); + if (fuelgauge->info.batt_soc == 0) + soc = 500; + else + soc = fuelgauge->info.batt_soc; + pr_info("%s : soc=%d\n", __func__, soc); + } else { + soc = ((ret&0xff00)>>8) * 10; //integer bit; + soc = soc + (((ret&0x00ff)*10)/256); // integer + fractional bit + } + if (sm5705_abnormal_reset_check(client) < 0) { + pr_info("%s: FG init ERROR!! pre_SOC returned!!, read_SOC = %d, pre_SOC = %d\n", + __func__, soc, fuelgauge->info.batt_soc); + return fuelgauge->info.batt_soc; + } +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: read = 0x%x, soc = %d\n", __func__, ret, soc); +#endif + // for low temp power off test + if(fuelgauge->info.volt_alert_flag && (fuelgauge->info.temperature < -100)) { + pr_info("%s: volt_alert_flag is TRUE!!!! SOC make force ZERO!!!!\n", __func__); + fuelgauge->info.batt_soc = 0; + return 0; + } + fuelgauge->info.batt_soc = soc; + + return soc; +} +static void sm5705_fg_buffer_read(struct i2c_client *client) +{ + int ret0, ret1, ret2, ret3, ret4, ret5; + + ret0 = sm5705_fg_i2c_read_word(client, 0x30); + ret1 = sm5705_fg_i2c_read_word(client, 0x31); + ret2 = sm5705_fg_i2c_read_word(client, 0x32); + ret3 = sm5705_fg_i2c_read_word(client, 0x33); + ret4 = sm5705_fg_i2c_read_word(client, 0x34); + ret5 = sm5705_fg_i2c_read_word(client, 0x35); + pr_info("%s: sm5705 FG buffer 0x30_0x35 lb_V = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", + __func__, ret0, ret1, ret2, ret3, ret4, ret5); + ret0 = sm5705_fg_i2c_read_word(client, 0x36); + ret1 = sm5705_fg_i2c_read_word(client, 0x37); + ret2 = sm5705_fg_i2c_read_word(client, 0x38); + ret3 = sm5705_fg_i2c_read_word(client, 0x39); + ret4 = sm5705_fg_i2c_read_word(client, 0x3A); + ret5 = sm5705_fg_i2c_read_word(client, 0x3B); + pr_info("%s: sm5705 FG buffer 0x36_0x3B cb_V = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", + __func__, ret0, ret1, ret2, ret3, ret4, ret5); + ret0 = sm5705_fg_i2c_read_word(client, 0x40); + ret1 = sm5705_fg_i2c_read_word(client, 0x41); + ret2 = sm5705_fg_i2c_read_word(client, 0x42); + ret3 = sm5705_fg_i2c_read_word(client, 0x43); + ret4 = sm5705_fg_i2c_read_word(client, 0x44); + ret5 = sm5705_fg_i2c_read_word(client, 0x45); + pr_info("%s: sm5705 FG buffer 0x40_0x45 lb_I = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", + __func__, ret0, ret1, ret2, ret3, ret4, ret5); + ret0 = sm5705_fg_i2c_read_word(client, 0x46); + ret1 = sm5705_fg_i2c_read_word(client, 0x47); + ret2 = sm5705_fg_i2c_read_word(client, 0x48); + ret3 = sm5705_fg_i2c_read_word(client, 0x49); + ret4 = sm5705_fg_i2c_read_word(client, 0x4A); + ret5 = sm5705_fg_i2c_read_word(client, 0x4B); + pr_info("%s: sm5705 FG buffer 0x46_0x4B cb_I = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", + __func__, ret0, ret1, ret2, ret3, ret4, ret5); + + return; +} +static bool sm5705_fg_get_batt_present(struct i2c_client *client) +{ + // SM5705 is not suport batt present + dev_dbg(&client->dev, "%s: sm5705_fg_get_batt_present\n", __func__); + + return true; +} +int sm5705_calculate_iocv(struct i2c_client *client) +{ + bool only_lb=false, sign_i_offset=0; + int roop_start=0, roop_max=0, i=0, cb_last_index = 0, cb_pre_last_index =0; + int lb_v_buffer[6] = {0, 0, 0, 0, 0, 0}; + int lb_i_buffer[6] = {0, 0, 0, 0, 0, 0}; + int cb_v_buffer[6] = {0, 0, 0, 0, 0, 0}; + int cb_i_buffer[6] = {0, 0, 0, 0, 0, 0}; + int i_offset_margin = 0x14, i_vset_margin = 0x67; + int v_max=0, v_min=0, v_sum=0, lb_v_avg=0, cb_v_avg=0, lb_v_set=0, lb_i_set=0, i_offset=0; + int i_max=0, i_min=0, i_sum=0, lb_i_avg=0, cb_i_avg=0, cb_v_set=0, cb_i_set=0; + int lb_i_p_v_min=0, lb_i_n_v_max=0, cb_i_p_v_min=0, cb_i_n_v_max=0; + int v_ret=0, i_ret=0, ret=0; + + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_END_V_IDX); + pr_info("%s: iocv_status_read = addr : 0x%x , data : 0x%x\n", __func__, SM5705_REG_END_V_IDX, ret); + // init start + if((ret & 0x0010) == 0x0000) + only_lb = true; + // init end + // lb get start + roop_max = (ret & 0x000F); + if(roop_max > 6) + roop_max = 6; + roop_start = SM5705_REG_IOCV_B_L_MIN; + for (i = roop_start; i < roop_start + roop_max; i++) { + v_ret = sm5705_fg_i2c_read_word(client, i); + i_ret = sm5705_fg_i2c_read_word(client, i+0x10); + if((i_ret&0x4000) == 0x4000) + i_ret = -(i_ret&0x3FFF); + lb_v_buffer[i-roop_start] = v_ret; + lb_i_buffer[i-roop_start] = i_ret; + if (i == roop_start) { + v_max = v_ret; + v_min = v_ret; + v_sum = v_ret; + i_max = i_ret; + i_min = i_ret; + i_sum = i_ret; + } else { + if(v_ret > v_max) + v_max = v_ret; + else if(v_ret < v_min) + v_min = v_ret; + v_sum = v_sum + v_ret; + if(i_ret > i_max) + i_max = i_ret; + else if(i_ret < i_min) + i_min = i_ret; + i_sum = i_sum + i_ret; + } + if(abs(i_ret) > i_vset_margin) { + if(i_ret > 0) { + if(lb_i_p_v_min == 0) + lb_i_p_v_min = v_ret; + else + if(v_ret < lb_i_p_v_min) + lb_i_p_v_min = v_ret; + } else { + if(lb_i_n_v_max == 0) + lb_i_n_v_max = v_ret; + else + if(v_ret > lb_i_n_v_max) + lb_i_n_v_max = v_ret; + } + } + } + v_sum = v_sum - v_max - v_min; + i_sum = i_sum - i_max - i_min; + lb_v_avg = v_sum / (roop_max-2); + lb_i_avg = i_sum / (roop_max-2); + // lb get end + // lb_vset start + if(abs(lb_i_buffer[roop_max-1]) < i_vset_margin) { + if(abs(lb_i_buffer[roop_max-2]) < i_vset_margin) { + lb_v_set = MAXVAL(lb_v_buffer[roop_max-2], lb_v_buffer[roop_max-1]); + if(abs(lb_i_buffer[roop_max-3]) < i_vset_margin) + lb_v_set = MAXVAL(lb_v_buffer[roop_max-3], lb_v_set); + } + else + lb_v_set = lb_v_buffer[roop_max-1]; + } + else + lb_v_set = lb_v_avg; + if(lb_i_n_v_max > 0) + lb_v_set = MAXVAL(lb_i_n_v_max, lb_v_set); + //else if(lb_i_p_v_min > 0) + //{ + // lb_v_set = MINVAL(lb_i_p_v_min, lb_v_set); + //} + // lb_vset end + // lb offset make start + if(roop_max > 3) + lb_i_set = (lb_i_buffer[2] + lb_i_buffer[3]) / 2; + if((abs(lb_i_buffer[roop_max-1]) < i_offset_margin) && (abs(lb_i_set) < i_offset_margin)) + lb_i_set = MAXVAL(lb_i_buffer[roop_max-1], lb_i_set); + else if(abs(lb_i_buffer[roop_max-1]) < i_offset_margin) + lb_i_set = lb_i_buffer[roop_max-1]; + else if(abs(lb_i_set) < i_offset_margin) + ; + else + lb_i_set = 0; + i_offset = lb_i_set; + i_offset = i_offset + 4; // add extra offset + if(i_offset <= 0) { + sign_i_offset = 1; +#ifdef IGNORE_N_I_OFFSET + i_offset = 0; +#else + i_offset = -i_offset; +#endif + } + i_offset = i_offset>>1; + if(sign_i_offset == 0) + i_offset = i_offset|0x0080; + //do not write in kernel point. + //sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_OFF, i_offset); + // lb offset make end + pr_info("%s: iocv_l_max=0x%x, iocv_l_min=0x%x, iocv_l_avg=0x%x, lb_v_set=0x%x, roop_max=%d \n", + __func__, v_max, v_min, lb_v_avg, lb_v_set, roop_max); + pr_info("%s: ioci_l_max=0x%x, ioci_l_min=0x%x, ioci_l_avg=0x%x, lb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n", + __func__, i_max, i_min, lb_i_avg, lb_i_set, i_offset, sign_i_offset); + if(!only_lb) { + // cb get start + roop_start = SM5705_REG_IOCV_B_C_MIN; + roop_max = 6; + for (i = roop_start; i < roop_start + roop_max; i++) { + v_ret = sm5705_fg_i2c_read_word(client, i); + i_ret = sm5705_fg_i2c_read_word(client, i+0x10); + if((i_ret&0x4000) == 0x4000) + i_ret = -(i_ret&0x3FFF); + cb_v_buffer[i-roop_start] = v_ret; + cb_i_buffer[i-roop_start] = i_ret; + if (i == roop_start) { + v_max = v_ret; + v_min = v_ret; + v_sum = v_ret; + i_max = i_ret; + i_min = i_ret; + i_sum = i_ret; + } else { + if(v_ret > v_max) + v_max = v_ret; + else if(v_ret < v_min) + v_min = v_ret; + v_sum = v_sum + v_ret; + if(i_ret > i_max) + i_max = i_ret; + else if(i_ret < i_min) + i_min = i_ret; + i_sum = i_sum + i_ret; + } + if(abs(i_ret) > i_vset_margin) { + if(i_ret > 0) { + if(cb_i_p_v_min == 0) + cb_i_p_v_min = v_ret; + else + if(v_ret < cb_i_p_v_min) + cb_i_p_v_min = v_ret; + } else { + if(cb_i_n_v_max == 0) + cb_i_n_v_max = v_ret; + else + if(v_ret > cb_i_n_v_max) + cb_i_n_v_max = v_ret; + } + } + } + v_sum = v_sum - v_max - v_min; + i_sum = i_sum - i_max - i_min; + cb_v_avg = v_sum / (roop_max-2); + cb_i_avg = i_sum / (roop_max-2); + // cb get end + // cb_vset start + cb_last_index = (ret & 0x000F)-7; //-6-1 + if(cb_last_index < 0) + cb_last_index = 5; + for (i = roop_max; i > 0; i--) { + if(abs(cb_i_buffer[cb_last_index]) < i_vset_margin) { + cb_v_set = cb_v_buffer[cb_last_index]; + if(abs(cb_i_buffer[cb_last_index]) < i_offset_margin) + cb_i_set = cb_i_buffer[cb_last_index]; + cb_pre_last_index = cb_last_index - 1; + if(cb_pre_last_index < 0) + cb_pre_last_index = 5; + if(abs(cb_i_buffer[cb_pre_last_index]) < i_vset_margin) { + cb_v_set = MAXVAL(cb_v_buffer[cb_pre_last_index], cb_v_set); + if(abs(cb_i_buffer[cb_pre_last_index]) < i_offset_margin) + cb_i_set = MAXVAL(cb_i_buffer[cb_pre_last_index], cb_i_set); + } + } else { + cb_last_index--; + if(cb_last_index < 0) + cb_last_index = 5; + } + } + if(cb_v_set == 0) { + cb_v_set = cb_v_avg; + if(cb_i_set == 0) + cb_i_set = cb_i_avg; + } + if(cb_i_n_v_max > 0) + cb_v_set = MAXVAL(cb_i_n_v_max, cb_v_set); + //else if(cb_i_p_v_min > 0) + //{ + // cb_v_set = MINVAL(cb_i_p_v_min, cb_v_set); + //} + // cb_vset end + // cb offset make start + if(abs(cb_i_set) < i_offset_margin) { + if(cb_i_set > lb_i_set) { + i_offset = cb_i_set; + i_offset = i_offset + 4; // add extra offset + if(i_offset <= 0) { + sign_i_offset = 1; +#ifdef IGNORE_N_I_OFFSET + i_offset = 0; +#else + i_offset = -i_offset; +#endif + } + i_offset = i_offset>>1; + if(sign_i_offset == 0) + i_offset = i_offset|0x0080; + //do not write in kernel point. + //sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_OFF, i_offset); + } + } + // cb offset make end + pr_info("%s: iocv_c_max=0x%x, iocv_c_min=0x%x, iocv_c_avg=0x%x, cb_v_set=0x%x, cb_last_index=%d \n", + __func__, v_max, v_min, cb_v_avg, cb_v_set, cb_last_index); + pr_info("%s: ioci_c_max=0x%x, ioci_c_min=0x%x, ioci_c_avg=0x%x, cb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n", + __func__, i_max, i_min, cb_i_avg, cb_i_set, i_offset, sign_i_offset); + } + // final set + if((abs(cb_i_set) > i_vset_margin) || only_lb) + ret = MAXVAL(lb_v_set, cb_i_n_v_max); + else + ret = cb_v_set; + + return ret; +} +static void sm5705_set_soc_cycle_cfg(struct i2c_client *client) +{ + int value; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + value = fuelgauge->info.cycle_limit_cntl|(fuelgauge->info.cycle_high_limit<<12)|(fuelgauge->info.cycle_low_limit<<8); + pr_info("%s: cycle cfg value = 0x%x\n", __func__, value); + sm5705_fg_i2c_write_word(client, SM5705_REG_SOC_CYCLE_CFG, value); +} +#if defined(CONFIG_BATTERY_AGE_FORECAST) +int get_v_max_index_by_cycle(struct i2c_client *client) +{ + int cycle_index=0, len; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + for (len = fuelgauge->pdata->num_age_step-1; len >= 0; --len) { + if(fuelgauge->chg_full_soc == fuelgauge->pdata->age_data[len].full_condition_soc) { + cycle_index=len; + break; + } + } + pr_info("%s: chg_full_soc = %d, index = %d \n", __func__, fuelgauge->chg_full_soc, cycle_index); + + return cycle_index; +} +#endif +static bool sm5705_fg_reg_init(struct i2c_client *client, int is_surge) +{ + int i, j, value, ret; + uint8_t table_reg; + int write_table[2][16]; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + pr_info("%s: sm5705_fg_reg_init START!!\n", __func__); + // init mark + sm5705_fg_i2c_write_word(client, SM5705_REG_RESET, SM5705_FG_INIT_MARK); + // start first param_ctrl unlock + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_CTRL, SM5705_FG_PARAM_UNLOCK_CODE); + // RCE write + for (i = 0; i < 3; i++) { + sm5705_fg_i2c_write_word(client, SM5705_REG_RCE0+i, fuelgauge->info.rce_value[i]); + pr_info("%s: RCE write RCE%d = 0x%x : 0x%x\n", __func__, i, SM5705_REG_RCE0+i, fuelgauge->info.rce_value[i]); + } + // DTCD write + sm5705_fg_i2c_write_word(client, SM5705_REG_DTCD, fuelgauge->info.dtcd_value); + pr_info("%s: DTCD write DTCD = 0x%x : 0x%x\n", __func__, SM5705_REG_DTCD, fuelgauge->info.dtcd_value); + // RS write + sm5705_fg_i2c_write_word(client, SM5705_REG_AUTO_RS_MAN, fuelgauge->info.rs_value[0]); + pr_info("%s: RS write RS = 0x%x : 0x%x\n", __func__, SM5705_REG_AUTO_RS_MAN, fuelgauge->info.rs_value[0]); + // VIT_PERIOD write + sm5705_fg_i2c_write_word(client, SM5705_REG_VIT_PERIOD, fuelgauge->info.vit_period); + pr_info("%s: VIT_PERIOD write VIT_PERIOD = 0x%x : 0x%x\n", __func__, SM5705_REG_VIT_PERIOD, fuelgauge->info.vit_period); + // TABLE_LEN write & pram unlock + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_CTRL, SM5705_FG_PARAM_UNLOCK_CODE | SM5705_FG_TABLE_LEN); +#if defined(CONFIG_BATTERY_AGE_FORECAST) + i = get_v_max_index_by_cycle(client); + pr_info("%s: v_max_now is change %x -> %x \n", __func__, fuelgauge->info.v_max_now, fuelgauge->info.v_max_table[i]); + pr_info("%s: q_max_now is change %x -> %x \n", __func__, fuelgauge->info.q_max_now, fuelgauge->info.q_max_table[i]); + fuelgauge->info.v_max_now = fuelgauge->info.v_max_table[i]; + fuelgauge->info.q_max_now = fuelgauge->info.q_max_table[i]; +#endif + for (i=TABLE_MAX-1; i >= 0; i--){ + for(j=0; j <= SM5705_FG_TABLE_LEN; j++){ +#if defined(CONFIG_BATTERY_AGE_FORECAST) + if(i == Q_TABLE){ + write_table[i][j] = fuelgauge->info.battery_table[i][j]; + if(j == SM5705_FG_TABLE_LEN){ + write_table[i][SM5705_FG_TABLE_LEN-1] = fuelgauge->info.q_max_now; + write_table[i][SM5705_FG_TABLE_LEN] = fuelgauge->info.q_max_now + (fuelgauge->info.q_max_now/1000); + } + }else{ + write_table[i][j] = fuelgauge->info.battery_table[i][j]; + if(j == SM5705_FG_TABLE_LEN-1){ + write_table[i][SM5705_FG_TABLE_LEN-1] = fuelgauge->info.v_max_now; + if(write_table[i][SM5705_FG_TABLE_LEN-1] < write_table[i][SM5705_FG_TABLE_LEN-2]){ + write_table[i][SM5705_FG_TABLE_LEN-2] = write_table[i][SM5705_FG_TABLE_LEN-1] - 0x18; // ~11.7mV + write_table[Q_TABLE][SM5705_FG_TABLE_LEN-2] = (write_table[Q_TABLE][SM5705_FG_TABLE_LEN-1]*99)/100; + } + } + } +#else + write_table[i][j] = fuelgauge->info.battery_table[i][j]; +#endif + } + } + for (i=0; i < TABLE_MAX; i++) { + table_reg = SM5705_REG_TABLE_START + (i<<4); + for(j=0; j <= SM5705_FG_TABLE_LEN; j++) { + sm5705_fg_i2c_write_word(client, (table_reg + j), write_table[i][j]); + msleep(10); + if(write_table[i][j] != sm5705_fg_i2c_read_word(client, (table_reg + j))) { + pr_info("%s: TABLE write FAIL retry[%d][%d] = 0x%x : 0x%x\n", __func__, i, j, (table_reg + j), write_table[i][j]); + sm5705_fg_i2c_write_word(client, (table_reg + j), write_table[i][j]); + } + pr_info("%s: TABLE write OK [%d][%d] = 0x%x : 0x%x\n", __func__, i, j, (table_reg + j), write_table[i][j]); + } + } + // MIX_MODE write + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MIX_FACTOR, fuelgauge->info.rs_value[2]); + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MAX, fuelgauge->info.rs_value[3]); + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MIN, fuelgauge->info.rs_value[4]); + sm5705_fg_i2c_write_word(client, SM5705_REG_MIX_RATE, fuelgauge->info.mix_value[0]); + sm5705_fg_i2c_write_word(client, SM5705_REG_MIX_INIT_BLANK, fuelgauge->info.mix_value[1]); + pr_info("%s: RS_MIX_FACTOR = 0x%x, RS_MAX = 0x%x, RS_MIN = 0x%x, MIX_RATE = 0x%x, MIX_INIT_BLANK = 0x%x\n", + __func__, fuelgauge->info.rs_value[2], fuelgauge->info.rs_value[3], fuelgauge->info.rs_value[4], + fuelgauge->info.mix_value[0], fuelgauge->info.mix_value[1]); + // CAL write + sm5705_fg_i2c_write_word(client, SM5705_REG_VOLT_CAL, fuelgauge->info.volt_cal); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_OFF, fuelgauge->info.curr_offset); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_P_SLOPE, fuelgauge->info.p_curr_cal); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_N_SLOPE, fuelgauge->info.n_curr_cal); + pr_info("%s: VOLT_CAL = 0x%x, curr_offset = 0x%x, p_curr_cal = 0x%x, n_curr_cal = 0x%x\n", + __func__,fuelgauge->info.volt_cal, fuelgauge->info.curr_offset, fuelgauge->info.p_curr_cal, fuelgauge->info.n_curr_cal); + // MISC write + sm5705_fg_i2c_write_word(client, SM5705_REG_MISC, fuelgauge->info.misc); + pr_info("%s: SM5705_REG_MISC 0x%x : 0x%x\n", __func__, SM5705_REG_MISC, fuelgauge->info.misc); + // TOPOFF SOC + sm5705_fg_i2c_write_word(client, SM5705_REG_TOPOFFSOC, fuelgauge->info.topoff_soc); + pr_info("%s: SM5705_REG_TOPOFFSOC 0x%x : 0x%x\n", __func__, SM5705_REG_TOPOFFSOC, fuelgauge->info.topoff_soc); + // INIT_last - control register set + value = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + if(value == CNTL_REG_DEFAULT_VALUE) + value = fuelgauge->info.cntl_value; + value = ENABLE_MIX_MODE | ENABLE_TEMP_MEASURE | ENABLE_MANUAL_OCV | (fuelgauge->info.enable_topoff_soc << 13); + pr_info("%s: SM5705_REG_CNTL reg : 0x%x\n", __func__, value); + ret = sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, value); + if (ret < 0) + pr_info("%s: fail control register set(%d)\n", __func__, ret); + pr_info("%s: LAST SM5705_REG_CNTL = 0x%x : 0x%x\n", __func__, SM5705_REG_CNTL, value); + // LOCK + value = SM5705_FG_PARAM_LOCK_CODE | SM5705_FG_TABLE_LEN; + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_CTRL, value); + pr_info("%s: LAST PARAM CTRL VALUE = 0x%x : 0x%x\n", __func__, SM5705_REG_PARAM_CTRL, value); + // surge reset defence + if(is_surge) + value = ((fuelgauge->info.batt_ocv<<8)/125)+1; + else { + value = sm5705_calculate_iocv(client); + if((fuelgauge->info.volt_cal & 0x0080) == 0x0080) + value = value - (fuelgauge->info.volt_cal & 0x007F); + else + value = value + (fuelgauge->info.volt_cal & 0x007F); + } + sm5705_fg_i2c_write_word(client, SM5705_REG_IOCV_MAN, value); + pr_info("%s: IOCV_MAN_WRITE = %d : 0x%x\n", __func__, SM5705_REG_IOCV_MAN, value); + // init delay + msleep(20); + // write batt data version + value = (fuelgauge->info.data_ver << 4) & SM5705_BATTERY_VERSION; + sm5705_fg_i2c_write_word(client, SM5705_REG_RESERVED, value); + pr_info("%s: RESERVED = %d : 0x%x\n", __func__, SM5705_REG_RESERVED, value); + pr_info("%s: init_MARK = %d : 0x%x\n", __func__, SM5705_REG_RESET, sm5705_fg_i2c_read_word(client, SM5705_REG_RESET)); + + return 1; +} +static bool sm5705_fg_init(struct i2c_client *client, bool is_surge) +{ + int ret; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + fuelgauge->info.is_FG_initialised = 0; + fuelgauge->info.iocv_error_count = 0; + //board_fuelgauge_init(fuelgauge); + //SM5705 i2c read check + ret = sm5705_get_device_id(client); + if (ret < 0) + pr_info("%s: fail to do i2c read(%d)\n", __func__, ret); + if(fuelgauge->info.batt_ocv == 0) + sm5705_get_ocv(client); + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + if(ret != CNTL_REG_DEFAULT_VALUE) + fuelgauge->info.cntl_value = ret; + sm5705_set_soc_cycle_cfg(client); +#if defined(CONFIG_BATTERY_AGE_FORECAST) + fuelgauge->info.q_max_now = sm5705_fg_i2c_read_word(client, 0xBE); + pr_info("%s: q_max_now = 0x%x\n", __func__, fuelgauge->info.q_max_now); + fuelgauge->info.q_max_now = sm5705_fg_i2c_read_word(client, 0xBE); + pr_info("%s: q_max_now = 0x%x\n", __func__, fuelgauge->info.q_max_now); +#endif + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_PARAM_CTRL); + pr_info("%s: SM5705_REG_PARAM_CTRL 0x13 = 0x%x \n", __func__, ret); + if (ret != (SM5705_FG_PARAM_LOCK_CODE | SM5705_FG_TABLE_LEN)) { + pr_info("%s: SM5705_FG_PARAM_LOCK_CODE is abnormal Start quick-start\n", __func__); + // SW reset code + sm5705_fg_i2c_verified_write_word(client, SM5705_REG_RESET, SW_RESET_CODE); + // delay 800ms + msleep(800); + } + if(sm5705_fg_check_reg_init_need(client) || (ret != (SM5705_FG_PARAM_LOCK_CODE | SM5705_FG_TABLE_LEN))) + sm5705_fg_reg_init(client, is_surge); + // curr_off save and restore + if(fuelgauge->info.en_auto_curr_offset) { + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CURR_OFF); + fuelgauge->info.curr_offset = ret; + } else + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_OFF, fuelgauge->info.curr_offset); + // set lcal + if(fuelgauge->info.curr_lcal_en) { + sm5705_fg_i2c_write_word(client, SM5705_REG_CURRLCAL_0, fuelgauge->info.curr_lcal_0); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURRLCAL_1, fuelgauge->info.curr_lcal_1); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURRLCAL_2, fuelgauge->info.curr_lcal_2); + } + // get first measure all value + //soc + sm5705_get_soc(client); + //vbat + sm5705_get_vbat(client); + //current + sm5705_get_curr(client); + //ocv + sm5705_get_ocv(client); + //temperature + sm5705_get_temperature(client); + //cycle + sm5705_get_soc_cycle(client); + pr_info("%s: vbat=%d, vbat_avg=%d, curr=%d, curr_avg=%d, ocv=%d, temp=%d, cycle=%d, soc=%d, state=0x%x, Q=0x%x\n", + __func__, fuelgauge->info.batt_voltage, fuelgauge->info.batt_avgvoltage, + fuelgauge->info.batt_current, fuelgauge->info.batt_avgcurrent, fuelgauge->info.batt_ocv, + fuelgauge->info.temp_fg, fuelgauge->info.batt_soc_cycle, fuelgauge->info.batt_soc, + sm5705_fg_i2c_read_word(client, SM5705_REG_OCV_STATE), + sm5705_fg_i2c_read_word(client, SM5705_REG_Q_EST)); + // for debug + sm5705_fg_buffer_read(client); + sm5705_fg_test_read(client); + fuelgauge->info.is_FG_initialised = 1; + + return true; +} +static int sm5705_abnormal_reset_check(struct i2c_client *client) +{ + int cntl_read, reset_read; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + reset_read = sm5705_fg_i2c_read_word(client, SM5705_REG_RESET) & 0xF000; + // abnormal case process + if(sm5705_fg_check_reg_init_need(client) || (reset_read == 0)) { + cntl_read = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + pr_info("%s: SM5705 FG abnormal case!!!! SM5705_REG_CNTL : 0x%x, is_FG_initialised : %d, reset_read : 0x%x\n", + __func__, cntl_read, fuelgauge->info.is_FG_initialised, reset_read); + msleep(50); + if(fuelgauge->info.is_FG_initialised == 1) { + // SW reset code + if(sm5705_fg_i2c_verified_write_word(client, SM5705_REG_RESET, SW_RESET_OTP_CODE) < 0) + pr_info("%s: Warning!!!! SM5705 FG abnormal case.... SW reset FAIL \n", __func__); + else + pr_info("%s: SM5705 FG abnormal case.... SW reset OK\n", __func__); + // delay 400ms + msleep(400); + // init code + sm5705_fg_init(client, true); + } + return FG_ABNORMAL_RESET; + } + + return 0; +} +#ifdef ENABLE_FULL_OFFSET +void sm5705_adabt_full_offset(struct i2c_client *client) +{ + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + int fg_temp_gap; + int full_offset, i_offset, sign_offset, curr; + int curr_off, sign_origin, i_origin; + int curr, sign_curr, i_curr; + +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: flag_charge_health=%d, flag_full_charge=%d\n", + __func__, fuelgauge->info.flag_charge_health, fuelgauge->info.flag_full_charge); +#endif + if(fuelgauge->info.flag_charge_health && fuelgauge->info.flag_full_charge) { + fg_temp_gap = (fuelgauge->info.temp_fg/10) - fuelgauge->info.temp_std; + if(abs(fg_temp_gap) < 10) { + curr = sm5705_fg_i2c_read_word(client, SM5705_REG_CURRENT); + sign_curr = curr & 0x8000; + i_curr = (curr & 0x7FFF)>>1; + if(sign_curr == 1) + i_curr = -i_curr; + curr_off = sm5705_fg_i2c_read_word(client, SM5705_REG_CURR_OFF); + sign_origin = curr_off & 0x0080; + i_origin = curr_off & 0x007F; + if(sign_origin == 1) + i_origin = -i_origin; + full_offset = i_origin - i_curr; + if(full_offset < 0) { + i_offset = -full_offset; + sign_offset = 1; + } else { + i_offset = full_offset; + sign_offset = 0; + } + pr_info("%s: curr=%x, curr_off=%x, i_offset=%x, sign_offset=%d, full_offset_margin=%x, full_extra_offset=%x\n", + __func__, curr, curr_off, i_offset, sign_offset, fuelgauge->info.full_offset_margin, fuelgauge->info.full_extra_offset); + if(i_offset < ((fuelgauge->info.full_offset_margin<<10)/1000)) { + if(sign_offset == 1) + i_offset = -i_offset; + i_offset = i_offset + ((fuelgauge->info.full_extra_offset<<10)/1000); + if(i_offset <= 0) + full_offset = -i_offset; + else + full_offset = i_offset|0x0080; + fuelgauge->info.curr_offset = full_offset; + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_OFF, full_offset); + pr_info("%s: LAST i_offset=%x, sign_offset=%x, full_offset=%x\n", __func__, i_offset, sign_offset, full_offset); + } + } + } + + return; +} +#endif +void sm5705_vbatocv_check(struct i2c_client *client) +{ + int ret; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + // iocv error case cover start +#ifdef ABSOLUTE_ERROR_OCV_MATCH + if((abs(fuelgauge->info.batt_current)<40) || + ((fuelgauge->is_charging) && (fuelgauge->info.batt_current<(fuelgauge->info.top_off)) && + (fuelgauge->info.batt_current>(fuelgauge->info.top_off/3)) && (fuelgauge->info.batt_soc>=150))) +#else + if(((!fuelgauge->ta_exist) && (fuelgauge->info.batt_current<0) && (fuelgauge->info.batt_current>-40)) || + ((fuelgauge->ta_exist) && (fuelgauge->info.batt_current>0) && (fuelgauge->info.batt_current<40)) || + ((fuelgauge->is_charging) && (fuelgauge->info.batt_current<(fuelgauge->info.top_off)) && + (fuelgauge->info.batt_current>(fuelgauge->info.top_off/3)))) +#endif + { + if(abs(fuelgauge->info.batt_ocv-fuelgauge->info.batt_voltage)>30) // 30mV over + fuelgauge->info.iocv_error_count ++; + pr_info("%s: sm5705 FG iocv_error_count (%d)\n", __func__, fuelgauge->info.iocv_error_count); + if(fuelgauge->info.iocv_error_count > 5) // prevent to overflow + fuelgauge->info.iocv_error_count = 6; + } else + fuelgauge->info.iocv_error_count = 0; + if(fuelgauge->info.iocv_error_count > 5) { + pr_info("%s: p_v - v = (%d)\n", __func__, fuelgauge->info.p_batt_voltage - fuelgauge->info.batt_voltage); + if(abs(fuelgauge->info.p_batt_voltage - fuelgauge->info.batt_voltage)>15) // 15mV over + fuelgauge->info.iocv_error_count = 0; + else { + // mode change to mix RS manual mode + pr_info("%s: mode change to mix RS manual mode\n", __func__); + // run update set + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 1); + // RS manual value write + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MAN, fuelgauge->info.rs_value[0]); + // run update set + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 0); + // mode change + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + ret = (ret | ENABLE_MIX_MODE) | ENABLE_RS_MAN_MODE; // +RS_MAN_MODE + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, ret); + } + } else { + if((fuelgauge->info.temperature/10) > 15) { + if((fuelgauge->info.p_batt_voltage < fuelgauge->info.n_tem_poff) && + (fuelgauge->info.batt_voltage < fuelgauge->info.n_tem_poff) && (!fuelgauge->is_charging)) { + pr_info("%s: mode change to normal tem mix RS manual mode\n", __func__); + // mode change to mix RS manual mode + // run update init + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 1); + // RS manual value write + if((fuelgauge->info.p_batt_voltage < + (fuelgauge->info.n_tem_poff - fuelgauge->info.n_tem_poff_offset)) && + (fuelgauge->info.batt_voltage < + (fuelgauge->info.n_tem_poff - fuelgauge->info.n_tem_poff_offset))) + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MAN, fuelgauge->info.rs_value[0]>>1); + else + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MAN, fuelgauge->info.rs_value[0]); + // run update set + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 0); + // mode change + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + ret = (ret | ENABLE_MIX_MODE) | ENABLE_RS_MAN_MODE; // +RS_MAN_MODE + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, ret); + } else { + pr_info("%s: mode change to mix RS auto mode\n", __func__); + // mode change to mix RS auto mode + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + ret = (ret | ENABLE_MIX_MODE) & ~ENABLE_RS_MAN_MODE; // -RS_MAN_MODE + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, ret); + } + } else { + if((fuelgauge->info.p_batt_voltage < fuelgauge->info.l_tem_poff) && + (fuelgauge->info.batt_voltage < fuelgauge->info.l_tem_poff) && (!fuelgauge->is_charging)) { + pr_info("%s: mode change to normal tem mix RS manual mode\n", __func__); + // mode change to mix RS manual mode + // run update init + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 1); + // RS manual value write + if((fuelgauge->info.p_batt_voltage < + (fuelgauge->info.l_tem_poff - fuelgauge->info.l_tem_poff_offset)) && + (fuelgauge->info.batt_voltage < + (fuelgauge->info.l_tem_poff - fuelgauge->info.l_tem_poff_offset))) + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MAN, fuelgauge->info.rs_value[0]>>1); + else + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MAN, fuelgauge->info.rs_value[0]); + // run update set + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 0); + // mode change + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + ret = (ret | ENABLE_MIX_MODE) | ENABLE_RS_MAN_MODE; // +RS_MAN_MODE + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, ret); + } else { + pr_info("%s: mode change to mix RS auto mode\n", __func__); + // mode change to mix RS auto mode + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + ret = (ret | ENABLE_MIX_MODE) & ~ENABLE_RS_MAN_MODE; // -RS_MAN_MODE + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, ret); + } + } + } + fuelgauge->info.p_batt_voltage = fuelgauge->info.batt_voltage; + fuelgauge->info.p_batt_current = fuelgauge->info.batt_current; + /* iocv error case cover end */ + + /* this code is for 0x14 sitll has god 1 */ + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_PARAM_RUN_UPDATE); + pr_info("%s: PARAM_RUN_UPDATE 0x14 = 0x%x \n", __func__, ret); + if(ret) { + pr_info("%s: force update PARAM_RUN_UPDATE \n", __func__); + /* run update set */ + sm5705_fg_i2c_write_word(client, SM5705_REG_PARAM_RUN_UPDATE, 0); + } +} +static int sm5705_cal_carc (struct i2c_client *client) +{ + int p_curr_cal=0, n_curr_cal=0, p_delta_cal=0, n_delta_cal=0, p_fg_delta_cal=0, n_fg_delta_cal=0, temp_curr_offset=0; + int volt_cal=0, fg_delta_volcal=0, pn_volt_slope=0, volt_offset=0; + int temp_gap, fg_temp_gap, mix_factor=0; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + sm5705_vbatocv_check(client); +#ifdef ENABLE_FULL_OFFSET + sm5705_adabt_full_offset(client); +#endif + if(fuelgauge->is_charging || (fuelgauge->info.batt_current < LIMIT_N_CURR_MIXFACTOR)) + mix_factor = fuelgauge->info.rs_value[1]; + else + mix_factor = fuelgauge->info.rs_value[2]; + sm5705_fg_i2c_write_word(client, SM5705_REG_RS_MIX_FACTOR, mix_factor); + fg_temp_gap = (fuelgauge->info.temp_fg/10) - fuelgauge->info.temp_std; + volt_cal = sm5705_fg_i2c_read_word(client, SM5705_REG_VOLT_CAL); + volt_offset = volt_cal & 0x00FF; + pn_volt_slope = fuelgauge->info.volt_cal & 0xFF00; + if (fuelgauge->info.en_fg_temp_volcal) { + fg_delta_volcal = (fg_temp_gap / fuelgauge->info.fg_temp_volcal_denom)*fuelgauge->info.fg_temp_volcal_fact; + pn_volt_slope = pn_volt_slope + (fg_delta_volcal<<8); + volt_cal = pn_volt_slope | volt_offset; + sm5705_fg_i2c_write_word(client, SM5705_REG_VOLT_CAL, volt_cal); + } + temp_curr_offset = fuelgauge->info.curr_offset; + if(fuelgauge->info.en_high_fg_temp_offset && (fg_temp_gap > 0)) { + if(temp_curr_offset & 0x0080) + temp_curr_offset = -(temp_curr_offset & 0x007F); + temp_curr_offset = temp_curr_offset + (fg_temp_gap / fuelgauge->info.high_fg_temp_offset_denom)*fuelgauge->info.high_fg_temp_offset_fact; + if(temp_curr_offset < 0) { + temp_curr_offset = -temp_curr_offset; + temp_curr_offset = temp_curr_offset|0x0080; + } + } else if (fuelgauge->info.en_low_fg_temp_offset && (fg_temp_gap < 0)) { + if(temp_curr_offset & 0x0080) + temp_curr_offset = -(temp_curr_offset & 0x007F); + temp_curr_offset = temp_curr_offset + ((-fg_temp_gap) / fuelgauge->info.low_fg_temp_offset_denom)*fuelgauge->info.low_fg_temp_offset_fact; + if(temp_curr_offset < 0) { + temp_curr_offset = -temp_curr_offset; + temp_curr_offset = temp_curr_offset|0x0080; + } + } + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_OFF, temp_curr_offset); + n_curr_cal = fuelgauge->info.n_curr_cal; + p_curr_cal = fuelgauge->info.p_curr_cal; + if (fuelgauge->info.en_high_fg_temp_cal && (fg_temp_gap > 0)) { + p_fg_delta_cal = (fg_temp_gap / fuelgauge->info.high_fg_temp_p_cal_denom)*fuelgauge->info.high_fg_temp_p_cal_fact; + n_fg_delta_cal = (fg_temp_gap / fuelgauge->info.high_fg_temp_n_cal_denom)*fuelgauge->info.high_fg_temp_n_cal_fact; + } else if (fuelgauge->info.en_low_fg_temp_cal && (fg_temp_gap < 0)) { + fg_temp_gap = -fg_temp_gap; + p_fg_delta_cal = (fg_temp_gap / fuelgauge->info.low_fg_temp_p_cal_denom)*fuelgauge->info.low_fg_temp_p_cal_fact; + n_fg_delta_cal = (fg_temp_gap / fuelgauge->info.low_fg_temp_n_cal_denom)*fuelgauge->info.low_fg_temp_n_cal_fact; + } + p_curr_cal = p_curr_cal + (p_fg_delta_cal); + n_curr_cal = n_curr_cal + (n_fg_delta_cal); + pr_info("%s: <%d %d %d %d %d %d %d %d %d %d>, temp_fg = %d ,p_curr_cal = 0x%x, n_curr_cal = 0x%x, " + "curr_offset = 0x%x, volt_cal = 0x%x ,fg_delta_volcal = 0x%x\n", + __func__, fuelgauge->info.en_high_fg_temp_cal, + fuelgauge->info.high_fg_temp_p_cal_denom, fuelgauge->info.high_fg_temp_p_cal_fact, + fuelgauge->info.high_fg_temp_n_cal_denom, fuelgauge->info.high_fg_temp_n_cal_fact, + fuelgauge->info.en_low_fg_temp_cal, + fuelgauge->info.low_fg_temp_p_cal_denom, fuelgauge->info.low_fg_temp_p_cal_fact, + fuelgauge->info.low_fg_temp_n_cal_denom, fuelgauge->info.low_fg_temp_n_cal_fact, + fuelgauge->info.temp_fg, p_curr_cal, n_curr_cal, temp_curr_offset, volt_cal, fg_delta_volcal); + temp_gap = (fuelgauge->info.temperature/10) - fuelgauge->info.temp_std; + if (fuelgauge->info.en_high_temp_cal && (temp_gap > 0)) { + p_delta_cal = (temp_gap / fuelgauge->info.high_temp_p_cal_denom)*fuelgauge->info.high_temp_p_cal_fact; + n_delta_cal = (temp_gap / fuelgauge->info.high_temp_n_cal_denom)*fuelgauge->info.high_temp_n_cal_fact; + } else if (fuelgauge->info.en_low_temp_cal && (temp_gap < 0)) { + temp_gap = -temp_gap; + p_delta_cal = (temp_gap / fuelgauge->info.low_temp_p_cal_denom)*fuelgauge->info.low_temp_p_cal_fact; + n_delta_cal = (temp_gap / fuelgauge->info.low_temp_n_cal_denom)*fuelgauge->info.low_temp_n_cal_fact; + } + p_curr_cal = p_curr_cal + (p_delta_cal); + n_curr_cal = n_curr_cal + (n_delta_cal); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_P_SLOPE, p_curr_cal); + sm5705_fg_i2c_write_word(client, SM5705_REG_CURR_N_SLOPE, n_curr_cal); + pr_info("%s: <%d %d %d %d %d %d %d %d %d %d>, p_curr_cal = 0x%x, n_curr_cal = 0x%x, mix_factor=0x%x ,batt_temp = %d\n", + __func__, fuelgauge->info.en_high_temp_cal, + fuelgauge->info.high_temp_p_cal_denom, fuelgauge->info.high_temp_p_cal_fact, + fuelgauge->info.high_temp_n_cal_denom, fuelgauge->info.high_temp_n_cal_fact, + fuelgauge->info.en_low_temp_cal, + fuelgauge->info.low_temp_p_cal_denom, fuelgauge->info.low_temp_p_cal_fact, + fuelgauge->info.low_temp_n_cal_denom, fuelgauge->info.low_temp_n_cal_fact, + p_curr_cal, n_curr_cal, mix_factor, fuelgauge->info.temperature); + + return 0; +} +static int sm5705_get_all_value(struct i2c_client *client) +{ + union power_supply_propval value; + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + // check charging. + value.intval = POWER_SUPPLY_HEALTH_UNKNOWN; + psy_do_property("sm5705-charger", get, POWER_SUPPLY_PROP_HEALTH, value); +#ifdef SM5705_FG_FULL_DEBUG + pr_info("%s: get POWER_SUPPLY_PROP_HEALTH = 0x%x\n", __func__, value.intval); +#endif + fuelgauge->info.flag_charge_health = (value.intval == POWER_SUPPLY_HEALTH_GOOD) ? 1 : 0; + fuelgauge->is_charging = (fuelgauge->info.flag_charge_health | fuelgauge->ta_exist) + && (fuelgauge->info.batt_current>=30); + // check charger status + psy_do_property("sm5705-charger", get, POWER_SUPPLY_PROP_STATUS, value); + fuelgauge->info.flag_full_charge = (value.intval == POWER_SUPPLY_STATUS_FULL) ? 1 : 0; + fuelgauge->info.flag_chg_status = (value.intval == POWER_SUPPLY_STATUS_CHARGING) ? 1 : 0; + //vbat + sm5705_get_vbat(client); + //current + sm5705_get_curr(client); + //ocv + sm5705_get_ocv(client); + //temperature + sm5705_get_temperature(client); + //cycle + sm5705_get_soc_cycle(client); + //carc + sm5705_cal_carc(client); + //soc + sm5705_get_soc(client); + sm5705_fg_test_read(client); + pr_info("%s: chg_h=%d, chg_f=%d, chg_s=%d, is_chg=%d, ta_exist=%d, " + "v=%d, v_avg=%d, i=%d, i_avg=%d, ocv=%d, fg_t=%d, b_t=%d, cycle=%d, soc=%d, state=0x%x\n", + __func__, fuelgauge->info.flag_charge_health, fuelgauge->info.flag_full_charge, + fuelgauge->info.flag_chg_status, fuelgauge->is_charging, fuelgauge->ta_exist, + fuelgauge->info.batt_voltage, fuelgauge->info.batt_avgvoltage, + fuelgauge->info.batt_current, fuelgauge->info.batt_avgcurrent, fuelgauge->info.batt_ocv, + fuelgauge->info.temp_fg, fuelgauge->info.temperature, fuelgauge->info.batt_soc_cycle, + fuelgauge->info.batt_soc, sm5705_fg_i2c_read_word(client, SM5705_REG_OCV_STATE)); + + return 0; +} +static int sm5705_fg_get_jig_mode_real_vbat(struct i2c_client *client) +{ + int cntl, ret; + + cntl = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + pr_info("%s: start, CNTL=0x%x\n", __func__, cntl); + if(sm5705_fg_check_reg_init_need(client)) + return -1; + + cntl = cntl | ENABLE_MODE_nENQ4; + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, cntl); + msleep(300); + ret = sm5705_get_vbat(client); + pr_info("%s: jig mode real batt V = %d, CNTL=0x%x\n", __func__, ret, cntl); + cntl = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + cntl = cntl & (~ENABLE_MODE_nENQ4); + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, cntl); + pr_info("%s: end_1, CNTL=0x%x\n", __func__, cntl); + msleep(300); + cntl = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + cntl = cntl & (~ENABLE_MODE_nENQ4); + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, cntl); + pr_info("%s: end_2, CNTL=0x%x\n", __func__, cntl); + + return ret; +} +#ifdef CONFIG_OF +#if defined(CONFIG_PROJECT_GTS210VE) +#define SDI_ADC_MAX_LIMIT 30000 +static struct qpnp_vadc_chip *adc_client; +static enum qpnp_vadc_channels batt_id_adc_channel; +static void sm5705_adc_ap_init(struct sec_fuelgauge_info *fuelgauge) +{ + + if (!(&fuelgauge->client->dev)) + pr_err("%s : can't get fuelgauge dev \n", __func__); + else { + adc_client = qpnp_get_vadc(&fuelgauge->client->dev, "sm5705-fuelgauge"); + if (IS_ERR(adc_client)) { + int rc; + rc = PTR_ERR(adc_client); + if (rc != -EPROBE_DEFER) + pr_err("%s: Fail to get vadc %d\n", __func__, rc); + } + } +} +static int sm5705_adc_ap_read(int channel) +{ + struct qpnp_vadc_result result; + int data = -1; + int rc; + + switch (channel) { + case SEC_BAT_ADC_CHANNEL_BAT_CHECK : + rc = qpnp_vadc_read(adc_client, batt_id_adc_channel, &result); + if (rc) { + pr_err("%s: Unable to read batt adc=%d, batt_id_adc_channel=%d\n", + __func__, rc, batt_id_adc_channel); + return 0; + } + data = result.adc_code; + break; + default : + break; + } + pr_debug("%s: data(%d)\n", __func__, data); + + return data; +} +static int get_battery_id(struct sec_fuelgauge_info *fuelgauge,enum sec_battery_adc_channel channel) +{ + int batt_adc; + + fuelgauge->info.battery_typ = SDI_BATTERY_TYPE; + batt_id_adc_channel = P_MUX2_1_1; + pr_info("%s channel = %d \n", __func__, channel); + if (channel == SEC_BAT_ADC_CHANNEL_BAT_CHECK) { + sec_mpp_mux_control(BATT_ID_MUX_SEL_NUM, SEC_MUX_SEL_BATT_ID, 1); + batt_adc = sm5705_adc_ap_read(channel); + sec_mpp_mux_control(BATT_ID_MUX_SEL_NUM, SEC_MUX_SEL_BATT_ID, 0); + if (batt_adc > SDI_ADC_MAX_LIMIT) { + fuelgauge->info.battery_typ = ATL_BATTERY_TYPE; + pr_info("%s: batt_id_adc = (%d), battery type (%d)\n", + __func__, batt_adc, fuelgauge->info.battery_typ); + return ATL_BATTERY_TYPE; + } + else { + fuelgauge->info.battery_typ = SDI_BATTERY_TYPE; + pr_info("%s: batt_id_adc = (%d), battery type (%d)\n", + __func__, batt_adc, fuelgauge->info.battery_typ); + return SDI_BATTERY_TYPE; + } + pr_info("%s : ADC not in range batt_id_adc = (%d)\n", __func__, batt_adc); + } + + return SDI_BATTERY_TYPE; +} +#else +static int get_battery_id(struct sec_fuelgauge_info *fuelgauge) +{ + // sm5705fg does not support this function + return 0; +} +#endif +#define PROPERTY_NAME_SIZE 128 +#define PINFO(format, args...) \ + printk(KERN_INFO "%s() line-%d: " format, \ + __func__, __LINE__, ## args) +#define DECL_PARAM_PROP(_id, _name) {.id = _id, .name = _name,} +#if defined(CONFIG_BATTERY_AGE_FORECAST) +static int temp_parse_dt(struct sec_fuelgauge_info *fuelgauge) +{ + struct device_node *np = of_find_node_by_name(NULL, "battery"); + int len=0, ret; + const u32 *p; + + if (np == NULL) + pr_err("%s np NULL\n", __func__); + else { + p = of_get_property(np, "battery,age_data", &len); + if (p) { + fuelgauge->pdata->num_age_step = len / sizeof(sec_age_data_t); + fuelgauge->pdata->age_data = kzalloc(len, GFP_KERNEL); + ret = of_property_read_u32_array(np, "battery,age_data", + (u32 *)fuelgauge->pdata->age_data, len/sizeof(u32)); + if (ret) { + pr_err("%s failed to read battery->pdata->age_data: %d\n", __func__, ret); + kfree(fuelgauge->pdata->age_data); + fuelgauge->pdata->age_data = NULL; + fuelgauge->pdata->num_age_step = 0; + } + pr_info("%s num_age_step : %d\n", __func__, fuelgauge->pdata->num_age_step); + for (len = 0; len < fuelgauge->pdata->num_age_step; ++len) { + pr_info("[%d/%d]cycle:%d, float:%d, full_v:%d, recharge_v:%d, soc:%d\n", + len, fuelgauge->pdata->num_age_step-1, + fuelgauge->pdata->age_data[len].cycle, + fuelgauge->pdata->age_data[len].float_voltage, + fuelgauge->pdata->age_data[len].full_condition_vcell, + fuelgauge->pdata->age_data[len].recharge_condition_vcell, + fuelgauge->pdata->age_data[len].full_condition_soc); + } + } else { + fuelgauge->pdata->num_age_step = 0; + pr_err("%s there is not age_data\n", __func__); + } + } + + return 0; +} +#endif +static int sm5705_fg_parse_dt(struct sec_fuelgauge_info *fuelgauge) +{ + char prop_name[PROPERTY_NAME_SIZE]; + int battery_id = -1; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + int v_max_table[5]; + int q_max_table[5]; +#endif + int table[16]; + int rce_value[3]; + int rs_value[5]; + int mix_value[2]; + int topoff_soc[3]; + int cycle_cfg[3]; + int v_offset_cancel[4]; + int temp_volcal[3]; + int temp_offset[6]; + int temp_cal[10]; + int ext_temp_cal[10]; + int set_temp_poff[4]; + int curr_offset[2]; + int curr_lcal[4]; +#ifdef ENABLE_FULL_OFFSET + int full_offset[2]; +#endif + int ret; + int i, j; + struct device_node *np = of_find_node_by_name(NULL, "sm5705-fuelgauge"); + + /* reset, irq gpio info */ + if (np == NULL) + pr_err("%s np NULL\n", __func__); + else { + ret = of_get_named_gpio(np, "fuelgauge,fuel_int", 0); + if (ret > 0) { + fuelgauge->pdata->fg_irq = ret; + pr_info("%s reading fg_irq = %d\n", __func__, ret); + } + + ret = of_get_named_gpio(np, "fuelgauge,bat_int", 0); + if (ret > 0) { + fuelgauge->pdata->bat_irq_gpio = ret; + fuelgauge->pdata->bat_irq = gpio_to_irq(ret); + pr_info("%s reading bat_int_gpio = %d\n", __func__, ret); + } + + ret = of_property_read_u32(np, "fuelgauge,capacity_max", + &fuelgauge->pdata->capacity_max); + if (ret < 0) + pr_err("%s error reading capacity_max %d\n", __func__, ret); + + ret = of_property_read_u32(np, "fuelgauge,capacity_max_margin", + &fuelgauge->pdata->capacity_max_margin); + if (ret < 0) + pr_err("%s error reading capacity_max_margin %d\n", __func__, ret); + + ret = of_property_read_u32(np, "fuelgauge,capacity_min", + &fuelgauge->pdata->capacity_min); + if (ret < 0) + pr_err("%s error reading capacity_min %d\n", __func__, ret); + + pr_info("%s: capacity_max: %d, capacity_max_margin: 0x%x, capacity_min: %d\n", + __func__, fuelgauge->pdata->capacity_max, + fuelgauge->pdata->capacity_max_margin, + fuelgauge->pdata->capacity_min); + + ret = of_property_read_u32(np, "fuelgauge,capacity_calculation_type", + &fuelgauge->pdata->capacity_calculation_type); + if (ret < 0) + pr_err("%s error reading capacity_calculation_type %d\n", + __func__, ret); + ret = of_property_read_u32(np, "fuelgauge,fuel_alert_soc", + &fuelgauge->pdata->fuel_alert_soc); + if (ret < 0) + pr_err("%s error reading pdata->fuel_alert_soc %d\n", + __func__, ret); + fuelgauge->pdata->repeated_fuelalert = of_property_read_bool(np, + "fuelgaguge,repeated_fuelalert"); + + pr_info("%s: fg_irq: %d, calculation_type: 0x%x, fuel_alert_soc: %d, repeated_fuelalert: %d\n", + __func__, fuelgauge->pdata->fg_irq, fuelgauge->pdata->capacity_calculation_type, + fuelgauge->pdata->fuel_alert_soc, fuelgauge->pdata->repeated_fuelalert); + } + + // get battery_params node + np = of_find_node_by_name(of_node_get(np), "battery_params"); + if (np == NULL) { + PINFO("Cannot find child node \"battery_params\"\n"); + return -EINVAL; + } +#if defined(CONFIG_PROJECT_GTS210VE) + /*To initialize batt_id_adc channel*/ + sm5705_adc_ap_init(fuelgauge); +#endif + // get battery_id + if (of_property_read_u32(np, "battery,id", &battery_id) < 0) + PINFO("not battery,id property\n"); + if (battery_id == -1) +#if defined(CONFIG_PROJECT_GTS210VE) + battery_id = get_battery_id(fuelgauge,SEC_BAT_ADC_CHANNEL_BAT_CHECK); +#else + battery_id = get_battery_id(fuelgauge); +#endif + PINFO("battery id = %d\n", battery_id); +#if defined(CONFIG_BATTERY_AGE_FORECAST) + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_max_table"); + ret = of_property_read_u32_array(np, prop_name, v_max_table, fuelgauge->pdata->num_age_step); + + if(ret < 0) { + PINFO("Can get prop %s (%d)\n", prop_name, ret); + + for (i = 0; i < fuelgauge->pdata->num_age_step; i++){ + fuelgauge->info.v_max_table[i] = fuelgauge->info.battery_table[DISCHARGE_TABLE][SM5705_FG_TABLE_LEN-1]; + PINFO("%s = \n", prop_name, i, fuelgauge->info.v_max_table[i]); + } + } else { + for (i = 0; i < fuelgauge->pdata->num_age_step; i++){ + fuelgauge->info.v_max_table[i] = v_max_table[i]; + PINFO("%s = \n", prop_name, i, fuelgauge->info.v_max_table[i]); + } + } + + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "q_max_table"); + ret = of_property_read_u32_array(np, prop_name, q_max_table, fuelgauge->pdata->num_age_step); + + if(ret < 0) { + PINFO("Can get prop %s (%d)\n", prop_name, ret); + + for (i = 0; i < fuelgauge->pdata->num_age_step; i++){ + fuelgauge->info.q_max_table[i] = 100; + PINFO("%s = \n", prop_name, i, fuelgauge->info.q_max_table[i]); + } + } else { + for (i = 0; i < fuelgauge->pdata->num_age_step; i++){ + fuelgauge->info.q_max_table[i] = q_max_table[i]; + PINFO("%s = \n", prop_name, i, fuelgauge->info.q_max_table[i]); + } + } + fuelgauge->chg_full_soc = fuelgauge->pdata->age_data[0].full_condition_soc; + fuelgauge->info.v_max_now = fuelgauge->info.v_max_table[0]; + fuelgauge->info.q_max_now = fuelgauge->info.q_max_table[0]; + PINFO("%s = , , \n", prop_name, + fuelgauge->info.v_max_now, fuelgauge->info.q_max_now, fuelgauge->chg_full_soc); +#endif + // get battery_table + for (i = DISCHARGE_TABLE; i < TABLE_MAX; i++) { + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s%d", battery_id, "battery_table", i); + + ret = of_property_read_u32_array(np, prop_name, table, 16); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + for (j = 0; j <= SM5705_FG_TABLE_LEN; j++) { + fuelgauge->info.battery_table[i][j] = table[j]; + PINFO("%s = \n", prop_name, i, j, table[j]); + } + } + + // get rce + for (i = 0; i < 3; i++) { + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "rce_value"); + ret = of_property_read_u32_array(np, prop_name, rce_value, 3); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.rce_value[i] = rce_value[i]; + } + PINFO("%s = <0x%x 0x%x 0x%x>\n", prop_name, rce_value[0], rce_value[1], rce_value[2]); + + // get dtcd_value + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "dtcd_value"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.dtcd_value, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <0x%x>\n",prop_name, fuelgauge->info.dtcd_value); + + // get rs_value + for (i = 0; i < 5; i++) { + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "rs_value"); + ret = of_property_read_u32_array(np, prop_name, rs_value, 5); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.rs_value[i] = rs_value[i]; + } + PINFO("%s = <0x%x 0x%x 0x%x 0x%x 0x%x>\n", prop_name, + rs_value[0], rs_value[1], rs_value[2], rs_value[3], rs_value[4]); + + // get vit_period + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "vit_period"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.vit_period, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <0x%x>\n",prop_name, fuelgauge->info.vit_period); + + // get mix_value + for (i = 0; i < 2; i++) { + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "mix_value"); + ret = of_property_read_u32_array(np, prop_name, mix_value, 2); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.mix_value[i] = mix_value[i]; + } + PINFO("%s = <0x%x 0x%x>\n", prop_name, mix_value[0], mix_value[1]); + + // battery_type + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "battery_type"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.battery_type, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <%d>\n", prop_name, fuelgauge->info.battery_type); + + // MISC + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "misc"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.misc, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.misc); + + // V_ALARM + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_alarm"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.value_v_alarm, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <%d>\n", prop_name, fuelgauge->info.value_v_alarm); + + // TOP OFF SOC + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "topoff_soc"); + ret = of_property_read_u32_array(np, prop_name, topoff_soc, 3); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.enable_topoff_soc = topoff_soc[0]; + fuelgauge->info.topoff_soc = topoff_soc[1]; + fuelgauge->info.top_off = topoff_soc[2]; + + PINFO("%s = <%d %d %d>\n", prop_name, + fuelgauge->info.enable_topoff_soc, fuelgauge->info.topoff_soc, fuelgauge->info.top_off); + + // SOC cycle cfg + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "cycle_cfg"); + ret = of_property_read_u32_array(np, prop_name, cycle_cfg, 3); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.cycle_high_limit = cycle_cfg[0]; + fuelgauge->info.cycle_low_limit = cycle_cfg[1]; + fuelgauge->info.cycle_limit_cntl = cycle_cfg[2]; + + PINFO("%s = <%d %d %d>\n", prop_name, + fuelgauge->info.cycle_high_limit, fuelgauge->info.cycle_low_limit, fuelgauge->info.cycle_limit_cntl); + + // v_offset_cancel + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_offset_cancel"); + ret = of_property_read_u32_array(np, prop_name, v_offset_cancel, 4); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.enable_v_offset_cancel_p = v_offset_cancel[0]; + fuelgauge->info.enable_v_offset_cancel_n = v_offset_cancel[1]; + fuelgauge->info.v_offset_cancel_level = v_offset_cancel[2]; + fuelgauge->info.v_offset_cancel_mohm = v_offset_cancel[3]; + + PINFO("%s = <%d %d %d %d>\n", prop_name, + fuelgauge->info.enable_v_offset_cancel_p, fuelgauge->info.enable_v_offset_cancel_n, + fuelgauge->info.v_offset_cancel_level, fuelgauge->info.v_offset_cancel_mohm); + + // VOL & CURR CAL + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "volt_cal"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.volt_cal, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.volt_cal); + + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "curr_offset"); + ret = of_property_read_u32_array(np, prop_name, curr_offset, 2); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.en_auto_curr_offset = curr_offset[0]; + fuelgauge->info.curr_offset = curr_offset[1]; + + PINFO("%s = <%d 0x%x>\n", prop_name, fuelgauge->info.en_auto_curr_offset, fuelgauge->info.curr_offset); + +#ifdef ENABLE_FULL_OFFSET + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "full_offset"); + ret = of_property_read_u32_array(np, prop_name, full_offset, 2); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.full_offset_margin = full_offset[0]; + fuelgauge->info.full_extra_offset = full_offset[1]; + + PINFO("%s = <%d %d>\n", prop_name, fuelgauge->info.full_offset_margin, fuelgauge->info.full_extra_offset); +#endif + + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "p_curr_cal"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.p_curr_cal, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.p_curr_cal); + + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "n_curr_cal"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.n_curr_cal, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.n_curr_cal); + + // curr_lcal + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "curr_lcal"); + ret = of_property_read_u32_array(np, prop_name, curr_lcal, 4); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.curr_lcal_en = curr_lcal[0]; + fuelgauge->info.curr_lcal_0 = curr_lcal[1]; + fuelgauge->info.curr_lcal_1 = curr_lcal[2]; + fuelgauge->info.curr_lcal_2 = curr_lcal[3]; + PINFO("%s = <%d, 0x%x, 0x%x, 0x%x>\n", prop_name, + fuelgauge->info.curr_lcal_en, fuelgauge->info.curr_lcal_0, + fuelgauge->info.curr_lcal_1, fuelgauge->info.curr_lcal_2); + + // temp_std + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_std"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.temp_std, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <%d>\n", prop_name, fuelgauge->info.temp_std); + + // temp_volcal + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_volcal"); + ret = of_property_read_u32_array(np, prop_name, temp_volcal, 3); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.en_fg_temp_volcal = temp_volcal[0]; + fuelgauge->info.fg_temp_volcal_denom = temp_volcal[1]; + fuelgauge->info.fg_temp_volcal_fact = temp_volcal[2]; + PINFO("%s = <%d, %d, %d>\n", prop_name, + fuelgauge->info.en_fg_temp_volcal, fuelgauge->info.fg_temp_volcal_denom, + fuelgauge->info.fg_temp_volcal_fact); + + // temp_offset + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_offset"); + ret = of_property_read_u32_array(np, prop_name, temp_offset, 6); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.en_high_fg_temp_offset = temp_offset[0]; + fuelgauge->info.high_fg_temp_offset_denom = temp_offset[1]; + fuelgauge->info.high_fg_temp_offset_fact = temp_offset[2]; + fuelgauge->info.en_low_fg_temp_offset = temp_offset[3]; + fuelgauge->info.low_fg_temp_offset_denom = temp_offset[4]; + fuelgauge->info.low_fg_temp_offset_fact = temp_offset[5]; + PINFO("%s = <%d, %d, %d, %d, %d, %d>\n", prop_name, + fuelgauge->info.en_high_fg_temp_offset, + fuelgauge->info.high_fg_temp_offset_denom, fuelgauge->info.high_fg_temp_offset_fact, + fuelgauge->info.en_low_fg_temp_offset, + fuelgauge->info.low_fg_temp_offset_denom, fuelgauge->info.low_fg_temp_offset_fact); + + // temp_calc + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_cal"); + ret = of_property_read_u32_array(np, prop_name, temp_cal, 10); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.en_high_fg_temp_cal = temp_cal[0]; + fuelgauge->info.high_fg_temp_p_cal_denom = temp_cal[1]; + fuelgauge->info.high_fg_temp_p_cal_fact = temp_cal[2]; + fuelgauge->info.high_fg_temp_n_cal_denom = temp_cal[3]; + fuelgauge->info.high_fg_temp_n_cal_fact = temp_cal[4]; + fuelgauge->info.en_low_fg_temp_cal = temp_cal[5]; + fuelgauge->info.low_fg_temp_p_cal_denom = temp_cal[6]; + fuelgauge->info.low_fg_temp_p_cal_fact = temp_cal[7]; + fuelgauge->info.low_fg_temp_n_cal_denom = temp_cal[8]; + fuelgauge->info.low_fg_temp_n_cal_fact = temp_cal[9]; + PINFO("%s = <%d, %d, %d, %d, %d, %d, %d, %d, %d, %d>\n", prop_name, + fuelgauge->info.en_high_fg_temp_cal, + fuelgauge->info.high_fg_temp_p_cal_denom, fuelgauge->info.high_fg_temp_p_cal_fact, + fuelgauge->info.high_fg_temp_n_cal_denom, fuelgauge->info.high_fg_temp_n_cal_fact, + fuelgauge->info.en_low_fg_temp_cal, + fuelgauge->info.low_fg_temp_p_cal_denom, fuelgauge->info.low_fg_temp_p_cal_fact, + fuelgauge->info.low_fg_temp_n_cal_denom, fuelgauge->info.low_fg_temp_n_cal_fact); + + // ext_temp_calc + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "ext_temp_cal"); + ret = of_property_read_u32_array(np, prop_name, ext_temp_cal, 10); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.en_high_temp_cal = ext_temp_cal[0]; + fuelgauge->info.high_temp_p_cal_denom = ext_temp_cal[1]; + fuelgauge->info.high_temp_p_cal_fact = ext_temp_cal[2]; + fuelgauge->info.high_temp_n_cal_denom = ext_temp_cal[3]; + fuelgauge->info.high_temp_n_cal_fact = ext_temp_cal[4]; + fuelgauge->info.en_low_temp_cal = ext_temp_cal[5]; + fuelgauge->info.low_temp_p_cal_denom = ext_temp_cal[6]; + fuelgauge->info.low_temp_p_cal_fact = ext_temp_cal[7]; + fuelgauge->info.low_temp_n_cal_denom = ext_temp_cal[8]; + fuelgauge->info.low_temp_n_cal_fact = ext_temp_cal[9]; + PINFO("%s = <%d, %d, %d, %d, %d, %d, %d, %d, %d, %d>\n", prop_name, + fuelgauge->info.en_high_temp_cal, + fuelgauge->info.high_temp_p_cal_denom, fuelgauge->info.high_temp_p_cal_fact, + fuelgauge->info.high_temp_n_cal_denom, fuelgauge->info.high_temp_n_cal_fact, + fuelgauge->info.en_low_temp_cal, + fuelgauge->info.low_temp_p_cal_denom, fuelgauge->info.low_temp_p_cal_fact, + fuelgauge->info.low_temp_n_cal_denom, fuelgauge->info.low_temp_n_cal_fact); + + // tem poff level + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "tem_poff"); + ret = of_property_read_u32_array(np, prop_name, set_temp_poff, 4); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + fuelgauge->info.n_tem_poff = set_temp_poff[0]; + fuelgauge->info.n_tem_poff_offset = set_temp_poff[1]; + fuelgauge->info.l_tem_poff = set_temp_poff[2]; + fuelgauge->info.l_tem_poff_offset = set_temp_poff[3]; + + PINFO("%s = <%d, %d, %d, %d>\n", prop_name, + fuelgauge->info.n_tem_poff, fuelgauge->info.n_tem_poff_offset, + fuelgauge->info.l_tem_poff, fuelgauge->info.l_tem_poff_offset); + + // batt data version + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "data_ver"); + ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.data_ver, 1); + if (ret < 0) + PINFO("Can get prop %s (%d)\n", prop_name, ret); + PINFO("%s = <%d>\n", prop_name, fuelgauge->info.data_ver); + + return 0; +} +#else +static int sm5705_fg_parse_dt(struct sec_fuelgauge_info *fuelgauge) +{ + return 0; +} +#endif +bool sm5705_fg_fuelalert_init(struct i2c_client *client, int soc) +{ + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + int ret; + int value_v_alarm, value_soc_alarm; + + if (soc >= 0) { + // remove interrupt + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_INTFG); + // check status + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_STATUS); + // remove all mask + sm5705_fg_i2c_write_word(client,SM5705_REG_INTFG_MASK, 0x0000); + /* enable volt alert only, other alert mask*/ + ret = MASK_L_SOC_INT|MASK_H_TEM_INT|MASK_L_TEM_INT; + sm5705_fg_i2c_write_word(client,SM5705_REG_INTFG_MASK,ret); + fuelgauge->info.irq_ctrl = ~(ret); + /* set volt and soc alert threshold */ + value_v_alarm = (((fuelgauge->info.value_v_alarm)<<8)/1000); + sm5705_fg_i2c_write_word(client, SM5705_REG_V_ALARM, value_v_alarm); + value_soc_alarm = 0x0100; // 1.00% + sm5705_fg_i2c_write_word(client, SM5705_REG_SOC_ALARM, value_soc_alarm); + // enabel volt alert control, other alert disable + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_CNTL); + ret = ret | ENABLE_V_ALARM; + ret = ret & (~ENABLE_SOC_ALARM & ~ENABLE_T_H_ALARM & ~ENABLE_T_L_ALARM); + sm5705_fg_i2c_write_word(client, SM5705_REG_CNTL, ret); + pr_info("%s: irq_ctrl=0x%x, REG_CNTL=0x%x, V_ALARM=%d, SOC_ALARM=0x%x \n", + __func__, fuelgauge->info.irq_ctrl, ret, value_v_alarm, value_soc_alarm); + } + /* alert flag init*/ + fuelgauge->info.soc_alert_flag = false; + fuelgauge->is_fuel_alerted = false; + + return true; +} +bool sm5705_fg_is_fuelalerted(struct i2c_client *client) +{ + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + int ret; + + /* alert process */ + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_INTFG); + pr_info("%s: SM5705_REG_INTFG(0x%x)\n", __func__, ret); + if(ret & fuelgauge->info.irq_ctrl) { + // check status + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_STATUS); + pr_info("%s: SM5705_REG_STATUS(0x%x)\n", __func__, ret); + if(ret & fuelgauge->info.irq_ctrl) + return true; + } + + return false; +} +bool sm5705_fg_fuelalert_process(void *irq_data, bool is_fuel_alerted) +{ + struct sec_fuelgauge_info *fuelgauge = irq_data; + struct i2c_client *client = fuelgauge->client; + int ret; + + pr_info("%s: is_fuel_alerted=%d \n", __func__, is_fuel_alerted); + if(is_fuel_alerted) { + ret = sm5705_fg_i2c_read_word(client, SM5705_REG_STATUS); + pr_info("%s: SM5705_REG_STATUS(0x%x)\n", __func__, ret); + /* not use SOC alarm + if(ret & fuelgauge->info.irq_ctrl & ENABLE_SOC_ALARM) { + fuelgauge->info.soc_alert_flag = true; + // todo more action + } + */ + if(ret & fuelgauge->info.irq_ctrl & ENABLE_V_ALARM) { + fuelgauge->info.volt_alert_flag = true; + // todo more action + } + } + + return true; +} +/* capacity is 0.1% unit */ +static void sm5705_fg_get_scaled_capacity(struct sec_fuelgauge_info *fuelgauge, + union power_supply_propval *val) +{ + val->intval = (val->intval < fuelgauge->pdata->capacity_min) ? + 0 : ((val->intval - fuelgauge->pdata->capacity_min) * 1000 / + (fuelgauge->capacity_max - fuelgauge->pdata->capacity_min)); + pr_info("%s: scaled capacity (%d.%d)\n", __func__, val->intval/10, val->intval%10); +} +/* capacity is integer */ +static void sm5705_fg_get_atomic_capacity(struct sec_fuelgauge_info *fuelgauge, + union power_supply_propval *val) +{ + pr_info("%s : NOW(%d), OLD(%d)\n", __func__, val->intval, fuelgauge->capacity_old); + if (fuelgauge->pdata->capacity_calculation_type & + SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC) { + if (fuelgauge->capacity_old < val->intval) + val->intval = fuelgauge->capacity_old + 1; + else if (fuelgauge->capacity_old > val->intval) + val->intval = fuelgauge->capacity_old - 1; + } + /* keep SOC stable in abnormal status */ + if (fuelgauge->pdata->capacity_calculation_type & + SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL) { + if (!fuelgauge->ta_exist && fuelgauge->capacity_old < val->intval) { + pr_info("%s: capacity (old %d : new %d)\n", + __func__, fuelgauge->capacity_old, val->intval); + val->intval = fuelgauge->capacity_old; + } + } + /* updated old capacity */ + fuelgauge->capacity_old = val->intval; +} +static int sm5705_fg_check_capacity_max(struct sec_fuelgauge_info *fuelgauge, int capacity_max) +{ + int cap_max, cap_min; + + cap_max = (fuelgauge->pdata->capacity_max + + fuelgauge->pdata->capacity_max_margin) * 100 / 101; + cap_min = (fuelgauge->pdata->capacity_max - + fuelgauge->pdata->capacity_max_margin) * 100 / 101; + + return (capacity_max < cap_min) ? cap_min : + ((capacity_max > cap_max) ? cap_max : capacity_max); +} +static int sm5705_fg_calculate_dynamic_scale(struct sec_fuelgauge_info *fuelgauge, int capacity) +{ + union power_supply_propval raw_soc_val; + + raw_soc_val.intval = sm5705_get_soc(fuelgauge->client); + if (raw_soc_val.intval < fuelgauge->pdata->capacity_max - fuelgauge->pdata->capacity_max_margin) { + pr_info("%s: raw soc(%d) is very low, skip routine\n", __func__, raw_soc_val.intval); + return fuelgauge->capacity_max; + } else { + fuelgauge->capacity_max = + (raw_soc_val.intval > + fuelgauge->pdata->capacity_max + fuelgauge->pdata->capacity_max_margin) ? + (fuelgauge->pdata->capacity_max + fuelgauge->pdata->capacity_max_margin) : + raw_soc_val.intval; + pr_debug("%s: raw soc (%d)", __func__, fuelgauge->capacity_max); + } + fuelgauge->capacity_max = (fuelgauge->capacity_max * 100 / (capacity + 1)); + fuelgauge->capacity_old = capacity; + pr_info("%s: %d is used for capacity_max, capacity(%d)\n", __func__, fuelgauge->capacity_max, capacity); + + return fuelgauge->capacity_max; +} +bool sm5705_fg_reset(struct i2c_client *client) +{ + pr_info("%s: Start quick-start\n", __func__); + // SW reset code + msleep(50); + sm5705_fg_i2c_verified_write_word(client, SM5705_REG_RESET, SW_RESET_CODE); + // delay 800ms + msleep(800); + // init code + sm5705_fg_init(client, false); + pr_info("%s: End quick-start\n", __func__); + + return true; +} +static void sm5705_fg_reset_capacity_by_jig_connection(struct sec_fuelgauge_info *fuelgauge) +{ + union power_supply_propval value; + int ret; + + pr_info("%s: (Jig Connection)\n", __func__); + ret = sm5705_fg_i2c_read_word(fuelgauge->client, SM5705_REG_RESERVED); + ret |= SM5705_JIG_CONNECTED; + sm5705_fg_i2c_write_word(fuelgauge->client, SM5705_REG_RESERVED, ret); + /* If JIG is attached, the voltage is set as 1079 */ + value.intval = 1079; + psy_do_property("battery", set, POWER_SUPPLY_PROP_VOLTAGE_NOW, value); +} +static int sm5705_fg_get_property(struct power_supply *psy, enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sec_fuelgauge_info *fuelgauge = power_supply_get_drvdata(psy); + + switch (psp) { + /* Additional Voltage Information (mV) */ + case POWER_SUPPLY_PROP_PRESENT: + // SM5705 is not suport this prop + sm5705_fg_get_batt_present(fuelgauge->client); + break; + /* Cell voltage (VCELL, mV) */ + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + sm5705_get_vbat(fuelgauge->client); + val->intval = fuelgauge->info.batt_voltage; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + switch (val->intval) { + case SEC_BATTERY_VOLTAGE_AVERAGE: + sm5705_get_vbat(fuelgauge->client); + val->intval = fuelgauge->info.batt_avgvoltage; + break; + case SEC_BATTERY_VOLTAGE_OCV: + sm5705_get_ocv(fuelgauge->client); + val->intval = fuelgauge->info.batt_ocv; + break; + } + break; + /* Current (mA) */ + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + val->intval = sm5705_fg_get_jig_mode_real_vbat(fuelgauge->client); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + sm5705_get_curr(fuelgauge->client); + if (val->intval == SEC_BATTERY_CURRENT_UA) + val->intval = fuelgauge->info.batt_current * 1000; + else + val->intval = fuelgauge->info.batt_current; + break; + /* Average Current (mA) */ + case POWER_SUPPLY_PROP_CURRENT_AVG: + sm5705_get_curr(fuelgauge->client); + if (val->intval == SEC_BATTERY_CURRENT_UA) + val->intval = fuelgauge->info.batt_avgcurrent * 1000; + else + val->intval = fuelgauge->info.batt_avgcurrent; + break; + /* Battery Temperature */ + case POWER_SUPPLY_PROP_TEMP: + /* Target Temperature */ + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + sm5705_get_temperature(fuelgauge->client); + val->intval = fuelgauge->info.temp_fg; + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + val->intval = fuelgauge->is_charging; + break; + /* SOC (%) */ + case POWER_SUPPLY_PROP_CAPACITY: + sm5705_get_all_value(fuelgauge->client); + /* SM5705 F/G unit is 0.1%, raw ==> convert the unit to 0.01% */ + if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RAW) { + val->intval = fuelgauge->info.batt_soc * 10; + break; + } else if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE) { + val->intval = fuelgauge->raw_capacity; + } else { + val->intval = fuelgauge->info.batt_soc; + if (fuelgauge->pdata->capacity_calculation_type & + (SEC_FUELGAUGE_CAPACITY_TYPE_SCALE | + SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE)) + sm5705_fg_get_scaled_capacity(fuelgauge, val); + /* capacity should be between 0% and 100% + * (0.1% degree) + */ + if (val->intval > 1000) + val->intval = 1000; + if (val->intval < 0) + val->intval = 0; + fuelgauge->raw_capacity = val->intval; + /* get only integer part */ + val->intval /= 10; + /* check whether doing the wake_unlock */ + if ((val->intval > fuelgauge->pdata->fuel_alert_soc) && + fuelgauge->is_fuel_alerted) { + wake_unlock(&fuelgauge->fuel_alert_wake_lock); + sm5705_fg_fuelalert_init(fuelgauge->client, + fuelgauge->pdata->fuel_alert_soc); + } + /* (Only for atomic capacity) + * In initial time, capacity_old is 0. + * and in resume from sleep, + * capacity_old is too different from actual soc. + * should update capacity_old + * by val->intval in booting or resume. + */ + if (fuelgauge->initial_update_of_soc) { + /* updated old capacity */ + fuelgauge->capacity_old = val->intval; + fuelgauge->initial_update_of_soc = false; + break; + } + if (fuelgauge->pdata->capacity_calculation_type & + (SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC | + SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL)) + sm5705_fg_get_atomic_capacity(fuelgauge, val); + } + break; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = fuelgauge->capacity_max; + break; + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_NOW: + return -ENODATA; + default: + return -EINVAL; + } + + return 0; +} +static int sm5705_fg_set_property(struct power_supply *psy, enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sec_fuelgauge_info *fuelgauge = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (val->intval == POWER_SUPPLY_STATUS_FULL) { + fuelgauge->info.flag_full_charge = 1; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + pr_info("%s: POWER_SUPPLY_STATUS_FULL : q_max_now = 0x%x \n", + __func__, fuelgauge->info.q_max_now); + if(fuelgauge->info.q_max_now != + fuelgauge->info.q_max_table[get_v_max_index_by_cycle(fuelgauge->client)]) + if (!sm5705_fg_reset(fuelgauge->client)) + return -EINVAL; +#endif + } + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + if (fuelgauge->pdata->capacity_calculation_type & + SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE) + sm5705_fg_calculate_dynamic_scale(fuelgauge, val->intval); +#if defined(CONFIG_BATTERY_AGE_FORECAST) + pr_info("%s: POWER_SUPPLY_PROP_CHARGE_FULL : q_max_now = 0x%x \n", + __func__, fuelgauge->info.q_max_now); + if(fuelgauge->info.q_max_now != + fuelgauge->info.q_max_table[get_v_max_index_by_cycle(fuelgauge->client)]){ + if (!sm5705_fg_reset(fuelgauge->client)) + return -EINVAL; + } +#endif + break; + case POWER_SUPPLY_PROP_ONLINE: + fuelgauge->cable_type = val->intval; + if (val->intval == SEC_BATTERY_CABLE_NONE) + fuelgauge->ta_exist = false; + else + fuelgauge->ta_exist = true; + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + switch (val->intval) { + case SEC_BAT_CHG_MODE_BUCK_OFF: + case SEC_BAT_CHG_MODE_CHARGING_OFF: + fuelgauge->is_charging = false; + break; + case SEC_BAT_CHG_MODE_CHARGING: + fuelgauge->is_charging = true; + break; + }; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RESET) { + fuelgauge->initial_update_of_soc = true; + if (!sm5705_fg_reset(fuelgauge->client)) + return -EINVAL; + else + break; + } + case POWER_SUPPLY_PROP_TEMP: + fuelgauge->info.temperature = val->intval; + break; + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + break; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + pr_info("%s: capacity_max changed, %d -> %d\n", + __func__, fuelgauge->capacity_max, val->intval); + fuelgauge->capacity_max = sm5705_fg_check_capacity_max(fuelgauge, val->intval); + fuelgauge->initial_update_of_soc = true; + break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + sm5705_fg_reset_capacity_by_jig_connection(fuelgauge); + break; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + pr_info("%s: full condition soc changed, %d -> %d\n", + __func__, fuelgauge->chg_full_soc, val->intval); + fuelgauge->chg_full_soc = val->intval; + break; +#endif + default: + return -EINVAL; + } + + return 0; +} +static void sm5705_fg_isr_work(struct work_struct *work) +{ + struct sec_fuelgauge_info *fuelgauge = + container_of(work, struct sec_fuelgauge_info, isr_work.work); + + /* process for fuel gauge chip */ + sm5705_fg_fuelalert_process(fuelgauge, fuelgauge->is_fuel_alerted); + /* process for others */ + if (fuelgauge->pdata->fuelalert_process != NULL) + fuelgauge->pdata->fuelalert_process(fuelgauge->is_fuel_alerted); +} +static irqreturn_t sm5705_fg_irq_thread(int irq, void *irq_data) +{ + struct sec_fuelgauge_info *fuelgauge = irq_data; + bool fuel_alerted; + + if (fuelgauge->pdata->fuel_alert_soc >= 0) { + fuel_alerted = sm5705_fg_is_fuelalerted(fuelgauge->client); + pr_info("%s: Fuel-alert %salerted!\n", __func__, fuel_alerted ? "" : "NOT "); + if (fuel_alerted == fuelgauge->is_fuel_alerted) { + if (!fuelgauge->pdata->repeated_fuelalert) { + dev_dbg(&fuelgauge->client->dev, "%s: Fuel-alert Repeated (%d)\n", + __func__, fuelgauge->is_fuel_alerted); + return IRQ_HANDLED; + } + } + if (fuel_alerted) + wake_lock(&fuelgauge->fuel_alert_wake_lock); + else + wake_unlock(&fuelgauge->fuel_alert_wake_lock); + fuelgauge->is_fuel_alerted = fuel_alerted; + schedule_delayed_work(&fuelgauge->isr_work, 0); + } + + return IRQ_HANDLED; +} +static int sm5705_create_attrs(struct device *dev) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(sm5705_fg_attrs); i++) { + rc = device_create_file(dev, &sm5705_fg_attrs[i]); + if (rc) + goto create_attrs_failed; + } + goto create_attrs_succeed; + +create_attrs_failed: + dev_err(dev, "%s: failed (%d)\n", __func__, rc); + while (i--) + device_remove_file(dev, &sm5705_fg_attrs[i]); +create_attrs_succeed: + return rc; +} +ssize_t sm5705_fg_show_attrs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const ptrdiff_t offset = attr - sm5705_fg_attrs; + struct sec_fuelgauge_info *fuelgauge = dev_get_drvdata(dev); + + int i = 0, j = 0; + u8 reg = 0; + int reg_data = 0; + + switch (offset) { + case FG_REG: + case FG_DATA: + /* 0x00 ~ 0x5f, 0x80~0xbf */ + for (j = 0; j < 12; j++) { + for (reg = 0; reg < 0x10; reg++) { + reg_data = sm5705_fg_i2c_read_word(fuelgauge->client, reg + j * 0x10); + i += scnprintf(buf + i, PAGE_SIZE - i, "0x%02x:\t0x%04x\n", reg + j * 0x10, reg_data); + } + if (j == 5) + j = 7; + } + break; + case FG_REGS: + break; + default: + i = -EINVAL; + break; + } + + return i; +} +ssize_t sm5705_fg_store_attrs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + const ptrdiff_t offset = attr - sm5705_fg_attrs; + struct sec_fuelgauge_info *fuelgauge = dev_get_drvdata(dev); + + int ret = 0; + int x = 0, y = 0; + + switch (offset) { + case FG_REG: + case FG_REGS: + break; + case FG_DATA: + if (sscanf(buf, "0x%8x 0x%8x", &x, &y) == 2) { + if (x >= 0x00 && x <= 0xff) { + u8 addr = x; + u16 data = y; + pr_info("%s FG_DATA write : 0x%x = 0x%x \n", __func__, addr, data); + if (sm5705_fg_i2c_write_word(fuelgauge->client, addr, data)) { + pr_err("%s: addr: 0x%x write fail\n", __func__, addr); + } + } else { + pr_err("%s: addr: 0x%x is wrong\n", __func__, x); + } + } + ret = count; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +static const struct power_supply_desc sm5705_fuelgauge_power_supply_desc = { + .name = "sm5705-fuelgauge", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .properties = sm5705_fuelgauge_props, + .num_properties = ARRAY_SIZE(sm5705_fuelgauge_props), + .get_property = sm5705_fg_get_property, + .set_property = sm5705_fg_set_property, +}; +static int sm5705_fuelgauge_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct sec_fuelgauge_info *fuelgauge; + sec_battery_platform_data_t *pdata = NULL; + struct power_supply_config fuelgauge_cfg = {}; +// struct battery_data_t *battery_data = NULL; + int ret = 0; + union power_supply_propval raw_soc_val; + + pr_info("%s: SM5705 Fuelgauge Driver Loading\n", __func__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + fuelgauge = kzalloc(sizeof(*fuelgauge), GFP_KERNEL); + if (!fuelgauge) + return -ENOMEM; + + mutex_init(&fuelgauge->fg_lock); + + fuelgauge->client = client; + + if (client->dev.of_node) { + int error; + pdata = devm_kzalloc(&client->dev, sizeof(sec_battery_platform_data_t), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free; + } + + fuelgauge->pdata = pdata; + + mutex_init(&fuelgauge->info.param_lock); + mutex_lock(&fuelgauge->info.param_lock); +#if defined(CONFIG_BATTERY_AGE_FORECAST) + temp_parse_dt(fuelgauge); +#endif + error = sm5705_fg_parse_dt(fuelgauge); + mutex_unlock(&fuelgauge->info.param_lock); + if (error < 0) { + dev_err(&client->dev, "%s: Failed to get fuel_int\n", __func__); + goto err_parse_dt; + } + } else { + dev_err(&client->dev, "%s: Failed to get of_node\n", __func__); + fuelgauge->pdata = client->dev.platform_data; + } + i2c_set_clientdata(client, fuelgauge); + + if (fuelgauge->pdata->fg_gpio_init != NULL) { + dev_err(&client->dev, "%s: @@@\n", __func__); + if (!fuelgauge->pdata->fg_gpio_init()) { + dev_err(&client->dev, "%s: Failed to Initialize GPIO\n", __func__); + goto err_devm_free; + } + } + + if (!sm5705_fg_init(fuelgauge->client, false)) { + dev_err(&client->dev, "%s: Failed to Initialize Fuelgauge\n", __func__); + goto err_devm_free; + } + + fuelgauge_cfg.drv_data = fuelgauge; + fuelgauge->capacity_max = fuelgauge->pdata->capacity_max; + raw_soc_val.intval = sm5705_get_soc(fuelgauge->client); + if(raw_soc_val.intval > fuelgauge->pdata->capacity_max) + sm5705_fg_calculate_dynamic_scale(fuelgauge, 100); + + fuelgauge->psy_fg = power_supply_register(&client->dev, + &sm5705_fuelgauge_power_supply_desc, &fuelgauge_cfg); + if (!fuelgauge->psy_fg) { + pr_err("%s: Failed to Register psy_fg\n", __func__); + goto err_free; + } + + fuelgauge->is_fuel_alerted = false; + if (fuelgauge->pdata->fuel_alert_soc >= 0) { + if (sm5705_fg_fuelalert_init(fuelgauge->client, fuelgauge->pdata->fuel_alert_soc)) + wake_lock_init(&fuelgauge->fuel_alert_wake_lock, + WAKE_LOCK_SUSPEND, "fuel_alerted"); + else { + dev_err(&client->dev, "%s: Failed to Initialize Fuel-alert\n", __func__); + goto err_irq; + } + } + + if (fuelgauge->pdata->fg_irq > 0) { + INIT_DELAYED_WORK(&fuelgauge->isr_work, sm5705_fg_isr_work); + fuelgauge->fg_irq = gpio_to_irq(fuelgauge->pdata->fg_irq); + pr_info("%s: fg_irq = %d\n", __func__, fuelgauge->fg_irq); + if (fuelgauge->fg_irq > 0) { + ret = request_threaded_irq(fuelgauge->fg_irq, + NULL, sm5705_fg_irq_thread, + IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, + "fuelgauge-irq", fuelgauge); + if (ret) { + dev_err(&client->dev, "%s: Failed to Reqeust IRQ\n", __func__); + goto err_supply_unreg; + } + + ret = enable_irq_wake(fuelgauge->fg_irq); + if (ret < 0) + dev_err(&client->dev, "%s: Failed to Enable Wakeup Source(%d)\n", + __func__, ret); + } else { + dev_err(&client->dev, "%s: Failed gpio_to_irq(%d)\n", + __func__, fuelgauge->fg_irq); + goto err_supply_unreg; + } + } + + fuelgauge->initial_update_of_soc = true; + fuelgauge->info.temperature = 250; + //if (sec_bat_check_jig_status()) + // sm5705_fg_reset_capacity_by_jig_connection(fuelgauge); + + dev_set_drvdata(&client->dev, fuelgauge); + ret = sm5705_create_attrs(&client->dev); + if (ret) { + dev_err(&client->dev, "%s : Failed to create_attrs\n", __func__); + goto err_irq; + } + + pr_info("%s: SEC Fuelgauge Driver Loaded\n", __func__); + + return 0; + +err_irq: + if (fuelgauge->fg_irq > 0) + free_irq(fuelgauge->fg_irq, fuelgauge); + wake_lock_destroy(&fuelgauge->fuel_alert_wake_lock); +err_supply_unreg: + power_supply_unregister(fuelgauge->psy_fg); +err_devm_free: +err_parse_dt: + if(pdata) + devm_kfree(&client->dev, pdata); +// if(battery_data) +// devm_kfree(&client->dev, battery_data); +err_free: + mutex_destroy(&fuelgauge->fg_lock); + kfree(fuelgauge); + pr_info("%s: Fuel gauge probe failed\n", __func__); + + return ret; +} +static int sm5705_fuelgauge_remove(struct i2c_client *client) +{ + struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client); + + if (fuelgauge->pdata->fuel_alert_soc >= 0) + wake_lock_destroy(&fuelgauge->fuel_alert_wake_lock); + + return 0; +} +static int sm5705_fuelgauge_suspend(struct device *dev) +{ + return 0; +} +static int sm5705_fuelgauge_resume(struct device *dev) +{ + return 0; +} +static void sm5705_fuelgauge_shutdown(struct i2c_client *client) +{ +} + +static const struct i2c_device_id sm5705_fuelgauge_id[] = { + {"sm5705-fuelgauge", 0}, + {} +}; + +static const struct dev_pm_ops sm5705_fuelgauge_pm_ops = { + .suspend = sm5705_fuelgauge_suspend, + .resume = sm5705_fuelgauge_resume, +}; + +MODULE_DEVICE_TABLE(i2c, sm5705_fuelgauge_id); +static struct of_device_id fuelgague_i2c_match_table[] = { + { .compatible = "sm5705-fuelgauge,i2c", }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, fuelgague_i2c_match_table); + +static struct i2c_driver sm5705_fuelgauge_driver = { + .driver = { + .name = "sm5705-fuelgauge", + .owner = THIS_MODULE, + .of_match_table = fuelgague_i2c_match_table, +#ifdef CONFIG_PM + .pm = &sm5705_fuelgauge_pm_ops, +#endif + }, + .probe = sm5705_fuelgauge_probe, + .remove = sm5705_fuelgauge_remove, + .shutdown = sm5705_fuelgauge_shutdown, + .id_table = sm5705_fuelgauge_id, +}; + +static int __init sm5705_fuelgauge_init(void) +{ + pr_info("%s \n", __func__); + + return i2c_add_driver(&sm5705_fuelgauge_driver); +} + +static void __exit sm5705_fuelgauge_exit(void) +{ + i2c_del_driver(&sm5705_fuelgauge_driver); +} + +module_init(sm5705_fuelgauge_init); +module_exit(sm5705_fuelgauge_exit); + +MODULE_DESCRIPTION("Samsung SM5705 Fuel Gauge Driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ccic/Kconfig b/drivers/ccic/Kconfig new file mode 100644 index 000000000000..5e20957c4ea4 --- /dev/null +++ b/drivers/ccic/Kconfig @@ -0,0 +1,61 @@ +# +# CCIC devices +# + +comment "CCIC configs" + +config CCIC_NOTIFIER + bool "CCIC notifier support" + depends on I2C + default n + help + If you say yes here you will get support for + the CCIC attached device status change notification. + +config CCIC_S2MM005 + bool "CCIC S2MM005" + depends on I2C + default n + help + If you say yes here you will get support for + s2mm005 ccic full version chipset + +config CCIC_ALTERNATE_MODE + bool "support CCIC alternate mode" + depends on I2C + default n + help + If you say yes here you will get support for + alternate mode + +config CCIC_LPM_ENABLE + bool "Support LPM ENABLE" + depends on CCIC_S2MM005 + default n + help + If you say yes here you will get support for + lpm enable + +config CCIC_WATER_DETECT + bool "support WATER DETECT" + depends on CCIC_S2MM005 + default n + help + If you say yes here you will get support for + water detect Enable + +config CCIC_MANUAL_UPDATE + bool "support CCIC manual update" + depends on CCIC_S2MM005 + default n + help + If you say yes here you will get support for + ccic manual update + +config CCIC_S2MM005_ANALOG_AUDIO + bool "Support type-C analog audio" + depends on CCIC_S2MM005 + default n + help + If you say yes here you will get support for + water detect Enable diff --git a/drivers/ccic/Makefile b/drivers/ccic/Makefile new file mode 100644 index 000000000000..a4c0be491a63 --- /dev/null +++ b/drivers/ccic/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for ccic devices +# + +obj-$(CONFIG_CCIC_NOTIFIER) += ccic_notifier.o ccic_sysfs.o ccic_alternate.o +obj-$(CONFIG_CCIC_S2MM005) += s2mm005_fw.o s2mm005_cc.o s2mm005_pd.o s2mm005.o +obj-$(CONFIG_CCIC_ALTERNATE_MODE) += ccic_misc.o diff --git a/drivers/ccic/ccic_alternate.c b/drivers/ccic/ccic_alternate.c new file mode 100644 index 000000000000..4f8d68c0a792 --- /dev/null +++ b/drivers/ccic/ccic_alternate.c @@ -0,0 +1,1982 @@ +/* + * driver/ccic/ccic_alternate.c - S2MM005 USB CCIC Alternate mode driver + * + * Copyright (C) 2016 Samsung Electronics + * Author: Wookwang Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +#include +#endif +#include "ccic_misc.h" +#if defined(CONFIG_USB_HOST_NOTIFY) +#include +#endif +/* switch device header */ +#if defined(CONFIG_SWITCH) +#include +#endif /* CONFIG_SWITCH */ +#include + +#define MAX_INPUT_DATA (255) + +//////////////////////////////////////////////////////////////////////////////// +// s2mm005_cc.c called s2mm005_alternate.c +//////////////////////////////////////////////////////////////////////////////// +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +extern struct device *ccic_device; +extern int dwc3_msm_is_suspended(void); +extern int dwc3_msm_is_host_highspeed(void); +extern int dwc3_restart_usb_host_mode_hs(void); + +#if defined(CONFIG_SWITCH) +static struct switch_dev switch_dock = { + .name = "ccic_dock", +}; +#endif /* CONFIG_SWITCH */ + +static char VDM_MSG_IRQ_State_Print[9][40] = +{ + {"bFLAG_Vdm_Reserve_b0"}, + {"bFLAG_Vdm_Discover_ID"}, + {"bFLAG_Vdm_Discover_SVIDs"}, + {"bFLAG_Vdm_Discover_MODEs"}, + {"bFLAG_Vdm_Enter_Mode"}, + {"bFLAG_Vdm_Exit_Mode"}, + {"bFLAG_Vdm_Attention"}, + {"bFlag_Vdm_DP_Status_Update"}, + {"bFlag_Vdm_DP_Configure"}, +}; + +static char DP_Pin_Assignment_Print[7][40] = +{ + {"DP_Pin_Assignment_None"}, + {"DP_Pin_Assignment_A"}, + {"DP_Pin_Assignment_B"}, + {"DP_Pin_Assignment_C"}, + {"DP_Pin_Assignment_D"}, + {"DP_Pin_Assignment_E"}, + {"DP_Pin_Assignment_F"}, + +}; +//////////////////////////////////////////////////////////////////////////////// +// Alternate mode processing +//////////////////////////////////////////////////////////////////////////////// +int ccic_register_switch_device(int mode) +{ +#ifdef CONFIG_SWITCH + int ret = 0; + if (mode) { + ret = switch_dev_register(&switch_dock); + if (ret < 0) { + pr_err("%s: Failed to register dock switch(%d)\n", + __func__, ret); + return -ENODEV; + } + } else { + switch_dev_unregister(&switch_dock); + } +#endif /* CONFIG_SWITCH */ + return 0; +} + +static void ccic_send_dock_intent(int type) +{ + pr_info("%s: CCIC dock type(%d)\n", __func__, type); +#ifdef CONFIG_SWITCH + switch_set_state(&switch_dock, type); +#endif /* CONFIG_SWITCH */ +} + +void ccic_send_dock_uevent(u32 vid, u32 pid, int state) +{ + char switch_string[32]; + char pd_ids_string[32]; + char *envp[3] = { switch_string, pd_ids_string, NULL }; + + pr_info("%s: CCIC dock : USBPD_IPS=%04x:%04x SWITCH_STATE=%d\n", + __func__, + le16_to_cpu(vid), + le16_to_cpu(pid), + state); + + if (IS_ERR(ccic_device)) { + pr_err("%s CCIC ERROR: Failed to send a dock uevent!\n", + __func__); + return; + } + + snprintf(switch_string, 32, "SWITCH_STATE=%d", state); + snprintf(pd_ids_string, 32, "USBPD_IDS=%04x:%04x", + le16_to_cpu(vid), + le16_to_cpu(pid)); + kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, envp); +} + +void acc_detach_check(struct work_struct *wk) +{ + struct delayed_work *delay_work = + container_of(wk, struct delayed_work, work); + struct s2mm005_data *usbpd_data = + container_of(delay_work, struct s2mm005_data, acc_detach_work); + + pr_info("%s: usbpd_data->pd_state : %d\n", __func__, usbpd_data->pd_state); + if (usbpd_data->pd_state == State_PE_Initial_detach) { + if (usbpd_data->acc_type != CCIC_DOCK_DETACHED) { + if (usbpd_data->acc_type != CCIC_DOCK_NEW) + ccic_send_dock_intent(CCIC_DOCK_DETACHED); + ccic_send_dock_uevent(usbpd_data->Vendor_ID, + usbpd_data->Product_ID, + CCIC_DOCK_DETACHED); + usbpd_data->acc_type = CCIC_DOCK_DETACHED; + usbpd_data->Vendor_ID = 0; + usbpd_data->Product_ID = 0; + } + } +} + +void set_enable_powernego(int mode) +{ + struct s2mm005_data *usbpd_data; + u8 W_DATA[2]; + + if (!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if (!usbpd_data) + return; + + if(mode) { + W_DATA[0] = 0x3; + W_DATA[1] = 0x42; + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + pr_info("%s : Power nego start\n", __func__); + } else { + W_DATA[0] = 0x3; + W_DATA[1] = 0x42; + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + pr_info("%s : Power nego stop\n", __func__); + } +} + +void acc_detach_process(void *data) +{ + struct s2mm005_data *usbpd_data = data; + CC_NOTI_TYPEDEF ccic_alt_noti; + + pr_info("%s: usbpd_data->pd_state : %d\n", __func__, usbpd_data->pd_state); + ccic_alt_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_alt_noti.dest = CCIC_NOTIFY_DEV_DP; + ccic_alt_noti.id = CCIC_NOTIFY_ID_DP_CONNECT; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DETACH; + ccic_alt_noti.sub2 = 0; + ccic_alt_noti.sub3 = 0; + ccic_event_work(usbpd_data,ccic_alt_noti.dest,ccic_alt_noti.id, + ccic_alt_noti.sub1,ccic_alt_noti.sub2,ccic_alt_noti.sub3); + usbpd_data->acc_type = CCIC_DOCK_DETACHED; +} + +void set_usb_phy_completion(int kind) +{ + struct s2mm005_data *usbpd_data; + + if(!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if(!usbpd_data) + return; + + if(kind == 0){ + pr_info("%s : resume complete! \n", __func__); + complete(&usbpd_data->resume_wait); + } + else{ + pr_info("%s : suspend complete! \n", __func__); + complete(&usbpd_data->suspend_wait); + } +} + +void set_enable_alternate_mode(int mode) +{ + struct s2mm005_data *usbpd_data; + static int check_is_driver_loaded = 0; + static int prev_alternate_mode = 0; + u8 W_DATA[2]; + + if (!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if (!usbpd_data) + return; + +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + store_usblog_notify(NOTIFY_ALTERNATEMODE, (void *)&mode, NULL); +#endif + if ((mode & ALTERNATE_MODE_NOT_READY) && (mode & ALTERNATE_MODE_READY)) { + pr_info("%s : mode is invalid! \n", __func__); + return; + } + if ((mode & ALTERNATE_MODE_START) && (mode & ALTERNATE_MODE_STOP)) { + pr_info("%s : mode is invalid! \n", __func__); + return; + } + if (mode & ALTERNATE_MODE_RESET) { + pr_info("%s : mode is reset! check_is_driver_loaded=%d, prev_alternate_mode=%d\n", + __func__, check_is_driver_loaded, prev_alternate_mode); + if (check_is_driver_loaded && (prev_alternate_mode == ALTERNATE_MODE_START)) { + W_DATA[0] = 0x3; + W_DATA[1] = 0x31; + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + pr_info("%s : alternate mode is reset as start! \n", __func__); + prev_alternate_mode = ALTERNATE_MODE_START; + set_enable_powernego(1); + } else if (check_is_driver_loaded && (prev_alternate_mode == ALTERNATE_MODE_STOP)) { + W_DATA[0] = 0x3; + W_DATA[1] = 0x33; + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + pr_info("%s : alternate mode is reset as stop! \n",__func__); + prev_alternate_mode = ALTERNATE_MODE_STOP; + } else + ; + } else { + if (mode & ALTERNATE_MODE_NOT_READY) { + check_is_driver_loaded = 0; + pr_info("%s : alternate mode is not ready! \n", __func__); + } else if (mode & ALTERNATE_MODE_READY) { + check_is_driver_loaded = 1; + pr_info("%s : alternate mode is ready! \n", __func__); + } else + ; + + if (check_is_driver_loaded) { + if (mode & ALTERNATE_MODE_START) { + W_DATA[0] = 0x3; + W_DATA[1] = 0x31; + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + pr_info("%s : alternate mode is started! \n", __func__); + prev_alternate_mode = ALTERNATE_MODE_START; + set_enable_powernego(1); + } else if(mode & ALTERNATE_MODE_STOP) { + W_DATA[0] = 0x3; + W_DATA[1] = 0x33; + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + pr_info("%s : alternate mode is stopped! \n",__func__); + prev_alternate_mode = ALTERNATE_MODE_STOP; + } + } + } + return; +} + +void set_clear_discover_mode(void) +{ + struct s2mm005_data *usbpd_data; + u8 W_DATA[2]; + + if(!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if(!usbpd_data) + return; + + W_DATA[0] =0x3; + W_DATA[1] =0x32; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + + pr_info("%s : clear discover mode! \n", __func__); +} + +void set_host_turn_on_event(int mode) +{ + struct s2mm005_data *usbpd_data; + + if(!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if(!usbpd_data) + return; + + pr_info("%s : current_set is %d! \n", __func__, mode); + if(mode) { + usbpd_data->detach_done_wait = 0; + usbpd_data->host_turn_on_event = 1; + wake_up_interruptible(&usbpd_data->host_turn_on_wait_q); + } + else { + usbpd_data->detach_done_wait = 0; + usbpd_data->host_turn_on_event = 0; + } +} + +// temporarily add this function for DP TG request +void sbu_check(void * data) +{ + struct s2mm005_data *usbpd_data = data; + u8 W_DATA[4]; + u8 R_DATA; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return; + } + + W_DATA[0] =0x2; + W_DATA[1] =0x10; + + // 0:Open_Drain 1:CMOS + W_DATA[2] =0x74; + W_DATA[3] =0x10; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 4); + s2mm005_read_byte(usbpd_data->i2c, 0x14, &R_DATA, 1); + + pr_err("%s CMOS SBU1 mode = %2x , SBU2 mode = %2x \n", __func__, + (R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5); // it shoud be 0 + + + W_DATA[0] =0x2; + W_DATA[1] =0x10; + + // 0:Input Mode 1:Output Mode + W_DATA[2] =0x78; + W_DATA[3] =0x10; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 4); + s2mm005_read_byte(usbpd_data->i2c, 0x14, &R_DATA, 1); + + pr_err("%s Output Mode SBU1 status = %2x , SBU2 status = %2x \n", __func__, + (R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5); // it shoud be 1 + + + W_DATA[0] =0x2; + W_DATA[1] =0x10; + + // 0:Out Low 1:Out High + W_DATA[2] =0x80; + W_DATA[3] =0x10; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 4); + s2mm005_read_byte(usbpd_data->i2c, 0x14, &R_DATA, 1); + + pr_err("%s Low/High Check SBU1 status = %2x , SBU2 status = %2x \n", __func__, + (R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5); // it shoud be 1 + + return; +} + +int get_diplayport_status(void) +{ + struct s2mm005_data *usbpd_data; + if(!ccic_device) + return 0; + usbpd_data = dev_get_drvdata(ccic_device); + if(!usbpd_data) + return 0; + + pr_info("%s : current dp status %d! \n", __func__, usbpd_data->dp_is_connect); + return usbpd_data->dp_is_connect; +} + +static int process_check_accessory(void * data) +{ + struct s2mm005_data *usbpd_data = data; +#if defined(CONFIG_USB_HOST_NOTIFY) && defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + uint16_t vid = usbpd_data->Vendor_ID; + uint16_t pid = usbpd_data->Product_ID; + uint16_t acc_type = CCIC_DOCK_DETACHED; + + /* detect Gear VR */ + if (usbpd_data->acc_type == CCIC_DOCK_DETACHED) { + if (vid == SAMSUNG_VENDOR_ID) { + switch (pid) { + /* GearVR: Reserved GearVR PID+6 */ + case GEARVR_PRODUCT_ID: + case GEARVR_PRODUCT_ID_1: + case GEARVR_PRODUCT_ID_2: + case GEARVR_PRODUCT_ID_3: + case GEARVR_PRODUCT_ID_4: + case GEARVR_PRODUCT_ID_5: + acc_type = CCIC_DOCK_HMT; + pr_info("%s : Samsung Gear VR connected.\n", __func__); +#if defined(CONFIG_USB_HOST_NOTIFY) && defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_VR_USE_COUNT); +#endif + break; + case DEXDOCK_PRODUCT_ID: + acc_type = CCIC_DOCK_DEX; + pr_info("%s : Samsung DEX connected.\n", __func__); +#if defined(CONFIG_USB_HOST_NOTIFY) && defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_DEX_USE_COUNT); +#endif + break; + case DEXPAD_PRODUCT_ID: + acc_type = CCIC_DOCK_DEXPAD; + pr_info("%s : Samsung DEX PADconnected.\n", __func__); +#if defined(CONFIG_USB_HOST_NOTIFY) && defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_DEX_USE_COUNT); +#endif + break; + case HDMI_PRODUCT_ID: + acc_type = CCIC_DOCK_HDMI; + pr_info("%s : Samsung HDMI connected.\n", __func__); + break; + default: + acc_type = CCIC_DOCK_NEW; + pr_info("%s : default device connected.\n", __func__); + break; + } + } else if (vid == SAMSUNG_MPA_VENDOR_ID) { + switch(pid) { + case MPA_PRODUCT_ID: + acc_type = CCIC_DOCK_MPA; + pr_info("%s : Samsung MPA connected.\n", __func__); + break; + default: + acc_type = CCIC_DOCK_NEW; + pr_info("%s : default device connected.\n", __func__); + break; + } + } else { + acc_type = CCIC_DOCK_NEW; + pr_info("%s : unknown device connected.\n", __func__); + } + usbpd_data->acc_type = acc_type; + } else + acc_type = usbpd_data->acc_type; + + if (acc_type != CCIC_DOCK_NEW) + ccic_send_dock_intent(acc_type); + + ccic_send_dock_uevent(vid, pid, acc_type); + return 1; +} + +static int process_discover_identity(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_RX_DIS_ID; + uint8_t ReadMSG[32] = {0,}; + int ret = 0; + + // Message Type Definition + U_DATA_MSG_ID_HEADER_Type *DATA_MSG_ID = (U_DATA_MSG_ID_HEADER_Type *)&ReadMSG[8]; + U_PRODUCT_VDO_Type *DATA_MSG_PRODUCT = (U_PRODUCT_VDO_Type *)&ReadMSG[16]; + + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c error.\n", __func__); + return ret; + } + + usbpd_data->is_samsung_accessory_enter_mode = 0; + usbpd_data->is_sent_pin_configuration = 0; + usbpd_data->Vendor_ID = DATA_MSG_ID->BITS.USB_Vendor_ID; + usbpd_data->Product_ID = DATA_MSG_PRODUCT->BITS.Product_ID; + usbpd_data->Device_Version = DATA_MSG_PRODUCT->BITS.Device_Version; + + dev_info(&i2c->dev, "%s Vendor_ID : 0x%X, Product_ID : 0x%X Device Version 0x%X \n",\ + __func__, usbpd_data->Vendor_ID, usbpd_data->Product_ID, usbpd_data->Device_Version); + if (process_check_accessory(usbpd_data)) + dev_info(&i2c->dev, "%s : Samsung Accessory Connected.\n", __func__); + return 0; +} + +static int process_discover_svids(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_RX_DIS_SVID; + uint8_t ReadMSG[32] = {0,}; + int ret = 0; +#if defined(CONFIG_USB_HOST_NOTIFY) && defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + CC_NOTI_TYPEDEF ccic_alt_noti; + // Message Type Definition + U_VDO1_Type *DATA_MSG_VDO1 = (U_VDO1_Type *)&ReadMSG[8]; +#if defined(CONFIG_USB_HOST_NOTIFY) + uint timeleft = 0; +#endif + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c error.\n", __func__); + return ret; + } + + usbpd_data->SVID_0 = DATA_MSG_VDO1->BITS.SVID_0; + usbpd_data->SVID_1 = DATA_MSG_VDO1->BITS.SVID_1; + + if (usbpd_data->SVID_0 == TypeC_DP_SUPPORT) { + ccic_alt_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_alt_noti.dest = CCIC_NOTIFY_DEV_DP; + ccic_alt_noti.id = CCIC_NOTIFY_ID_DP_CONNECT; + ccic_alt_noti.sub1 = CCIC_NOTIFY_ATTACH; + ccic_alt_noti.sub2 = usbpd_data->Vendor_ID; + ccic_alt_noti.sub3 = usbpd_data->Product_ID; + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + timeleft = wait_event_interruptible_timeout(usbpd_data->host_turn_on_wait_q, + !usbpd_data->detach_done_wait, (usbpd_data->host_turn_on_wait_time)*HZ); + dev_info(&i2c->dev, "%s detach_done_wait = %d\n", __func__, timeleft); + usbpd_data->dp_is_connect = 1; + + /* If you want to support USB SuperSpeed when you connect + * Display port dongle, You should change dp_hs_connect depend + * on Pin assignment.If DP use 4lane(Pin Assignment C,E,A), + * dp_hs_connect is 1. USB can support HS. + * If DP use 2lane(Pin Assigment B,D,F),dp_hs_connect is 0. USB + * can support SS + */ + usbpd_data->dp_hs_connect = 1; +#if defined(CONFIG_USB_HOST_NOTIFY) && defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_DP_USE_COUNT); +#endif + + timeleft = wait_event_interruptible_timeout(usbpd_data->host_turn_on_wait_q, + usbpd_data->host_turn_on_event, (usbpd_data->host_turn_on_wait_time)*HZ); + dev_info(&i2c->dev, "%s host turn on wait = %d \n", __func__, timeleft); +#endif + ccic_event_work(usbpd_data,ccic_alt_noti.dest,ccic_alt_noti.id, + ccic_alt_noti.sub1,ccic_alt_noti.sub2,ccic_alt_noti.sub3); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB_DP, CCIC_NOTIFY_ID_USB_DP, + usbpd_data->dp_is_connect /*attach*/, usbpd_data->dp_hs_connect, 0); + } + + dev_info(&i2c->dev, "%s SVID_0 : 0x%X, SVID_1 : 0x%X\n", __func__, usbpd_data->SVID_0, usbpd_data->SVID_1); + return 0; +} + +static int process_discover_modes(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_RX_MODE; + uint8_t ReadMSG[32] = {0,}; + uint8_t W_DATA[3] = {0,}; + int ret = 0; + DIS_MODE_DP_CAPA_Type *pDP_DIS_MODE = (DIS_MODE_DP_CAPA_Type *)&ReadMSG[0]; + U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type*)&ReadMSG[4]; + + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c error.\n", __func__); + return ret; + } + + dev_info(&i2c->dev, "%s : vendor_id = 0x%04x , svid_1 = 0x%04x\n", __func__,DATA_MSG_VDM->BITS.Standard_Vendor_ID,usbpd_data->SVID_1); + if(DATA_MSG_VDM->BITS.Standard_Vendor_ID == TypeC_DP_SUPPORT && usbpd_data->SVID_0 == TypeC_DP_SUPPORT) { + // pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS. + dev_info(&i2c->dev,"pDP_DIS_MODE->MSG_HEADER.DATA = 0x%08X\n\r",pDP_DIS_MODE->MSG_HEADER.DATA); + dev_info(&i2c->dev,"pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA = 0x%08X\n\r",pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA); + dev_info(&i2c->dev,"pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA = 0x%08X\n\r",pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA); + + if(pDP_DIS_MODE->MSG_HEADER.BITS.Number_of_obj > 1) { + if( ( (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_UFP_D_Capable) + && (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_Receptacle) ) + || ( (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_DFP_D_Capable) + && (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_PLUG) ) ) { + + usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.UFP_D_Pin_Assignments; + dev_info(&i2c->dev, "%s support UFP_D 0x%08x\n", + __func__, usbpd_data->pin_assignment); + } else if( ( (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_DFP_D_Capable) + && (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_Receptacle) ) + || ( (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_UFP_D_Capable) + && (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_PLUG) ) ) { + + usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.DFP_D_Pin_Assignments; + dev_info(&i2c->dev, "%s support DFP_D 0x%08x\n", + __func__, usbpd_data->pin_assignment); + } + else if( pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_DFP_D_and_UFP_D_Capable ) { + if(pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_PLUG){ + usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.DFP_D_Pin_Assignments; + dev_info(&i2c->dev, "%s support DFP_D 0x%08x\n",__func__, usbpd_data->pin_assignment); + } + else{ + usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.UFP_D_Pin_Assignments; + dev_info(&i2c->dev, "%s support UFP_D 0x%08x\n",__func__, usbpd_data->pin_assignment); + } + } + else{ + usbpd_data->pin_assignment = DP_PIN_ASSIGNMENT_NODE; + dev_info(&i2c->dev, "%s there is not valid object information!!! \n",__func__); + } + } + } + + if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == TypeC_Dex_SUPPORT && usbpd_data->SVID_1 == TypeC_SAMSUNG_SVID) { + dev_info(&i2c->dev, "%s : dex mode discover_mode ack status!\n", __func__); + // pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS. + dev_info(&i2c->dev,"pDP_DIS_MODE->MSG_HEADER.DATA = 0x%08X\n\r",pDP_DIS_MODE->MSG_HEADER.DATA); + dev_info(&i2c->dev,"pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA = 0x%08X\n\r",pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA); + dev_info(&i2c->dev,"pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA = 0x%08X\n\r",pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA); + + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; /* Mode Interface */ + W_DATA[1] = PD_NEXT_STATE; /* Select mode as pd next state*/ + W_DATA[2] = 89; /* PD next state*/ + ret = s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3 ); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c write error.\n", + __func__); + return ret; + } + } + + dev_info(&i2c->dev, "%s\n", __func__); + set_clear_discover_mode(); + return 0; +} + +static int process_enter_mode(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + uint8_t ReadMSG[32] = {0,}; + int ret = 0; + + U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type*)&ReadMSG[4]; + + dev_info(&i2c->dev, "%s\n", __func__); + + REG_ADD = REG_RX_ENTER_MODE; + /* Message Type Definition */ + DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&ReadMSG[4]; + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c read error.\n", __func__); + return ret; + } + + if (DATA_MSG_VDM->BITS.VDM_command_type == 1) { + dev_info(&i2c->dev, "%s : EnterMode ACK.\n", __func__); + if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == TypeC_Dex_SUPPORT) { + usbpd_data->is_samsung_accessory_enter_mode = 1; + dev_info(&i2c->dev, "%s : dex mode enter_mode ack status!\n", __func__); + } + } + else + dev_info(&i2c->dev, "%s : EnterMode NAK.\n", __func__); + + return 0; +} + +static int process_attention(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_RX_DIS_ATTENTION; + uint8_t ReadMSG[32] = {0,}; + uint8_t W_DATA[3] = {0,}; + int ret = 0; + int i; + CC_NOTI_TYPEDEF ccic_alt_noti; + VDO_MESSAGE_Type *VDO_MSG; + DIS_ATTENTION_MESSAGE_DP_STATUS_Type *DP_ATTENTION; + u8 multi_func_preference = 0; + + pr_info("%s \n",__func__); + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c error.\n", __func__); + return ret; + } + + if( usbpd_data->SVID_0 == TypeC_DP_SUPPORT ) { + DP_ATTENTION = (DIS_ATTENTION_MESSAGE_DP_STATUS_Type *)&ReadMSG[0]; + + dev_info(&i2c->dev, "%s DP_ATTENTION = 0x%08X\n", __func__, + DP_ATTENTION->DATA_MSG_DP_STATUS.DATA); + + if(usbpd_data->is_sent_pin_configuration == 0) { + /* 1. Pin Assignment */ + REG_ADD = 0x10; + W_DATA[0] = MODE_INTERFACE; /* Mode Interface */ + W_DATA[1] = DP_ALT_MODE_REQ; /* DP Alternate Mode Request */ + W_DATA[2] = 0; /* DP Pin Assign select */ + + multi_func_preference = + DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.Multi_Function_Preference; + if(multi_func_preference == 1) { + if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_D) { + W_DATA[2] =DP_PIN_ASSIGNMENT_D; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_D; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_B) { + W_DATA[2] =DP_PIN_ASSIGNMENT_B; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_B; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_F) { + W_DATA[2] =DP_PIN_ASSIGNMENT_F; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_F; + } else { + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_UNKNOWN; + pr_info("wrong pin assignment value\n"); + } + } else { + dwc3_restart_usb_host_mode_hs(); + if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_C) { + W_DATA[2] =DP_PIN_ASSIGNMENT_C; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_C; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_E) { + W_DATA[2] =DP_PIN_ASSIGNMENT_E; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_E; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_A) { + W_DATA[2] =DP_PIN_ASSIGNMENT_A; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_A; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_D) { + W_DATA[2] =DP_PIN_ASSIGNMENT_D; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_D; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_B) { + W_DATA[2] =DP_PIN_ASSIGNMENT_B; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_B; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_F) { + W_DATA[2] =DP_PIN_ASSIGNMENT_F; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_F; + } else { + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_UNKNOWN; + pr_info("wrong pin assignment value\n"); + } + } + + usbpd_data->selected_pin = ccic_alt_noti.sub1; + dev_info(&i2c->dev, "%s %s\n", __func__, + DP_Pin_Assignment_Print[ccic_alt_noti.sub1]); + + sbu_check(usbpd_data); + + ret = s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3 ); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c write error.\n", + __func__); + return ret; + } + usbpd_data->is_sent_pin_configuration = 1; + } else { + dev_info(&i2c->dev, "%s : pin configuration is already sent as %s!\n", __func__, + DP_Pin_Assignment_Print[usbpd_data->selected_pin]); + } + + ccic_alt_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_alt_noti.dest = CCIC_NOTIFY_DEV_DP; + ccic_alt_noti.id = CCIC_NOTIFY_ID_DP_HPD; + ccic_alt_noti.sub1 = CCIC_NOTIFY_LOW; /*HPD_STATE*/ + ccic_alt_noti.sub2 = 0; /*HPD IRQ*/ + ccic_alt_noti.sub3 = 0; + + if ( DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.HPD_State == 1) { + ccic_alt_noti.sub1 = CCIC_NOTIFY_HIGH; + } else if(DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.HPD_State == 0){ + ccic_alt_noti.sub1 = CCIC_NOTIFY_LOW; + } + if(DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.HPD_Interrupt == 1) { + ccic_alt_noti.sub2 = CCIC_NOTIFY_IRQ; + } + ccic_event_work(usbpd_data,ccic_alt_noti.dest,ccic_alt_noti.id, + ccic_alt_noti.sub1,ccic_alt_noti.sub2,ccic_alt_noti.sub3); + } else { + VDO_MSG = (VDO_MESSAGE_Type *)&ReadMSG[8]; + + for(i=0;i<6;i++) + dev_info(&i2c->dev, "%s : VDO_%d : %d\n", __func__, + i+1, VDO_MSG->VDO[i]); + } + return 0; + +} + +static int process_dp_status_update(void *data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_RX_DIS_DP_STATUS_UPDATE; + uint8_t ReadMSG[32] = {0,}; + uint8_t W_DATA[3] = {0,}; + int ret = 0; + int i; + u8 multi_func_preference = 0; + + CC_NOTI_TYPEDEF ccic_alt_noti; + VDO_MESSAGE_Type *VDO_MSG; + DP_STATUS_UPDATE_Type *DP_STATUS; + + pr_info("%s \n",__func__); + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c error.\n", __func__); + return ret; + } + + if( usbpd_data->SVID_0 == TypeC_DP_SUPPORT ) { + DP_STATUS = (DP_STATUS_UPDATE_Type *)&ReadMSG[0]; + + dev_info(&i2c->dev, "%s DP_STATUS_UPDATE = 0x%08X\n", __func__, + DP_STATUS->DATA_DP_STATUS_UPDATE.DATA); + + if(DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.Port_Connected == 0x00) { + dev_info(&i2c->dev, "%s : port disconnected!\n", __func__); + } + else { + if(usbpd_data->is_sent_pin_configuration == 0) { + /* 1. Pin Assignment */ + REG_ADD = 0x10; + W_DATA[0] = MODE_INTERFACE; /* Mode Interface */ + W_DATA[1] = DP_ALT_MODE_REQ; /* DP Alternate Mode Request */ + W_DATA[2] = 0; /* DP Pin Assign select */ + + multi_func_preference = + DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.Multi_Function_Preference; + if(multi_func_preference == 1) { + if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_D) { + W_DATA[2] =DP_PIN_ASSIGNMENT_D; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_D; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_B) { + W_DATA[2] =DP_PIN_ASSIGNMENT_B; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_B; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_F) { + W_DATA[2] =DP_PIN_ASSIGNMENT_F; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_F; + } else { + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_UNKNOWN; + pr_info("wrong pin assignment value\n"); + } + } else { + dwc3_restart_usb_host_mode_hs(); + if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_C) { + W_DATA[2] =DP_PIN_ASSIGNMENT_C; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_C; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_E) { + W_DATA[2] =DP_PIN_ASSIGNMENT_E; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_E; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_A) { + W_DATA[2] =DP_PIN_ASSIGNMENT_A; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_A; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_D) { + W_DATA[2] =DP_PIN_ASSIGNMENT_D; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_D; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_B) { + W_DATA[2] =DP_PIN_ASSIGNMENT_B; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_B; + } else if(usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_F) { + W_DATA[2] =DP_PIN_ASSIGNMENT_F; + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_F; + } else { + ccic_alt_noti.sub1 = CCIC_NOTIFY_DP_PIN_UNKNOWN; + pr_info("wrong pin assignment value\n"); + } + } + + usbpd_data->selected_pin = ccic_alt_noti.sub1; + dev_info(&i2c->dev, "%s %s\n", __func__, + DP_Pin_Assignment_Print[ccic_alt_noti.sub1]); + + sbu_check(usbpd_data); + + ret = s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3 ); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c write error.\n", + __func__); + return ret; + } + usbpd_data->is_sent_pin_configuration = 1; + } else { + dev_info(&i2c->dev, "%s : pin configuration is already sent as %s!\n", __func__, + DP_Pin_Assignment_Print[usbpd_data->selected_pin]); + } + } + + ccic_alt_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_alt_noti.dest = CCIC_NOTIFY_DEV_DP; + ccic_alt_noti.id = CCIC_NOTIFY_ID_DP_HPD; + ccic_alt_noti.sub1 = CCIC_NOTIFY_LOW; /*HPD_STATE*/ + ccic_alt_noti.sub2 = 0; /*HPD IRQ*/ + ccic_alt_noti.sub3 = 0; + + if ( DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.HPD_State == 1) { + ccic_alt_noti.sub1 = CCIC_NOTIFY_HIGH; + } else if(DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.HPD_State == 0){ + ccic_alt_noti.sub1 = CCIC_NOTIFY_LOW; + } + if(DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.HPD_Interrupt == 1) { + ccic_alt_noti.sub2 = CCIC_NOTIFY_IRQ; + } + ccic_event_work(usbpd_data,ccic_alt_noti.dest,ccic_alt_noti.id, + ccic_alt_noti.sub1,ccic_alt_noti.sub2,ccic_alt_noti.sub3); + }else { + VDO_MSG = (VDO_MESSAGE_Type *)&ReadMSG[8]; + + for(i=0;i<6;i++) + dev_info(&i2c->dev, "%s : VDO_%d : %d\n", __func__, + i+1, VDO_MSG->VDO[i]); + } + return 0; +} + +static int process_dp_configure(void *data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_RX_DIS_DP_CONFIGURE; + uint8_t ReadMSG[32] = {0,}; + uint8_t W_DATA[3] = {0,}; + int ret = 0; + U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type*)&ReadMSG[4]; + CC_NOTI_TYPEDEF ccic_alt_noti; + + dev_info(&i2c->dev, "%s\n", __func__); + + ret = s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c error.\n", __func__); + return ret; + } + + dev_info(&i2c->dev, "%s : vendor_id = 0x%04x , svid_1 = 0x%04x\n", __func__,DATA_MSG_VDM->BITS.Standard_Vendor_ID,usbpd_data->SVID_1); + if( usbpd_data->SVID_0 == TypeC_DP_SUPPORT ) { + ccic_alt_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_alt_noti.dest = CCIC_NOTIFY_DEV_DP; + ccic_alt_noti.id = CCIC_NOTIFY_ID_DP_LINK_CONF; + ccic_alt_noti.sub1 = usbpd_data->selected_pin; + ccic_alt_noti.sub2 = 0; /*HPD IRQ*/ + ccic_alt_noti.sub3 = 0; + ccic_event_work(usbpd_data,ccic_alt_noti.dest,ccic_alt_noti.id, + ccic_alt_noti.sub1,ccic_alt_noti.sub2,ccic_alt_noti.sub3); + } + + if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == TypeC_DP_SUPPORT && usbpd_data->SVID_1 == TypeC_SAMSUNG_SVID) { + /* write s2mm005 with TypeC_Dex_SUPPORT SVID */ + /* It will start discover mode with that svid */ + dev_info(&i2c->dev, "%s : svid1 is dex station\n", __func__); + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; /* Mode Interface */ + W_DATA[1] = SVID_SELECT; /* SVID select*/ + W_DATA[2] = 1; /* SVID select with Samsung vendor ID*/ + ret = s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3 ); + if (ret < 0) { + dev_err(&i2c->dev, "%s has i2c write error.\n", + __func__); + return ret; + } + } + + return 0; +} + +static void process_alternate_mode(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint32_t mode = usbpd_data->alternate_state; + int ret = 0; + struct otg_notify *o_notify = get_otg_notify(); + if (mode) { + dev_info(&i2c->dev, "%s, mode : 0x%x\n", __func__, mode); + if (o_notify != NULL && is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST)) { + dev_info(&i2c->dev, "%s, host is blocked, skip all the alternate mode.\n", __func__); + goto process_error; + } + if (mode & VDM_DISCOVER_ID) + ret = process_discover_identity(usbpd_data); + if(ret) + goto process_error; + if (mode & VDM_DISCOVER_SVIDS) + ret = process_discover_svids(usbpd_data); + if(ret) + goto process_error; + if (mode & VDM_DISCOVER_MODES) + ret = process_discover_modes(usbpd_data); + if(ret) + goto process_error; + if (mode & VDM_ENTER_MODE) + ret = process_enter_mode(usbpd_data); + if(ret) + goto process_error; + if (mode & VDM_DP_STATUS_UPDATE) + ret = process_dp_status_update(usbpd_data); + if(ret) + goto process_error; + if (mode & VDM_DP_CONFIGURE) + ret = process_dp_configure(usbpd_data); + if(ret) + goto process_error; + if (mode & VDM_ATTENTION) + ret = process_attention(usbpd_data); +process_error: + usbpd_data->alternate_state = 0; + } +} + +void send_alternate_message(void * data, int cmd) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_VDM_MSG_REQ; + u8 mode = (u8)cmd; + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[cmd][0]); + s2mm005_write_byte(i2c, REG_ADD, &mode, 1); +} + +void receive_alternate_message(void * data, VDM_MSG_IRQ_STATUS_Type *VDM_MSG_IRQ_State) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_ID) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[1][0]); + usbpd_data->alternate_state |= VDM_DISCOVER_ID; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_SVIDs) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[2][0]); + usbpd_data->alternate_state |= VDM_DISCOVER_SVIDS; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_MODEs) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[3][0]); + usbpd_data->alternate_state |= VDM_DISCOVER_MODES; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Enter_Mode) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[4][0]); + usbpd_data->alternate_state |= VDM_ENTER_MODE; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Exit_Mode) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[5][0]); + usbpd_data->alternate_state |= VDM_EXIT_MODE; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Attention) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[6][0]); + usbpd_data->alternate_state |= VDM_ATTENTION; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_DP_Status_Update) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[7][0]); + usbpd_data->alternate_state |= VDM_DP_STATUS_UPDATE; + } + if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_DP_Configure) { + dev_info(&i2c->dev, "%s : %s\n",__func__, &VDM_MSG_IRQ_State_Print[8][0]); + usbpd_data->alternate_state |= VDM_DP_CONFIGURE; + } + + process_alternate_mode(usbpd_data); +} + +void set_endian(char *src, char *dest, int size) +{ + int i, j; + int loop; + int dest_pos; + int src_pos; + + loop = size / SAMSUNGUVDM_ALIGN; + loop += (((size % SAMSUNGUVDM_ALIGN) > 0) ? 1:0); + + for (i = 0 ; i < loop ; i++) + for (j = 0 ; j < SAMSUNGUVDM_ALIGN ; j++) { + src_pos = SAMSUNGUVDM_ALIGN * i + j; + dest_pos = SAMSUNGUVDM_ALIGN * i + SAMSUNGUVDM_ALIGN - j - 1; + dest[dest_pos] = src[src_pos]; + } +} + +int get_checksum(char *data, int start_addr, int size) +{ + int checksum = 0; + int i; + + for (i = 0; i < size; i++) { + checksum += data[start_addr+i]; + printk("%x ", (uint32_t)data[start_addr+i]); + } + printk(" %s \n", __func__); + return checksum; +} + +int set_uvdmset_count(int size) +{ + int ret = 0; + + if (size <= SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) + ret = 1; + else { + ret = ((size-SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) / SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET); + if (((size-SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) % SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET) == 0) + ret += 1; + else + ret += 2; + } + return ret; +} + +void set_msghedader(void *data, int msg_type, int obj_num) +{ + MSG_HEADER_Type *MSG_HDR; + uint8_t *SendMSG = (uint8_t *)data; + + MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + MSG_HDR->Message_Type = msg_type; + MSG_HDR->Number_of_obj = obj_num; + return; +} +int get_writesize(void *data) +{ + MSG_HEADER_Type *MSG_HDR; + uint8_t *SendMSG = (uint8_t *)data; + + MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + return ((MSG_HDR->Number_of_obj)*4+4); +} + +void set_uvdmheader(void *data, int vendor_id, int vdm_type) +{ + U_UNSTRUCTURED_VDM_HEADER_Type *UVDM_HEADER; + U_DATA_MSG_VDM_HEADER_Type *VDM_HEADER; + uint8_t *SendMSG = (uint8_t *)data; + + UVDM_HEADER = (U_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4]; + UVDM_HEADER->BITS.USB_Vendor_ID = vendor_id; + UVDM_HEADER->BITS.VDM_TYPE = vdm_type; + UVDM_HEADER->BITS.VENDOR_DEFINED_MESSAGE = SEC_UVDM_UNSTRUCTURED_VDM; + VDM_HEADER = (U_DATA_MSG_VDM_HEADER_Type *)&SendMSG[4]; + VDM_HEADER->BITS.VDM_command = 4; //s2mm005 only + return; +} + +void set_sec_uvdmheader(void *data, int pid, bool data_type, int cmd_type, + bool dir, int total_uvdmset_num, uint8_t received_data) +{ + U_SEC_UVDM_HEADER *SEC_VDM_HEADER; + uint8_t *SendMSG = (uint8_t *)data; + + SEC_VDM_HEADER = (U_SEC_UVDM_HEADER *)&SendMSG[8]; + SEC_VDM_HEADER->BITS.pid = pid; + SEC_VDM_HEADER->BITS.data_type = data_type; + SEC_VDM_HEADER->BITS.command_type = cmd_type; + SEC_VDM_HEADER->BITS.direction = dir; + if (dir == DIR_OUT) + SEC_VDM_HEADER->BITS.total_number_of_uvdm_set = total_uvdmset_num; + if (data_type == TYPE_SHORT) + SEC_VDM_HEADER->BITS.data = received_data; + + pr_info("%s pid = %d data_type=%d ,cmd_type =%d,direction= %d, total_num_of_uvdm_set = %d\n", + __func__, SEC_VDM_HEADER->BITS.pid, + SEC_VDM_HEADER->BITS.data_type, + SEC_VDM_HEADER->BITS.command_type, + SEC_VDM_HEADER->BITS.direction, + SEC_VDM_HEADER->BITS.total_number_of_uvdm_set); + return; +} + +int get_datasize_of_currentset (int first_set, int remained_data_size) +{ + int ret = 0; + + if (first_set) + ret = (remained_data_size <= SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) ? \ + remained_data_size : SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET; + else + ret = (remained_data_size <= SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET) ? \ + remained_data_size : SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET; + + return ret; +} + +void set_sec_uvdm_txdataheader (void *data, int first_set, int cur_uvdmset, + int total_data_size, int remained_data_size) +{ + U_SEC_TX_DATA_HEADER *SEC_TX_DATA_HEADER; + uint8_t *SendMSG = (uint8_t *)data; + + if (first_set) + SEC_TX_DATA_HEADER = (U_SEC_TX_DATA_HEADER *)&SendMSG[12]; + else + SEC_TX_DATA_HEADER = (U_SEC_TX_DATA_HEADER *)&SendMSG[8]; + + SEC_TX_DATA_HEADER->BITS.data_size_of_current_set =\ + get_datasize_of_currentset(first_set, remained_data_size); + SEC_TX_DATA_HEADER->BITS.total_data_size = total_data_size; + SEC_TX_DATA_HEADER->BITS.order_of_current_uvdm_set = cur_uvdmset; + + return; +} + +void set_sec_uvdm_txdata_tailer(void *data) +{ + U_SEC_TX_DATA_TAILER *SEC_TX_DATA_TAILER; + uint8_t *SendMSG = (uint8_t *)data; + + SEC_TX_DATA_TAILER = (U_SEC_TX_DATA_TAILER *)&SendMSG[28]; + SEC_TX_DATA_TAILER->BITS.checksum = get_checksum(SendMSG, S2MM005_SECUVDM_START_ADDR,\ + SAMSUNGUVDM_CHECKSUM_DATA_COUNT); + return; +} + +void set_sec_uvdm_rxdata_header(void *data, int cur_uvdmset_num, int cur_uvdmset_data, int ack) +{ + U_SEC_RX_DATA_HEADER *SEC_UVDM_RX_HEADER; + uint8_t *SendMSG = (uint8_t *)data; + + SEC_UVDM_RX_HEADER = (U_SEC_RX_DATA_HEADER *)&SendMSG[8]; + SEC_UVDM_RX_HEADER->BITS.order_of_current_uvdm_set = cur_uvdmset_num; + SEC_UVDM_RX_HEADER->BITS.received_data_size_of_current_set = cur_uvdmset_data; + SEC_UVDM_RX_HEADER->BITS.result_value = ack; + +} + +ssize_t send_samsung_unstructured_long_uvdm_message(void *data, void *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[2]; + uint8_t *SEC_DATA; + + /* Valuable to calc the uvdm set and each uvdm set's data size*/ + int need_uvdmset_count = 0; + int cur_uvdmset_data = 0; + int cur_uvdmset_num = 0; + int accumulated_data_size = 0; + int remained_data_size = 0; + uint8_t received_data[MAX_INPUT_DATA] = {0,}; + int time_left; + int i; + int received_data_index; + int write_size = 0; + + /* 1. Calc the receivced data size and determin the uvdm set count and last data of uvdm set. */ + set_endian(buf, received_data, size); + + need_uvdmset_count = set_uvdmset_count(size); + dev_info(&i2c->dev, "%s need_uvdmset_count = %d \n", __func__, need_uvdmset_count); + + usbpd_data->is_in_first_sec_uvdm_req = true; + usbpd_data->is_in_sec_uvdm_out = DIR_OUT; + cur_uvdmset_num = 1; + accumulated_data_size = 0; + remained_data_size = size; + received_data_index = 0; + + /* 2. Common : Fill the MSGHeader */ + set_msghedader(SendMSG, 15, 7); + /* 3. Common : Fill the UVDMHeader*/ + set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0); + /* 4. Common : Fill the First SEC_VDMHeader*/ + if (usbpd_data->is_in_first_sec_uvdm_req) + set_sec_uvdmheader(SendMSG, usbpd_data->Product_ID, TYPE_LONG,\ + SEC_UVDM_ININIATOR, DIR_OUT, need_uvdmset_count, 0); + + while (cur_uvdmset_num <= need_uvdmset_count) { + cur_uvdmset_data = 0; + time_left = 0; + + set_sec_uvdm_txdataheader(SendMSG, usbpd_data->is_in_first_sec_uvdm_req,\ + cur_uvdmset_num, size, remained_data_size); + + cur_uvdmset_data = get_datasize_of_currentset(usbpd_data->is_in_first_sec_uvdm_req, remained_data_size); + + dev_info(&i2c->dev, "%s data_size_of_current_set = %d ,total_data_size = %ld,\ + order_of_current_uvdm_set = %d\n", __func__, cur_uvdmset_data, size, cur_uvdmset_num); + /* 6. Common : Fill the DATA */ + if (usbpd_data->is_in_first_sec_uvdm_req) { + SEC_DATA = (uint8_t *)&SendMSG[S2MM005_SECUVDM_START_ADDR+8]; + for (i = 0; i < SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET; i++) + SEC_DATA[i] = received_data[received_data_index++]; + } else { + SEC_DATA = (uint8_t *)&SendMSG[S2MM005_SECUVDM_START_ADDR+4]; + for (i = 0; i < SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET; i++) + SEC_DATA[i] = received_data[received_data_index++]; + } + + /* 7. Common : Fill the TX_DATA_Tailer */ + set_sec_uvdm_txdata_tailer(SendMSG); + + /* 8. Send data to PDIC */ + REG_ADD = REG_SSM_MSG_SEND; + write_size = get_writesize(SendMSG); + s2mm005_write_byte(i2c, REG_ADD, SendMSG, write_size); + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + /* 9. Wait Response*/ + reinit_completion(&usbpd_data->uvdm_out_wait); + time_left = + wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_out_wait, + msecs_to_jiffies(SASMSUNGUVDM_WAIT_MS)); + if (time_left <= 0) + return -ETIME; + + accumulated_data_size += cur_uvdmset_data; + remained_data_size -= cur_uvdmset_data; + if (usbpd_data->is_in_first_sec_uvdm_req) + usbpd_data->is_in_first_sec_uvdm_req = false; + cur_uvdmset_num++; + } + + return size; +} + +int check_is_wait_ack_accessroy(int vid, int pid, int svid) +{ + int should_wait = true; + if (vid == SAMSUNG_VENDOR_ID && pid == DEXDOCK_PRODUCT_ID && svid == TypeC_DP_SUPPORT) { + pr_info("%s : no need to wait ack response!\n", __func__); + should_wait = false; + } + return should_wait; +} + +int send_samsung_unstructured_short_vdm_message(void *data, void *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[2]; + /* Message Type Definition */ + uint8_t received_data = 0; + int time_left; + + if ((buf == NULL) || size <= 0) { + dev_info(&i2c->dev, "%s given data is not valid !\n", __func__); + return -EINVAL; + } + + if (!usbpd_data->is_samsung_accessory_enter_mode) { + dev_info(&i2c->dev, "%s - samsung_accessory mode is not ready!\n", __func__); + return -ENXIO; + } + + /* 1. Calc the receivced data size and determin the uvdm set count and last data of uvdm set. */ + received_data = *(char *)buf; + /* 2. Common : Fill the MSGHeader */ + set_msghedader(SendMSG, 15, 2); + /* 3. Common : Fill the UVDMHeader*/ + set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0); + /* 4. Common : Fill the First SEC_VDMHeader*/ + set_sec_uvdmheader(SendMSG, usbpd_data->Product_ID, TYPE_SHORT,\ + SEC_UVDM_ININIATOR, DIR_OUT, 1, received_data); + usbpd_data->is_in_first_sec_uvdm_req = true; + + dev_info(&i2c->dev, "%s - process short data!\n", __func__); + s2mm005_write_byte(i2c, REG_ADD, SendMSG, 32); + + /* send uVDM message */ + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + if (check_is_wait_ack_accessroy(usbpd_data->Vendor_ID, usbpd_data->Product_ID, usbpd_data->SVID_0)) { + reinit_completion(&usbpd_data->uvdm_out_wait); + /* Wait Response*/ + time_left = + wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_out_wait, + msecs_to_jiffies(SASMSUNGUVDM_WAIT_MS)); + if (time_left <= 0) { + usbpd_data->is_in_first_sec_uvdm_req = false; + return -ETIME; + } + } + + dev_info(&i2c->dev, "%s - exit : short data transfer complete!\n", __func__); + usbpd_data->is_in_first_sec_uvdm_req = false; + return size; +} + +int send_samsung_unstructured_vdm_message(void *data, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[2]; + // Message Type Definition + MSG_HEADER_Type *MSG_HDR; + U_UNSTRUCTURED_VDM_HEADER_Type *UVDM_HEADER; + U_SEC_UNSTRUCTURED_VDM_HEADER_Type *SEND_SEC_VDM_HEADER; + U_SEC_UNSTRUCTURED_VDM_HEADER_Type *RECEIVED_VDM_HEADER; +#if 0 + char *received_data; + int sret = 0; +#else + int received_data; +#endif +#if 0 //TEMP + + U_SEC_UNSTRUCTURED_VDM_HEADER_Type temp_test; + + dev_info(&i2c->dev, "%s - enter!\n", __func__); + + temp_test.BITS.PID = DEXDOCK_PRODUCT_ID; + temp_test.BITS.DATA_TYPE = SEC_UVDM_SHORT_DATA; + temp_test.BITS.COMMAND_TYPE = SEC_UVDM_ININIATOR; + sscanf(buf, "%d", &mode); + temp_test.BITS.DATA = mode; + + RECEIVED_VDM_HEADER = (U_SEC_UNSTRUCTURED_VDM_HEADER_Type*)&temp_test; + MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + UVDM_HEADER = (U_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4]; + SEND_SEC_VDM_HEADER = (VDO_MESSAGE_Type *)&SendMSG[8]; + + // process common data + MSG_HDR->Message_Type = 15; // send VDM message + UVDM_HEADER->BITS.USB_Vendor_ID = SAMSUNG_VENDOR_ID; // VID + UVDM_HEADER->BITS.VENDOR_DEFINED_MESSAGE = SEC_UVDM_UNSTRUCTURED_VDM; + + *SEND_SEC_VDM_HEADER = *RECEIVED_VDM_HEADER; +#else + if(buf == NULL || size < sizeof(U_SEC_UNSTRUCTURED_VDM_HEADER_Type)) + { + dev_info(&i2c->dev, "%s given data is not valid !\n", __func__); + return -EINVAL; + } + + if(!usbpd_data->is_samsung_accessory_enter_mode) { + dev_info(&i2c->dev, "%s - samsung_accessory mode is not ready!\n", __func__); + return -ENXIO; + } +#if 0 + received_data = kzalloc(size+1, GFP_KERNEL); + if (!received_data) + goto error; + + sret = sscanf(buf, "%s", received_data); + if (sret != 1) + goto error1; +#else + sscanf(buf, "%d", &received_data); +#endif + + RECEIVED_VDM_HEADER = (U_SEC_UNSTRUCTURED_VDM_HEADER_Type*)&received_data; + MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + UVDM_HEADER = (U_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4]; + SEND_SEC_VDM_HEADER = (U_SEC_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[8]; + + // process common data + MSG_HDR->Message_Type = 15; // send VDM message + UVDM_HEADER->BITS.USB_Vendor_ID = SAMSUNG_VENDOR_ID; // VID + UVDM_HEADER->BITS.VENDOR_DEFINED_MESSAGE = SEC_UVDM_UNSTRUCTURED_VDM; + + *SEND_SEC_VDM_HEADER = *RECEIVED_VDM_HEADER; +#endif + if(RECEIVED_VDM_HEADER->BITS.DATA_TYPE == SEC_UVDM_SHORT_DATA) + { + dev_info(&i2c->dev, "%s - process short data!\n", __func__); + // process short data + // phase 1. fill message header + MSG_HDR->Number_of_obj = 2; // VDM Header + 6 VDOs = MAX 7 + // phase 2. fill uvdm header (already filled) + // phase 3. fill sec uvdm header + SEND_SEC_VDM_HEADER->BITS.TOTAL_NUMBER_OF_UVDM_SET = 1; + } + else + { + dev_info(&i2c->dev, "%s - process long data!\n", __func__); + // process long data + // phase 1. fill message header + // phase 2. fill uvdm header + // phase 3. fill sec uvdm header + // phase 4.5.6.7 fill sec data header , data , sec data tail and so on. + } + + s2mm005_write_byte(i2c, REG_ADD, SendMSG, 32); + + // send uVDM message + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + if(RECEIVED_VDM_HEADER->BITS.DATA_TYPE == SEC_UVDM_SHORT_DATA) + { + dev_info(&i2c->dev, "%s - exit : short data transfer complete!\n", __func__); + } +#if 0 +error1: + kfree(received_data); +error: + return; +#endif + return 0; +} + +void send_unstructured_vdm_message(void * data, int cmd) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[2]; + uint32_t message = (uint32_t)cmd; + int i; + + // Message Type Definition + MSG_HEADER_Type *MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + U_UNSTRUCTURED_VDM_HEADER_Type *DATA_MSG_UVDM = (U_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4]; + VDO_MESSAGE_Type *VDO_MSG = (VDO_MESSAGE_Type *)&SendMSG[8]; + + // fill message + MSG_HDR->Message_Type = 15; // send VDM message + MSG_HDR->Number_of_obj = 7; // VDM Header + 6 VDOs = MAX 7 + + DATA_MSG_UVDM->BITS.USB_Vendor_ID = SAMSUNG_VENDOR_ID; // VID + + for(i=0;i<6;i++) + VDO_MSG->VDO[i] = message; // VD01~VDO6 : Max 24bytes + + s2mm005_write_byte(i2c, REG_ADD, SendMSG, 32); + + // send uVDM message + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + dev_info(&i2c->dev, "%s - message : 0x%x\n", __func__, message); +} + +void receive_unstructured_vdm_message(void * data, SSM_MSG_IRQ_STATUS_Type *SSM_MSG_IRQ_State) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_READ; + uint8_t ReadMSG[32] = {0,}; + u8 W_DATA[1]; + U_SEC_UVDM_RESPONSE_HEADER *SEC_UVDM_RESPONSE_HEADER; + U_SEC_RX_DATA_HEADER *SEC_UVDM_RX_HEADER; + + if (usbpd_data->is_in_sec_uvdm_out == DIR_OUT) { + s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 16); + /* first uvdm req for direction out */ + if (usbpd_data->is_in_first_sec_uvdm_req) { + SEC_UVDM_RESPONSE_HEADER = (U_SEC_UVDM_RESPONSE_HEADER *)&ReadMSG[8]; + if (SEC_UVDM_RESPONSE_HEADER->BITS.data_type == TYPE_LONG) { + if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_ACK) { + SEC_UVDM_RX_HEADER = (U_SEC_RX_DATA_HEADER *)&ReadMSG[12]; + if (SEC_UVDM_RX_HEADER->BITS.result_value != SEC_UVDM_RESPONDER_ACK) { + dev_err(&i2c->dev, "%s Busy or Nak received.\n", __func__); + } + } else { + dev_err(&i2c->dev, "%s Response type is wrong.\n", __func__); + } + } else { + if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_ACK) + dev_info(&i2c->dev, "%s Short packet ack is received\n", __func__); + else { + dev_err(&i2c->dev, "%s Short packet Response type is wrong.\n", __func__); + } + } + /* uvdm req for direction out */ + } else { + SEC_UVDM_RX_HEADER = (U_SEC_RX_DATA_HEADER *)&ReadMSG[8]; + if (SEC_UVDM_RX_HEADER->BITS.result_value != SEC_UVDM_RESPONDER_ACK) { + dev_err(&i2c->dev, "%s Busy or Nak received.\n", __func__); + } + } + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INT_CLEAR; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 1); + complete(&usbpd_data->uvdm_out_wait); + /* In uvdm req */ + } else { + s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + /* first uvdm req for direction in */ + if (usbpd_data->is_in_first_sec_uvdm_req) { + SEC_UVDM_RESPONSE_HEADER = (U_SEC_UVDM_RESPONSE_HEADER *)&ReadMSG[8]; + dev_info(&i2c->dev, "[jj time]%s : data_type = %d , command_type = %d, direction=%d \n", __func__, + SEC_UVDM_RESPONSE_HEADER->BITS.data_type, + SEC_UVDM_RESPONSE_HEADER->BITS.command_type, + SEC_UVDM_RESPONSE_HEADER->BITS.direction); + /* for long data */ + if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type != SEC_UVDM_RESPONDER_ACK) { + dev_info(&i2c->dev, "%s :received nak or busy in response \n", __func__); + return; + } + } + complete(&usbpd_data->uvdm_longpacket_in_wait); + } +} + +int samsung_uvdm_ready(void) +{ + int uvdm_ready = false; + struct s2mm005_data *usbpd_data; + + usbpd_data = dev_get_drvdata(ccic_device); + if (usbpd_data->is_samsung_accessory_enter_mode) + uvdm_ready = true; + + pr_info("%s : uvdm ready=%d!\n", __func__, uvdm_ready); + return uvdm_ready; +} + +void samsung_uvdm_close(void) +{ + struct s2mm005_data *usbpd_data; + pr_info("%s + samsung_uvdm_close success\n", __func__); + usbpd_data = dev_get_drvdata(ccic_device); + complete(&usbpd_data->uvdm_out_wait); + complete(&usbpd_data->uvdm_longpacket_in_wait); + pr_info("%s - samsung_uvdm_close success\n", __func__); +} + +ssize_t samsung_uvdm_out_request_message(void *data, size_t size) +{ + struct s2mm005_data *usbpd_data; + struct i2c_client *i2c; + ssize_t ret; + if (!ccic_device) + return -ENXIO; + + usbpd_data = dev_get_drvdata(ccic_device); + if (!usbpd_data) + return -ENXIO; + + i2c = usbpd_data->i2c; + if (i2c == NULL) { + dev_err(&i2c->dev, "%s usbpd_data->i2c is not valid!\n", __func__); + return -EINVAL; + } + + if (data == NULL) { + dev_err(&i2c->dev, "%s given data is not valid !\n", __func__); + return -EINVAL; + } + + if (size >= SAMSUNGUVDM_MAX_LONGPACKET_SIZE) { + dev_err(&i2c->dev, "%s : size %ld is too big to send data\n", __func__, size); + ret = -EFBIG; + } else if (size <= SAMSUNGUVDM_MAX_SHORTPACKET_SIZE) + ret = send_samsung_unstructured_short_vdm_message(usbpd_data, data, size); + else + ret = send_samsung_unstructured_long_uvdm_message(usbpd_data, data, size); + + return ret; +} + +int samsung_uvdm_in_request_message(void *data) +{ + struct s2mm005_data *usbpd_data; + struct i2c_client *i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + uint8_t ReadMSG[32] = {0,}; + u8 W_DATA[2]; + uint8_t IN_DATA[MAX_INPUT_DATA] = {0, }; + + /* Send Request */ + /* 1 Message Type Definition */ + U_SEC_UVDM_RESPONSE_HEADER *SEC_RES_HEADER; + U_SEC_TX_DATA_HEADER *SEC_UVDM_TX_HEADER; + U_SEC_TX_DATA_TAILER *SEC_UVDM_TX_TAILER; + + int cur_uvdmset_data = 0; + int cur_uvdmset_num = 0; + int total_uvdmset_num = 0; + int received_data_size = 0; + int total_received_data_size = 0; + int ack = 0; + int size = 0; + int time_left = 0; + int i; + + int write_size = 0; + int cal_checksum = 0; + + if (!ccic_device) + return -ENXIO; + + usbpd_data = dev_get_drvdata(ccic_device); + if (!usbpd_data) + return -ENXIO; + + i2c = usbpd_data->i2c; + if (i2c == NULL) { + dev_err(&i2c->dev, "%s usbpd_data->i2c is not valid!\n", __func__); + return -EINVAL; + } + + dev_info(&i2c->dev, "%s\n", __func__); + usbpd_data->is_in_sec_uvdm_out = DIR_IN; + usbpd_data->is_in_first_sec_uvdm_req = true; + + /* 2. Common : Fill the MSGHeader */ + set_msghedader(SendMSG, 15, 2); + /* 3. Common : Fill the UVDMHeader*/ + set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0); + + /* 4. Common : Fill the First SEC_VDMHeader*/ + if (usbpd_data->is_in_first_sec_uvdm_req) + set_sec_uvdmheader(SendMSG, usbpd_data->Product_ID, TYPE_LONG,\ + SEC_UVDM_ININIATOR, DIR_IN, 0, 0); + + /* 5. Send data to PDIC */ + write_size = get_writesize(SendMSG); + s2mm005_write_byte(usbpd_data->i2c, REG_ADD, SendMSG, write_size); + + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + cur_uvdmset_num = 0; + total_uvdmset_num = 1; + + do { + reinit_completion(&usbpd_data->uvdm_longpacket_in_wait); + time_left = + wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_longpacket_in_wait, + msecs_to_jiffies(SASMSUNGUVDM_WAIT_MS)); + if (time_left <= 0) { + dev_err(&i2c->dev, "%s timeout\n", __func__); + return -ETIME; + } + + /* read data */ + REG_ADD = REG_SSM_MSG_READ; + s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + + if (usbpd_data->is_in_first_sec_uvdm_req) { + SEC_RES_HEADER = (U_SEC_UVDM_RESPONSE_HEADER *)&ReadMSG[8]; + SEC_UVDM_TX_HEADER = (U_SEC_TX_DATA_HEADER *)&ReadMSG[12]; + + if (SEC_RES_HEADER->BITS.data_type == TYPE_SHORT) { + IN_DATA[received_data_size++] = SEC_RES_HEADER->BITS.data; + return received_data_size; + } else { + /* 1. check the data size received */ + size = SEC_UVDM_TX_HEADER->BITS.total_data_size; + cur_uvdmset_data = SEC_UVDM_TX_HEADER->BITS.data_size_of_current_set; + cur_uvdmset_num = SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set; + total_uvdmset_num = + SEC_RES_HEADER->BITS.total_number_of_uvdm_set; + + usbpd_data->is_in_first_sec_uvdm_req = false; + /* 2. copy data to buffer */ + for (i = 0; i < SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET; i++) + IN_DATA[received_data_size++] = ReadMSG[16+i]; + + total_received_data_size += cur_uvdmset_data; + usbpd_data->is_in_first_sec_uvdm_req = false; + } + } else { + SEC_UVDM_TX_HEADER = (U_SEC_TX_DATA_HEADER *)&ReadMSG[8]; + cur_uvdmset_data = SEC_UVDM_TX_HEADER->BITS.data_size_of_current_set; + cur_uvdmset_num = SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set; + /* 2. copy data to buffer */ + for (i = 0 ; i < SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET ; i++) + IN_DATA[received_data_size++] = ReadMSG[12+i]; + total_received_data_size += cur_uvdmset_data; + } + /* 3. Check Checksum */ + SEC_UVDM_TX_TAILER = (U_SEC_TX_DATA_TAILER *)&ReadMSG[28]; + cal_checksum = get_checksum(ReadMSG, 8, SAMSUNGUVDM_CHECKSUM_DATA_COUNT); + ack = (cal_checksum == SEC_UVDM_TX_TAILER->BITS.checksum) ? + SEC_UVDM_RX_HEADER_ACK : SEC_UVDM_RX_HEADER_NAK; + + ///* 4. clear IRQ */ + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INT_CLEAR; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 1); + /* 5. Send Ack */ + /* 5-1. Common : Fill the MSGHeader */ + set_msghedader(SendMSG, 15, 2); + /* 5-2. Common : Fill the UVDMHeader*/ + set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0); + /* 5-3. Common : Fill the SEC RXHeader */ + set_sec_uvdm_rxdata_header(SendMSG, cur_uvdmset_num, cur_uvdmset_data, ack); + /* 5-4. Send data to PDIC */ + REG_ADD = REG_SSM_MSG_SEND; + write_size = get_writesize(SendMSG); + s2mm005_write_byte(usbpd_data->i2c, REG_ADD, SendMSG, write_size); + /* send uVDM message */ + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + } while (cur_uvdmset_num < total_uvdmset_num); + + set_endian(IN_DATA, data, size); + + return size; +} + +void send_dna_audio_unstructured_vdm_message(void * data, int cmd) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[2]; + uint32_t message = (uint32_t)cmd; + + // Message Type Definition + MSG_HEADER_Type *MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + U_UNSTRUCTURED_VDM_HEADER_Type *DATA_MSG_UVDM = (U_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4]; + VDO_MESSAGE_Type *VDO_MSG = (VDO_MESSAGE_Type *)&SendMSG[8]; + + // fill message + MSG_HDR->Message_Type = 15; // send VDM message + MSG_HDR->Number_of_obj = 7; // VDM Header + 6 VDOs = MAX 7 + + DATA_MSG_UVDM->BITS.USB_Vendor_ID = SAMSUNG_VENDOR_ID; // VID + + VDO_MSG->VDO[0] = message; + + s2mm005_write_byte(i2c, REG_ADD, SendMSG, 32); + + // send uVDM message + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + dev_info(&i2c->dev, "%s - message : 0x%x\n", __func__, message); +} + +void send_dex_fan_unstructured_vdm_message(void * data, int cmd) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_SSM_MSG_SEND; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[2]; + uint32_t message = (uint32_t)cmd; + + // Message Type Definition + MSG_HEADER_Type *MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + U_UNSTRUCTURED_VDM_HEADER_Type *DATA_MSG_UVDM = (U_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4]; + VDO_MESSAGE_Type *VDO_MSG = (VDO_MESSAGE_Type *)&SendMSG[8]; + + // fill message + MSG_HDR->Message_Type = 15; // send VDM message + MSG_HDR->Number_of_obj = 2; // VDM Header + 6 VDOs = MAX 7 + + DATA_MSG_UVDM->BITS.USB_Vendor_ID = SAMSUNG_VENDOR_ID; // VID + DATA_MSG_UVDM->BITS.VENDOR_DEFINED_MESSAGE = 1; + + VDO_MSG->VDO[0] = message; + + s2mm005_write_byte(i2c, REG_ADD, SendMSG, 32); + + // send uVDM message + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = SEL_SSM_MSG_REQ; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + dev_info(&i2c->dev, "%s - message : 0x%x\n", __func__, message); +} + +void send_role_swap_message(void * data, int cmd) // cmd 0 : PR_SWAP, cmd 1 : DR_SWAP +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_I2C_SLV_CMD; + u8 mode = (u8)cmd; + u8 W_DATA[2]; + + // send uVDM message + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = mode ? REQ_DR_SWAP : REQ_PR_SWAP; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); + + dev_info(&i2c->dev, "%s : sent %s message\n", __func__, mode ? "DR_SWAP" : "PR_SWAP"); +} + +void send_attention_message(void * data, int cmd) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = REG_TX_DIS_ATTENTION_RESPONSE; + uint8_t SendMSG[32] = {0,}; + u8 W_DATA[3]; + uint32_t message = (uint32_t)cmd; + int i; + + // Message Type Definition + MSG_HEADER_Type *MSG_HDR = (MSG_HEADER_Type *)&SendMSG[0]; + U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&SendMSG[4]; + VDO_MESSAGE_Type *VDO_MSG = (VDO_MESSAGE_Type *)&SendMSG[8]; + + // fill message + DATA_MSG_VDM->BITS.VDM_command = 6; // attention + DATA_MSG_VDM->BITS.VDM_Type = 1; // structured VDM + DATA_MSG_VDM->BITS.Standard_Vendor_ID = SAMSUNG_VENDOR_ID; + + MSG_HDR->Message_Type = 15; // send VDM message + MSG_HDR->Number_of_obj = 7; // VDM Header + 6 VDOs = MAX 7 + + for(i=0;i<6;i++) + VDO_MSG->VDO[i] = message; // VD01~VDO6 : Max 24bytes + + s2mm005_write_byte(i2c, REG_ADD, SendMSG, 32); + + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = PD_NEXT_STATE; + W_DATA[2] = 100; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3); + + dev_info(&i2c->dev, "%s - message : 0x%x\n", __func__, message); +} + +void do_alternate_mode_step_by_step(void * data, int cmd) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD = 0; + u8 W_DATA[3]; + + REG_ADD = REG_I2C_SLV_CMD; + W_DATA[0] = MODE_INTERFACE; + W_DATA[1] = PD_NEXT_STATE; + switch (cmd) { + case VDM_DISCOVER_ID: + W_DATA[2] = 80; + break; + case VDM_DISCOVER_SVIDS: + W_DATA[2] = 83; + break; + case VDM_DISCOVER_MODES: + W_DATA[2] = 86; + break; + case VDM_ENTER_MODE: + W_DATA[2] = 89; + break; + case VDM_EXIT_MODE: + W_DATA[2] = 92; + break; + } + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3); + + dev_info(&i2c->dev, "%s\n", __func__); +} + +#endif diff --git a/drivers/ccic/ccic_misc.c b/drivers/ccic/ccic_misc.c new file mode 100644 index 000000000000..c4d456b799f6 --- /dev/null +++ b/drivers/ccic/ccic_misc.c @@ -0,0 +1,253 @@ +/* + * driver/ccic/ccic_misc.c - S2MM005 CCIC MISC driver + * + * Copyright (C) 2017 Samsung Electronics + * Author: Wookwang Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see . + * + */ +//serial_acm.c +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccic_misc.h" + +static struct ccic_misc_dev *c_dev; + +#define MAX_BUF 255 +#define DEXDOCK_PRODUCT_ID 0xA020 +#define NODE_OF_MISC "ccic_misc" +#define CCIC_IOCTL_UVDM _IOWR('C', 0, struct uvdm_data) + +static inline int _lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void _unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +static int ccic_misc_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + pr_info("%s + open success\n", __func__); + if (!c_dev) { + pr_err("%s - error : c_dev is NULL\n", __func__); + ret = -ENODEV; + goto err; + } + + if (_lock(&c_dev->open_excl)) { + pr_err("%s - error : device busy\n", __func__); + ret = -EBUSY; + goto err1; + } + + if (!samsung_uvdm_ready()) { + // check if there is some connection + _unlock(&c_dev->open_excl); + pr_err("%s - error : uvdm is not ready\n", __func__); + ret = -EBUSY; + goto err1; + } + + pr_info("%s - open success\n", __func__); + + return 0; +err1: +err: + return ret; +} + +static int ccic_misc_close(struct inode *inode, struct file *file) +{ + if (c_dev) + _unlock(&c_dev->open_excl); + samsung_uvdm_close(); + pr_info("%s - close success\n", __func__); + return 0; +} + +static int send_uvdm_message(void *data, int size) +{ + int ret; + + pr_info("%s - size : %d\n", __func__, size); + ret = samsung_uvdm_out_request_message(data, size); + return ret; +} + +static int receive_uvdm_message(void *data, int size) +{ + int ret; + + pr_info("%s - size : %d\n", __func__, size); + ret = samsung_uvdm_in_request_message(data); + return ret; +} + +static long +ccic_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + void *buf = NULL; + + if (_lock(&c_dev->ioctl_excl)) { + pr_err("%s - error : ioctl busy - cmd : %d\n", __func__, cmd); + ret = -EBUSY; + goto err2; + } + + switch (cmd) { + case CCIC_IOCTL_UVDM: + pr_info("%s - CCIC_IOCTL_UVDM cmd\n", __func__); + if (copy_from_user(&c_dev->u_data, (void __user *) arg, + sizeof(struct uvdm_data))) { + ret = -EIO; + pr_err("%s - copy_from_user error\n", __func__); + goto err1; + } + + buf = kzalloc(MAX_BUF, GFP_KERNEL); + if (!buf) { + ret = -EINVAL; + pr_err("%s - kzalloc error\n", __func__); + goto err1; + } + + if (c_dev->u_data.size > MAX_BUF) { + ret = -ENOMEM; + pr_err("%s - user data size is %d error\n", __func__, c_dev->u_data.size); + goto err; + } + + if (c_dev->u_data.dir == DIR_OUT) { + if (copy_from_user(buf, c_dev->u_data.pData,\ + c_dev->u_data.size)) { + ret = -EIO; + pr_err("%s - copy_from_user error\n", __func__); + goto err; + } + ret = send_uvdm_message(buf, c_dev->u_data.size); + if (ret <= 0) { + pr_err("%s - send_uvdm_message error\n", __func__); + goto err; + } + } else { + ret = receive_uvdm_message(buf, c_dev->u_data.size); + if (ret <= 0) { + pr_err("%s - receive_uvdm_message error\n", __func__); + goto err; + } + if (copy_to_user((void __user *)c_dev->u_data.pData, + buf, ret)) { + ret = -EIO; + pr_err("%s - copy_to_user error\n", __func__); + goto err; + } + } + break; + + default: + pr_err("%s - unknown ioctl cmd : %d\n", __func__, cmd); + ret = -ENOIOCTLCMD; + goto err; + } +err: + kfree(buf); +err1: + _unlock(&c_dev->ioctl_excl); +err2: + return ret; +} + +#ifdef CONFIG_COMPAT +static long +ccic_misc_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + pr_info("%s - cmd : %d\n", __func__, cmd); + ret = ccic_misc_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + return ret; +} +#endif + +static const struct file_operations ccic_misc_fops = { + .owner = THIS_MODULE, + .open = ccic_misc_open, + .release = ccic_misc_close, + .llseek = no_llseek, + .unlocked_ioctl = ccic_misc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ccic_misc_compat_ioctl, +#endif +}; + +static struct miscdevice ccic_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = NODE_OF_MISC, + .fops = &ccic_misc_fops, +}; + +int ccic_misc_init(void) +{ + int ret = 0; + + ret = misc_register(&ccic_misc_device); + if (ret) { + pr_err("%s - return error : %d\n", __func__, ret); + goto err; + } + + c_dev = kzalloc(sizeof(struct ccic_misc_dev), GFP_KERNEL); + if (!c_dev) { + ret = -ENOMEM; + pr_err("%s - kzalloc failed : %d\n", __func__, ret); + goto err1; + } + atomic_set(&c_dev->open_excl, 0); + atomic_set(&c_dev->ioctl_excl, 0); + + pr_info("%s - register success\n", __func__); + return 0; +err1: + misc_deregister(&ccic_misc_device); +err: + return ret; +} +EXPORT_SYMBOL(ccic_misc_init); + +void ccic_misc_exit(void) +{ + pr_info("%s() called\n", __func__); + if (!c_dev) + return; + kfree(c_dev); + misc_deregister(&ccic_misc_device); +} +EXPORT_SYMBOL(ccic_misc_exit); diff --git a/drivers/ccic/ccic_misc.h b/drivers/ccic/ccic_misc.h new file mode 100644 index 000000000000..9a8c6bf89411 --- /dev/null +++ b/drivers/ccic/ccic_misc.h @@ -0,0 +1,105 @@ +/* + * driver/ccic/ccic_misc.h - S2MM005 CCIC MISC driver + * + * Copyright (C) 2017 Samsung Electronics + * Author: Wookwang Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see . + * + */ + +enum uvdm_data_type { + TYPE_SHORT = 0, + TYPE_LONG, +}; + +enum uvdm_direction_type { + DIR_OUT = 0, + DIR_IN, +}; +#if 0 +typedef union sec_uvdm_header { + uint32_t data; + struct { + uint8_t bdata[4]; + } BYTES; + struct { + uint32_t data:8, + total_number_of_uvdm_set:4, + direction:1, + command_type:2, + data_type:1, + pid:16; + } BITS; +} U_SEC_UVDM_HEADER; + +typedef U_SEC_UVDM_HEADER U_SEC_UVDM_RESPONSE_HEADER; + +typedef union sec_tx_data_header { + uint32_t data; + struct { + uint8_t bdata[4]; + } BYTES; + struct { + uint32_t data_size_of_current_set:8, + total_data_size:8, + reserved:12, + order_of_current_uvdm_set:4; + } BITS; +} U_SEC_TX_DATA_HEADER; + +typedef union sec_data_tx_tailer { + uint32_t data; + struct { + uint8_t bdata[4]; + } BYTES; + struct { + uint32_t checksum:16, + reserved:16; + } BITS; +} U_SEC_DATA_TX_TAILER; + +typedef union sec_data_rx_header { + uint32_t data; + struct { + uint8_t bdata[4]; + } BYTES; + struct { + uint32_t reserved:18, + result_value:2, + received_data_size_of_current_set:8, + order_of_current_uvdm_set:4; + } BITS; +} U_SEC_DATA_RX_HEADER; +#endif +struct uvdm_data { + unsigned short pid; /* Product ID */ + char type; /* uvdm_data_type */ + char dir; /* uvdm_direction_type */ + unsigned int size; /* data size */ + void __user *pData; /* data pointer */ +}; + +struct ccic_misc_dev { + struct uvdm_data u_data; + atomic_t open_excl; + atomic_t ioctl_excl; + int (*uvdm_write)(void *data, int size); + int (*uvdm_read)(void *data, int size); +}; + +extern ssize_t samsung_uvdm_out_request_message(void *data, size_t size); +extern int samsung_uvdm_in_request_message(void *data); +extern int samsung_uvdm_ready(void); +extern void samsung_uvdm_close(void); diff --git a/drivers/ccic/ccic_notifier.c b/drivers/ccic/ccic_notifier.c new file mode 100644 index 000000000000..4eddacaa30ed --- /dev/null +++ b/drivers/ccic/ccic_notifier.c @@ -0,0 +1,321 @@ +#include +#include + +#include +#include +#include +#include +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER +#include +#endif +#include + +#define DEBUG +#define SET_CCIC_NOTIFIER_BLOCK(nb, fn, dev) do { \ + (nb)->notifier_call = (fn); \ + (nb)->priority = (dev); \ + } while (0) + +#define DESTROY_CCIC_NOTIFIER_BLOCK(nb) \ + SET_CCIC_NOTIFIER_BLOCK(nb, NULL, -1) + +static struct ccic_notifier_struct ccic_notifier; + +struct device *ccic_device; +static int ccic_notifier_init_done = 0; +int ccic_notifier_init(void); + +char CCIC_NOTI_DEST_Print[CCIC_NOTI_DEST_NUM][10] = +{ + {"INITIAL"}, + {"USB"}, + {"BATTERY"}, + {"PDIC"}, + {"MUIC"}, + {"CCIC"}, + {"MANAGER"}, + {"DP"}, + {"DPUSB"}, + {"ALL"}, +}; + +char CCIC_NOTI_ID_Print[CCIC_NOTI_ID_NUM][20] = +{ + {"ID_INITIAL"}, + {"ID_ATTACH"}, + {"ID_RID"}, + {"ID_USB"}, + {"ID_POWER_STATUS"}, + {"ID_WATER"}, + {"ID_VCONN"}, + {"ID_DP_CONNECT"}, + {"ID_DP_HPD"}, + {"ID_DP_LINK_CONF"}, + {"ID_DP_USB"}, + {"ID_ROLE_SWAP"}, + {"ID_FAC"}, + {"ID_PIN_STATUS"}, +}; + +char CCIC_NOTI_RID_Print[CCIC_NOTI_RID_NUM][15] = +{ + {"RID_UNDEFINED"}, + {"RID_000K"}, + {"RID_001K"}, + {"RID_255K"}, + {"RID_301K"}, + {"RID_523K"}, + {"RID_619K"}, + {"RID_OPEN"}, +}; + +char CCIC_NOTI_USB_STATUS_Print[CCIC_NOTI_USB_STATUS_NUM][20] = +{ + {"USB_DETACH"}, + {"USB_ATTACH_DFP"}, + {"USB_ATTACH_UFP"}, + {"USB_ATTACH_DRP"}, + {"USB_ATTACH_NO_USB"}, +}; + +int ccic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier, + ccic_notifier_device_t listener) +{ + int ret = 0; + + pr_info("%s: listener=%d register\n", __func__, listener); + + /* Check if CCIC Notifier is ready. */ + if(!ccic_notifier_init_done) + ccic_notifier_init(); + + if (!ccic_device) { + pr_err("%s: Not Initialized...\n", __func__); + return -1; + } + + SET_CCIC_NOTIFIER_BLOCK(nb, notifier, listener); + ret = blocking_notifier_chain_register(&(ccic_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_register error(%d)\n", + __func__, ret); + + /* current ccic's attached_device status notify */ + nb->notifier_call(nb, 0, + &(ccic_notifier.ccic_template)); + + return ret; +} + +int ccic_notifier_unregister(struct notifier_block *nb) +{ + int ret = 0; + + pr_info("%s: listener=%d unregister\n", __func__, nb->priority); + + ret = blocking_notifier_chain_unregister(&(ccic_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_CCIC_NOTIFIER_BLOCK(nb); + + return ret; +} + +static void ccic_uevent_work(int id, int state) +{ + char *water[2] = { "CCIC=WATER", NULL }; + char *dry[2] = { "CCIC=DRY", NULL }; + char *vconn[2] = { "CCIC=VCONN", NULL }; +#if defined(CONFIG_SEC_FACTORY) + char ccicrid[15] = {0,}; + char *rid[2] = {ccicrid, NULL}; + char ccicFacErr[20] = {0,}; + char *facErr[2] = {ccicFacErr, NULL}; +#endif + + pr_info("usb: %s: id=%s state=%d\n", __func__, CCIC_NOTI_ID_Print[id], state); + + switch (id) { + case CCIC_NOTIFY_ID_WATER: + if (state) + kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, water); + else + kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, dry); + break; + case CCIC_NOTIFY_ID_VCONN: + kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, vconn); + break; +#if defined(CONFIG_SEC_FACTORY) + case CCIC_NOTIFY_ID_RID: + snprintf(ccicrid, sizeof(ccicrid), "%s", + (state < CCIC_NOTI_RID_NUM) ? CCIC_NOTI_RID_Print[state] : CCIC_NOTI_RID_Print[0]); + kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, rid); + break; + case CCIC_NOTIFY_ID_FAC: + snprintf(ccicFacErr, sizeof(ccicFacErr), "%s:%d", + "ERR_STATE", state); + kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, facErr); + break; +#endif + default: + break; + } +} + +/* ccic's attached_device attach broadcast */ +int ccic_notifier_notify(CC_NOTI_TYPEDEF *p_noti, void *pd, int pdic_attach) +{ + int ret = 0; + ccic_notifier.ccic_template = *p_noti; + + switch (p_noti->id) { +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + case CCIC_NOTIFY_ID_POWER_STATUS: // PDIC_NOTIFY_EVENT_PD_SINK + pr_info("%s: src:%01x dest:%01x id:%02x " + "attach:%02x cable_type:%02x rprd:%01x\n", __func__, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->cable_type, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->rprd); + + if (pd != NULL) { + if (!((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach && + ((struct pdic_notifier_struct *)pd)->event != PDIC_NOTIFY_EVENT_CCIC_ATTACH) { + ((struct pdic_notifier_struct *)pd)->event = PDIC_NOTIFY_EVENT_DETACH; + } + ccic_notifier.ccic_template.pd = pd; + + pr_info("%s: PD event:%d, num:%d, sel:%d \n", __func__, + ((struct pdic_notifier_struct *)pd)->event, + ((struct pdic_notifier_struct *)pd)->sink_status.available_pdo_num, + ((struct pdic_notifier_struct *)pd)->sink_status.selected_pdo_num); + } + break; +#endif + case CCIC_NOTIFY_ID_ATTACH: + pr_info("%s: src:%01x dest:%01x id:%02x " + "attach:%02x cable_type:%02x rprd:%01x\n", __func__, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->cable_type, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->rprd); + break; + case CCIC_NOTIFY_ID_RID: + pr_info("%s: src:%01x dest:%01x id:%02x rid:%02x\n", __func__, + ((CC_NOTI_RID_TYPEDEF *)p_noti)->src, + ((CC_NOTI_RID_TYPEDEF *)p_noti)->dest, + ((CC_NOTI_RID_TYPEDEF *)p_noti)->id, + ((CC_NOTI_RID_TYPEDEF *)p_noti)->rid); +#if defined(CONFIG_SEC_FACTORY) + ccic_uevent_work(CCIC_NOTIFY_ID_RID,((CC_NOTI_RID_TYPEDEF *)p_noti)->rid); +#endif + break; +#ifdef CONFIG_SEC_FACTORY + case CCIC_NOTIFY_ID_FAC: + pr_info("%s: src:%01x dest:%01x id:%02x ErrState:%02x\n", __func__, + p_noti->src, p_noti->dest, p_noti->id, p_noti->sub1); + ccic_uevent_work(CCIC_NOTIFY_ID_FAC, p_noti->sub1); + return 0; +#endif + case CCIC_NOTIFY_ID_WATER: + pr_info("%s: src:%01x dest:%01x id:%02x attach:%02x\n", __func__, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach); + ccic_uevent_work(CCIC_NOTIFY_ID_WATER, ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach); +#ifdef CONFIG_SEC_FACTORY + return 0; +#endif + break; + case CCIC_NOTIFY_ID_VCONN: + ccic_uevent_work(CCIC_NOTIFY_ID_VCONN, 0); + break; + case CCIC_NOTIFY_ID_ROLE_SWAP: + pr_info("%s: src:%01x dest:%01x id:%02x sub1:%02x\n", __func__, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, + ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach); + break; + default: + pr_info("%s: src:%01x dest:%01x id:%02x " + "sub1:%d sub2:%02x sub3:%02x\n", __func__, + ((CC_NOTI_TYPEDEF *)p_noti)->src, + ((CC_NOTI_TYPEDEF *)p_noti)->dest, + ((CC_NOTI_TYPEDEF *)p_noti)->id, + ((CC_NOTI_TYPEDEF *)p_noti)->sub1, + ((CC_NOTI_TYPEDEF *)p_noti)->sub2, + ((CC_NOTI_TYPEDEF *)p_noti)->sub3); + break; + } +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + store_usblog_notify(NOTIFY_CCIC_EVENT, (void *)p_noti, NULL); +#endif + ret = blocking_notifier_call_chain(&(ccic_notifier.notifier_call_chain), + p_noti->id, &(ccic_notifier.ccic_template)); + + + switch (ret) { + case NOTIFY_STOP_MASK: + case NOTIFY_BAD: + pr_err("%s: notify error occur(0x%x)\n", __func__, ret); + break; + case NOTIFY_DONE: + case NOTIFY_OK: + pr_info("%s: notify done(0x%x)\n", __func__, ret); + break; + default: + pr_info("%s: notify status unknown(0x%x)\n", __func__, ret); + break; + } + + return ret; + +} + +int ccic_notifier_init(void) +{ + int ret = 0; + + pr_info("%s\n", __func__); + if(ccic_notifier_init_done) + { + pr_err("%s already registered\n", __func__); + goto out; + } + ccic_notifier_init_done = 1; + ccic_device = sec_device_create(0, NULL, "ccic"); + + if (IS_ERR(ccic_device)) { + pr_err("%s Failed to create device(switch)!\n", __func__); + ret = -ENODEV; + goto out; + } + + /* create sysfs group */ + ret = sysfs_create_group(&ccic_device->kobj, &ccic_sysfs_group); + if (ret) { + pr_err("%s: ccic sysfs fail, ret %d", __func__, ret); + goto out; + } + + + BLOCKING_INIT_NOTIFIER_HEAD(&(ccic_notifier.notifier_call_chain)); + +out: + return ret; +} + +static void __exit ccic_notifier_exit(void) +{ + pr_info("%s: exit\n", __func__); +} + +device_initcall(ccic_notifier_init); +module_exit(ccic_notifier_exit); diff --git a/drivers/ccic/ccic_sysfs.c b/drivers/ccic/ccic_sysfs.c new file mode 100644 index 000000000000..7743a51c2998 --- /dev/null +++ b/drivers/ccic/ccic_sysfs.c @@ -0,0 +1,662 @@ +/* + * ccic_sysfs.c + * + * Copyright (C) 2016 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static ssize_t s2mm005_cur_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_version chip_swver; + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + s2mm005_get_chip_swversion(usbpd_data, &chip_swver); + pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__, + chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot); + + usbpd_data->firm_ver[0] = chip_swver.main[2]; + usbpd_data->firm_ver[1] = chip_swver.main[1]; + usbpd_data->firm_ver[2] = chip_swver.main[0]; + usbpd_data->firm_ver[3] = chip_swver.boot; + + return sprintf(buf, "%02X %02X %02X %02X\n", + usbpd_data->firm_ver[0], usbpd_data->firm_ver[1], + usbpd_data->firm_ver[2], usbpd_data->firm_ver[3]); + +} +static DEVICE_ATTR(cur_version, 0664, s2mm005_cur_ver_show, NULL); + +static ssize_t s2mm005_src_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + struct s2mm005_version fw_swver; + + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id, + &fw_swver, usbpd_data->firm_ver[3], usbpd_data->hw_rev); + return sprintf(buf, "%02X %02X %02X %02X\n", + fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot); +} +static DEVICE_ATTR(src_version, 0664, s2mm005_src_ver_show, NULL); + +static ssize_t s2mm005_show_manual_lpm_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + return sprintf(buf, "%d\n", usbpd_data->manual_lpm_mode); +} +static ssize_t s2mm005_store_manual_lpm_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int mode; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &mode); + pr_info("usb: %s mode=%d\n", __func__, mode); + + switch(mode){ + case 0: + /* Disable Low Power Mode for App (SW JIGON Disable) */ + s2mm005_manual_JIGON(usbpd_data, 0); + usbpd_data->manual_lpm_mode = 0; + break; + case 1: + /* Enable Low Power Mode for App (SW JIGON Enable) */ + s2mm005_manual_JIGON(usbpd_data, 1); + usbpd_data->manual_lpm_mode = 1; + break; + case 2: + /* SW JIGON Enable */ + s2mm005_manual_JIGON(usbpd_data, 1); +// s2mm005_manual_LPM(usbpd_data, 0x1); + usbpd_data->manual_lpm_mode = 1; + break; + default: + /* SW JIGON Disable */ + s2mm005_manual_JIGON(usbpd_data, 0); + usbpd_data->manual_lpm_mode = 0; + break; + } + + return size; +} +static DEVICE_ATTR(lpm_mode, 0664, + s2mm005_show_manual_lpm_mode, s2mm005_store_manual_lpm_mode); + +static ssize_t ccic_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + + return sprintf(buf, "%d\n", usbpd_data->pd_state); +} +static DEVICE_ATTR(state, 0664, ccic_state_show, NULL); + +#if defined(CONFIG_SEC_FACTORY) +static ssize_t ccic_rid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + + return sprintf(buf, "%d\n", usbpd_data->cur_rid); +} +static DEVICE_ATTR(rid, 0664, ccic_rid_show, NULL); + +static ssize_t s2mm005_store_control_option_command(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int cmd; + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + + sscanf(buf, "%d", &cmd); + pr_info("usb: %s mode=%d\n", __func__, cmd); + + s2mm005_control_option_command(usbpd_data, cmd); + + return size; +} +static DEVICE_ATTR(ccic_control_option, 0664, NULL, s2mm005_store_control_option_command); + +static ssize_t ccic_booting_dry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + pr_info("%s booting_run_dry=%d\n", __func__, + usbpd_data->fac_booting_dry_check); + + return sprintf(buf, "%d\n", (usbpd_data->fac_booting_dry_check)); +} +static DEVICE_ATTR(booting_dry, 0444, ccic_booting_dry_show, NULL); +#endif + +static int ccic_firmware_update_built_in(struct device *dev) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + struct s2mm005_version chip_swver, fw_swver, hwver; + + s2mm005_get_chip_hwversion(usbpd_data, &hwver); + pr_err("%s CHIP HWversion %2x %2x %2x %2x\n", __func__, + hwver.main[2] , hwver.main[1], hwver.main[0], hwver.boot); + s2mm005_get_chip_swversion(usbpd_data, &chip_swver); + pr_err("%s CHIP SWversion %2x %2x %2x %2x - before\n", __func__, + chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot); + s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id, + &fw_swver, chip_swver.boot, usbpd_data->hw_rev); + pr_err("%s SRC SWversion:%2x,%2x,%2x,%2x\n",__func__, + fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot); + + pr_err("%s: FW UPDATE boot:%01d hw_rev:%02d\n", __func__, chip_swver.boot, usbpd_data->hw_rev); + + if(chip_swver.main[0] == fw_swver.main[0]) { + pr_err("%s: FW version is same. Stop FW update. src:%2x chip:%2x\n", + __func__, chip_swver.main[0], fw_swver.main[0]); + } else + s2mm005_flash_fw(usbpd_data, chip_swver.boot); + + return 0; +} + +static int ccic_firmware_update_ums(struct device *dev) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + unsigned char *fw_data; + struct s2mm005_fw *fw_hd; + struct file *fp; + mm_segment_t old_fs; + long fw_size, nread; + int error = 0; + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(CCIC_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR); + if (IS_ERR(fp)) { + pr_err("%s: failed to open %s.\n", __func__, + CCIC_DEFAULT_UMS_FW); + error = -ENOENT; + goto open_err; + } + + fw_size = fp->f_path.dentry->d_inode->i_size; + + if (0 < fw_size) { + fw_data = kzalloc(fw_size, GFP_KERNEL); + nread = vfs_read(fp, (char __user *)fw_data, fw_size, &fp->f_pos); + + pr_info("%s: start, file path %s, size %ld Bytes\n", + __func__, CCIC_DEFAULT_UMS_FW, fw_size); + filp_close(fp, NULL); + + if (nread != fw_size) { + pr_err("%s: failed to read firmware file, nread %ld Bytes\n", + __func__, nread); + error = -EIO; + } else { + fw_hd = (struct s2mm005_fw*)fw_data; + pr_info("CCIC FW ver - cur:%02X %02X %02X %02X / bin:%02X %02X %02X %02X\n", + usbpd_data->firm_ver[0], usbpd_data->firm_ver[1], usbpd_data->firm_ver[2], usbpd_data->firm_ver[3], + fw_hd->boot, fw_hd->main[0], fw_hd->main[1], fw_hd->main[2]); + + if(fw_hd->boot == usbpd_data->firm_ver[3]) { + if (s2mm005_flash_fw(usbpd_data, FLASH_WRITE_UMS) >= 0) + goto done; + } else { + pr_err("error : Didn't match to CCIC FW firmware version\n"); + error = -EINVAL; + } + } + if (error < 0) + pr_err("%s: failed update firmware\n", __func__); +done: + kfree(fw_data); + } + +open_err: + set_fs(old_fs); + return error; +} + +static ssize_t ccic_store_firmware_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + u8 val = 0; + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + s2mm005_read_byte_flash(usbpd_data->i2c, FLASH_STATUS_0x24, &val, 1); + pr_err("%s flash mode: %s\n", __func__, flashmode_to_string(val)); + + return sprintf(buf, "%s\n", flashmode_to_string(val)); +} +static DEVICE_ATTR(fw_update_status, 0444, ccic_store_firmware_status_show, NULL); + +static ssize_t ccic_store_firmware_update(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + struct s2mm005_version version; + int mode = 0, ret = 1; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &mode); + pr_info("%s mode=%d\n", __func__, mode); + + s2mm005_get_chip_swversion(usbpd_data, &version); + pr_err("%s CHIP SWversion %2x %2x %2x %2x - before\n", __func__, + version.main[2] , version.main[1], version.main[0], version.boot); + + /* Factory cmd for firmware update + * argument represent what is source of firmware like below. + * + * 0 : [BUILT_IN] Getting firmware from source. + * 1 : [UMS] Getting firmware from sd card. + */ + switch (mode) { + case BUILT_IN: + ret = ccic_firmware_update_built_in(dev); + break; + case UMS: + ret = ccic_firmware_update_ums(dev); + break; + default: + pr_err("%s: Not support command[%d]\n", + __func__, mode); + break; + } + + s2mm005_get_chip_swversion(usbpd_data, &version); + pr_err("%s CHIP SWversion %2x %2x %2x %2x - after\n", __func__, + version.main[2] , version.main[1], version.main[0], version.boot); + + return size; +} +static DEVICE_ATTR(fw_update, 0220, NULL, ccic_store_firmware_update); + + +static ssize_t ccic_set_gpio(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int mode; + u8 W_DATA[2]; + u8 REG_ADD; + u8 R_DATA; + int i; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &mode); + pr_info("usb: %s mode=%d\n", __func__, mode); + + /* for Wake up*/ + for(i=0; i<5; i++){ + R_DATA = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(usbpd_data->i2c, REG_ADD, &R_DATA, 1); //dummy read + } + udelay(10); + + switch(mode){ + case 0: + /* SBU1/SBU2 set as open-drain status*/ + // SBU1/2 Open command ON + REG_ADD = 0x10; + W_DATA[0] = 0x03; + W_DATA[1] = 0x85; + s2mm005_write_byte(usbpd_data->i2c, REG_ADD, &W_DATA[0], 2); + break; + case 1: + /* SBU1/SBU2 set as default status */ + // SBU1/2 Open command OFF + REG_ADD = 0x10; + W_DATA[0] = 0x03; + W_DATA[1] = 0x86; + s2mm005_write_byte(usbpd_data->i2c, REG_ADD, &W_DATA[0], 2); + break; + default: + break; + } + + return size; +} + +static ssize_t ccic_get_gpio(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + u8 W_DATA[4]; + u8 REG_ADD; + u8 R_DATA; + int i; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + /* for Wake up*/ + for(i=0; i<5; i++){ + R_DATA = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(usbpd_data->i2c, REG_ADD, &R_DATA, 1); //dummy read + } + udelay(10); + + W_DATA[0] =0x2; + W_DATA[1] =0x10; + + W_DATA[2] =0x84; + W_DATA[3] =0x10; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 4); + s2mm005_read_byte(usbpd_data->i2c, 0x14, &R_DATA, 1); + + pr_err("%s SBU1 status = %2x , SBU2 status = %2x \n", __func__, + (R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5); + + return sprintf(buf, "%d %d\n", (R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5); +} +static DEVICE_ATTR(control_gpio, 0664, ccic_get_gpio, ccic_set_gpio); + +static ssize_t ccic_water_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + + if(!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + pr_info("%s water=%d, run_dry=%d\n", __func__, + usbpd_data->water_det, usbpd_data->run_dry); + + return sprintf(buf, "%d\n", (usbpd_data->water_det | !usbpd_data->run_dry)); +} +static DEVICE_ATTR(water, 0444, ccic_water_show, NULL); + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +static ssize_t ccic_send_samsung_uVDM_message(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int ret = 0; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + ret = send_samsung_unstructured_vdm_message(usbpd_data, buf, size); + if( ret < 0 ) + return ret; + else + return size; +} +static DEVICE_ATTR(samsung_uvdm, 0220, NULL, ccic_send_samsung_uVDM_message); + +static ssize_t ccic_send_uVDM_message(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int cmd = 0; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &cmd); + pr_info("%s cmd=%d\n", __func__, cmd); + + send_unstructured_vdm_message(usbpd_data, cmd); + + return size; +} +static DEVICE_ATTR(uvdm, 0220, NULL, ccic_send_uVDM_message); + +static ssize_t ccic_send_dna_audio_uVDM_message(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int cmd = 0; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &cmd); + pr_info("%s cmd=%d\n", __func__, cmd); + + send_dna_audio_unstructured_vdm_message(usbpd_data, cmd); + + return size; +} +static DEVICE_ATTR(dna_audio_uvdm, 0220, NULL, ccic_send_dna_audio_uVDM_message); + +static ssize_t ccic_send_dex_fan_uVDM_message(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int cmd = 0; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &cmd); + pr_info("%s cmd=%d\n", __func__, cmd); + + send_dex_fan_unstructured_vdm_message(usbpd_data, cmd); + + return size; +} +static DEVICE_ATTR(dex_fan_uvdm, 0220, NULL, ccic_send_dex_fan_uVDM_message); + +static ssize_t ccic_send_attention_message(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int cmd = 0; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &cmd); + pr_info("%s cmd=%d\n", __func__, cmd); + + send_attention_message(usbpd_data, cmd); + + return size; +} +static DEVICE_ATTR(attention, 0220, NULL, ccic_send_attention_message); +static ssize_t ccic_send_role_swap_message(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int cmd = 0; + + if (!usbpd_data) { + pr_err("usbpd_data is NULL\n"); + return -ENODEV; + } + + sscanf(buf, "%d", &cmd); + pr_info("%s cmd=%d\n", __func__, cmd); + + send_role_swap_message(usbpd_data, cmd); + + return size; +} +static DEVICE_ATTR(role_swap, 0220, NULL, ccic_send_role_swap_message); + +static ssize_t ccic_acc_device_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + + if (!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + pr_info("%s 0x%04x\n", __func__, usbpd_data->Device_Version); + + return sprintf(buf, "%04x\n", usbpd_data->Device_Version); +} +static DEVICE_ATTR(acc_device_version, 0444, ccic_acc_device_version_show,NULL); + +static ssize_t ccic_usbpd_ids_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int retval = 0; + + if (!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + retval = sprintf(buf, "%04x:%04x\n", + le16_to_cpu(usbpd_data->Vendor_ID), + le16_to_cpu(usbpd_data->Product_ID)); + pr_info("usb: %s : %s", + __func__, buf); + + return retval; +} +static DEVICE_ATTR(usbpd_ids, 0444, ccic_usbpd_ids_show, NULL); + +static ssize_t ccic_usbpd_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(dev); + int retval = 0; + + if (!usbpd_data) { + pr_err("%s usbpd_data is null!!\n", __func__); + return -ENODEV; + } + retval = sprintf(buf, "%d\n", usbpd_data->acc_type); + pr_info("usb: %s : %d", + __func__, usbpd_data->acc_type); + + return retval; +} + +static DEVICE_ATTR(usbpd_type, 0444, ccic_usbpd_type_show, NULL); +#endif + +static struct attribute *ccic_attributes[] = { + &dev_attr_cur_version.attr, + &dev_attr_src_version.attr, + &dev_attr_lpm_mode.attr, + &dev_attr_state.attr, +#if defined(CONFIG_SEC_FACTORY) + &dev_attr_rid.attr, + &dev_attr_ccic_control_option.attr, + &dev_attr_booting_dry.attr, +#endif + &dev_attr_fw_update.attr, + &dev_attr_fw_update_status.attr, + &dev_attr_control_gpio.attr, + &dev_attr_water.attr, +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + &dev_attr_uvdm.attr, + &dev_attr_dna_audio_uvdm.attr, + &dev_attr_dex_fan_uvdm.attr, + &dev_attr_samsung_uvdm.attr, + &dev_attr_attention.attr, + &dev_attr_role_swap.attr, + &dev_attr_acc_device_version.attr, + &dev_attr_usbpd_ids.attr, + &dev_attr_usbpd_type.attr, +#endif + NULL +}; + +const struct attribute_group ccic_sysfs_group = { + .attrs = ccic_attributes, +}; diff --git a/drivers/ccic/pdic_notifier.c b/drivers/ccic/pdic_notifier.c new file mode 100644 index 000000000000..9a57ee9c616f --- /dev/null +++ b/drivers/ccic/pdic_notifier.c @@ -0,0 +1,173 @@ +#include + +#include +#include + +#define SET_PDIC_NOTIFIER_BLOCK(nb, fn, dev) do { \ + (nb)->notifier_call = (fn); \ + (nb)->priority = (dev); \ + } while (0) + +#define DESTROY_PDIC_NOTIFIER_BLOCK(nb) \ + SET_PDIC_NOTIFIER_BLOCK(nb, NULL, -1) + +static struct s2mu004_pdic_notifier_struct pdic_notifier; + +int s2mu004_pdic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier, + muic_notifier_device_t listener) +{ + int ret = 0; + + pr_info("%s: listener=%d register\n", __func__, listener); + + SET_PDIC_NOTIFIER_BLOCK(nb, notifier, listener); + ret = blocking_notifier_chain_register(&(pdic_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_register error(%d)\n", + __func__, ret); + + /* current pdic's attached_device status notify */ + nb->notifier_call(nb, pdic_notifier.cmd, + &(pdic_notifier.attached_dev)); + + return ret; +} + +int s2mu004_pdic_notifier_unregister(struct notifier_block *nb) +{ + int ret = 0; + + pr_info("%s: listener=%d unregister\n", __func__, nb->priority); + + ret = blocking_notifier_chain_unregister(&(pdic_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_PDIC_NOTIFIER_BLOCK(nb); + + return ret; +} + +static int s2mu004_pdic_notifier_notify(void) +{ + int ret = 0; + + pr_info("%s: CMD=%d, DATA=%d\n", __func__, pdic_notifier.cmd, + pdic_notifier.attached_dev); + + ret = blocking_notifier_call_chain(&(pdic_notifier.notifier_call_chain), + pdic_notifier.cmd, &(pdic_notifier.attached_dev)); + + switch (ret) { + case NOTIFY_STOP_MASK: + case NOTIFY_BAD: + pr_err("%s: notify error occur(0x%x)\n", __func__, ret); + break; + case NOTIFY_DONE: + case NOTIFY_OK: + pr_info("%s: notify done(0x%x)\n", __func__, ret); + break; + default: + pr_info("%s: notify status unknown(0x%x)\n", __func__, ret); + break; + } + + return ret; +} + +void s2mu004_pdic_notifier_attach_attached_jig_dev(muic_attached_dev_t new_dev) +{ + pr_info("%s: (%d)\n", __func__, new_dev); + + pdic_notifier.cmd = PDIC_MUIC_NOTIFY_CMD_JIG_ATTACH; + pdic_notifier.attached_dev = new_dev; + + /* pdic's attached_device attach broadcast */ + s2mu004_pdic_notifier_notify(); +} +#if 0 +void s2mu004_pdic_notifier_detach_attached_jig_dev(muic_attached_dev_t cur_dev) +{ + pr_info("%s: (%d)\n", __func__, cur_dev); + + pdic_notifier.cmd = PDIC_MUIC_NOTIFY_CMD_JIG_DETACH; + + if (pdic_notifier.attached_dev != cur_dev) + pr_warn("%s: attached_dev of pdic_notifier(%d) != pdic_data(%d)\n", + __func__, pdic_notifier.attached_dev, cur_dev); + + if (pdic_notifier.attached_dev != ATTACHED_DEV_NONE_MUIC) { + /* pdic's attached_device detach broadcast */ + s2mu004_pdic_notifier_notify(); + } + + pdic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC; +} +#endif +void s2mu004_pdic_notifier_attach_attached_dev(muic_attached_dev_t new_dev) +{ + pr_info("%s: (%d)\n", __func__, new_dev); + + pdic_notifier.cmd = MUIC_NOTIFY_CMD_ATTACH; + pdic_notifier.attached_dev = new_dev; + + /* pdic's attached_device attach broadcast */ + s2mu004_pdic_notifier_notify(); +} + +void s2mu004_pdic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev) +{ + pr_info("%s: (%d)\n", __func__, cur_dev); + + pdic_notifier.cmd = MUIC_NOTIFY_CMD_DETACH; + + if (pdic_notifier.attached_dev != cur_dev) + pr_warn("%s: attached_dev of pdic_notifier(%d) != pdic_data(%d)\n", + __func__, pdic_notifier.attached_dev, cur_dev); + + if (pdic_notifier.attached_dev != ATTACHED_DEV_NONE_MUIC) { + /* pdic's attached_device detach broadcast */ + s2mu004_pdic_notifier_notify(); + } + + pdic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC; +} + +void s2mu004_pdic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev) +{ + pr_info("%s: (%d)\n", __func__, new_dev); + + pdic_notifier.cmd = MUIC_NOTIFY_CMD_LOGICALLY_ATTACH; + pdic_notifier.attached_dev = new_dev; + + /* pdic's attached_device attach broadcast */ + s2mu004_pdic_notifier_notify(); +} + +void s2mu004_pdic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev) +{ + pr_info("%s: (%d)\n", __func__, cur_dev); + + pdic_notifier.cmd = MUIC_NOTIFY_CMD_LOGICALLY_DETACH; + pdic_notifier.attached_dev = cur_dev; + + /* pdic's attached_device detach broadcast */ + s2mu004_pdic_notifier_notify(); + + pdic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC; +} + +static int __init s2mu004_pdic_notifier_init(void) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + BLOCKING_INIT_NOTIFIER_HEAD(&(pdic_notifier.notifier_call_chain)); + pdic_notifier.cmd = MUIC_NOTIFY_CMD_DETACH; + pdic_notifier.attached_dev = ATTACHED_DEV_UNKNOWN_MUIC; + + return ret; +} +subsys_initcall(s2mu004_pdic_notifier_init); + diff --git a/drivers/ccic/s2mm005.c b/drivers/ccic/s2mm005.c new file mode 100644 index 000000000000..7f36997b76c6 --- /dev/null +++ b/drivers/ccic/s2mm005.c @@ -0,0 +1,1396 @@ +/* + * driver/../s2mm005.c - S2MM005 USBPD device driver + * + * Copyright (C) 2015 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +#include +#endif + +#if defined (TEMP_BLOCK_USB) +extern unsigned int system_rev; +#endif + +extern struct device *ccic_device; +extern struct pdic_notifier_struct pd_noti; + + +extern unsigned int lpcharge; + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +static enum dual_role_property fusb_drp_properties[] = { + DUAL_ROLE_PROP_MODE, + DUAL_ROLE_PROP_PR, + DUAL_ROLE_PROP_DR, +}; +#endif +#define TEMP_CODE 1 +#if TEMP_CODE +static int is_irq_thread_func_called = 0; +#endif +//////////////////////////////////////////////////////////////////////////////// +// function definition +//////////////////////////////////////////////////////////////////////////////// +void s2mm005_int_clear(struct s2mm005_data *usbpd_data); +int s2mm005_read_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size); +int s2mm005_read_byte_flash(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size); +int s2mm005_write_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size); +int s2mm005_read_byte_16(const struct i2c_client *i2c, u16 reg, u8 *val); +int s2mm005_write_byte_16(const struct i2c_client *i2c, u16 reg, u8 val); +void s2mm005_rprd_mode_change(struct s2mm005_data *usbpd_data, u8 mode); +void s2mm005_manual_JIGON(struct s2mm005_data *usbpd_data, int mode); +void s2mm005_manual_LPM(struct s2mm005_data *usbpd_data, int cmd); +#if defined(CONFIG_CCIC_S2MM005_ANALOG_AUDIO) +void s2mm005_manual_ACC_LPM(struct s2mm005_data *usbpd_data); +#endif +void s2mm005_control_option_command(struct s2mm005_data *usbpd_data, int cmd); +int s2mm005_fw_ver_check(void * data); +void s2mm005_set_cabletype_as_TA(void); +int ccic_misc_init(void); +void ccic_misc_exit(void); +//////////////////////////////////////////////////////////////////////////////// +//status machine of s2mm005 ccic +//////////////////////////////////////////////////////////////////////////////// +//enum ccic_status { +// state_cc_unknown = 0, +// state_cc_idle, +// state_cc_rid, +// state_cc_updatefw, +// state_cc_alternate, +// state_cc_end=0xff, +//}; +//////////////////////////////////////////////////////////////////////////////// + +int s2mm005_read_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size) +{ + int ret, i2c_retry; u8 wbuf[2]; + struct i2c_msg msg[2]; + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + mutex_lock(&usbpd_data->i2c_mutex); + i2c_retry = 0; + msg[0].addr = i2c->addr; + msg[0].flags = i2c->flags; + msg[0].len = 2; + msg[0].buf = wbuf; + msg[1].addr = i2c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = size; + msg[1].buf = val; + + wbuf[0] = (reg & 0xFF00) >> 8; + wbuf[1] = (reg & 0xFF); + + do { + ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg)); + } while (ret < 0 && i2c_retry++ < 5); + + if (ret < 0) { +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + dev_err(&i2c->dev, "i2c read16 fail reg:0x%x error %d\n", reg, ret); + } + mutex_unlock(&usbpd_data->i2c_mutex); + + return ret; +} + +int s2mm005_read_byte_flash(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size) +{ + int ret; u8 wbuf[2]; + struct i2c_msg msg[2]; + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + u8 W_DATA[1]; + udelay(20); + W_DATA[0] = 0xAA; + s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 1); + udelay(20); + + mutex_lock(&usbpd_data->i2c_mutex); + msg[0].addr = i2c->addr; + msg[0].flags = i2c->flags; + msg[0].len = 2; + msg[0].buf = wbuf; + msg[1].addr = i2c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = size; + msg[1].buf = val; + + wbuf[0] = (reg & 0xFF00) >> 8; + wbuf[1] = (reg & 0xFF); + + ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + dev_err(&i2c->dev, "i2c read16 fail reg:0x%x error %d\n", reg, ret); + } + mutex_unlock(&usbpd_data->i2c_mutex); + + return ret; +} + +int s2mm005_write_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size) +{ + int ret, i2c_retry; u8 buf[258] = {0,}; + struct i2c_msg msg[1]; + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + if (size > 256) + { + pr_err("I2C error, over the size %d", size); + return -EIO; + } + + mutex_lock(&usbpd_data->i2c_mutex); + i2c_retry = 0; + msg[0].addr = i2c->addr; + msg[0].flags = 0; + msg[0].len = size+2; + msg[0].buf = buf; + + buf[0] = (reg & 0xFF00) >> 8; + buf[1] = (reg & 0xFF); + memcpy(&buf[2], val, size); + + do { + ret = i2c_transfer(i2c->adapter, msg, 1); + } while (ret < 0 && i2c_retry++ < 5); + + if (ret < 0) { +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + dev_err(&i2c->dev, "i2c write fail reg:0x%x error %d\n", reg, ret); + } + mutex_unlock(&usbpd_data->i2c_mutex); + + return ret; +} + +int s2mm005_read_byte_16(const struct i2c_client *i2c, u16 reg, u8 *val) +{ + int ret; u8 wbuf[2], rbuf; + struct i2c_msg msg[2]; + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + mutex_lock(&usbpd_data->i2c_mutex); + msg[0].addr = i2c->addr; + msg[0].flags = i2c->flags; + msg[0].len = 2; + msg[0].buf = wbuf; + msg[1].addr = i2c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &rbuf; + + wbuf[0] = (reg & 0xFF00) >> 8; + wbuf[1] = (reg & 0xFF); + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) { +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + dev_err(&i2c->dev, "i2c read16 fail reg(0x%x), error %d\n", reg, ret); + } + mutex_unlock(&usbpd_data->i2c_mutex); + + *val = rbuf; + return rbuf; +} + +int s2mm005_write_byte_16(const struct i2c_client *i2c, u16 reg, u8 val) +{ + int ret = 0; u8 wbuf[3]; + struct i2c_msg msg[1]; + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + mutex_lock(&usbpd_data->i2c_mutex); + msg[0].addr = i2c->addr; + msg[0].flags = 0; + msg[0].len = 3; + msg[0].buf = wbuf; + + wbuf[0] = (reg & 0xFF00) >> 8; + wbuf[1] = (reg & 0xFF); + wbuf[2] = (val & 0xFF); + + ret = i2c_transfer(i2c->adapter, msg, 1); + if (ret < 0) { +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + dev_err(&i2c->dev, "i2c write fail reg(0x%x:%x), error %d\n", reg, val, ret); + } + mutex_unlock(&usbpd_data->i2c_mutex); + + return ret; +} + +void s2mm005_int_clear(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + pr_info("%s : -- clear clear -- \n", __func__); + s2mm005_write_byte_16(i2c, 0x10, 0x1); +} + +void s2mm005_reset(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[5]; + u8 R_DATA[1]; + int i; + + pr_info("%s\n", __func__); + /* for Wake up*/ + for(i=0; i<5; i++){ + R_DATA[0] = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read + } + udelay(10); + + printk("%s\n",__func__); + W_DATA[0] = 0x02; + W_DATA[1] = 0x01; + W_DATA[2] = 0x1C; + W_DATA[3] = 0x10; + W_DATA[4] = 0x01; + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5); + /* reset stable time */ + msleep(100); +} + +void s2mm005_reset_enable(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[5]; + printk("%s\n",__func__); + W_DATA[0] = 0x02; + W_DATA[1] = 0x01; + W_DATA[2] = 0x5C; + W_DATA[3] = 0x10; + W_DATA[4] = 0x01; + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5); +} + +void s2mm005_system_reset(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + u8 W_DATA[6]; + u8 R_DATA[6]; + + W_DATA[0] =0x2; + W_DATA[1] =0x20; //word write + W_DATA[2] =0x64; + W_DATA[3] =0x10; + + s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 4); + s2mm005_read_byte(i2c, 0x14, &R_DATA[0], 2); + + /* SYSTEM RESET */ + W_DATA[0] = 0x02; + W_DATA[1] = 0x02; + W_DATA[2] = 0x68; + W_DATA[3] = 0x10; + W_DATA[4] = R_DATA[0]; + W_DATA[5] = R_DATA[1]; + + s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 6); + +} + +void s2mm005_hard_reset(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + struct device *i2c_dev = i2c->dev.parent->parent; + + struct pinctrl *i2c_pinctrl; + + i2c_lock_adapter(i2c->adapter); + i2c_pinctrl = devm_pinctrl_get_select(i2c_dev, "hard_reset"); + if (IS_ERR(i2c_pinctrl)) + pr_err("could not set reset pins\n"); + printk("hard_reset: %04d %1d %01d\n", __LINE__, gpio_get_value(usbpd_data->s2mm005_sda), gpio_get_value(usbpd_data->s2mm005_scl)); + + usleep_range(60 * 1000, 60 * 1000); + i2c_pinctrl = devm_pinctrl_get_select(i2c_dev, "default"); + if (IS_ERR(i2c_pinctrl)) + pr_err("could not set default pins\n"); + usleep_range(8 * 1000, 8 * 1000); + i2c_unlock_adapter(i2c->adapter); + printk("hard_reset: %04d %1d %01d\n", __LINE__, gpio_get_value(usbpd_data->s2mm005_sda), gpio_get_value(usbpd_data->s2mm005_scl)); +} + +void s2mm005_sram_reset(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[5]; + printk("%s\n",__func__); + /* boot control reset OM HIGH */ + W_DATA[0] = 0x02; + W_DATA[1] = 0x01; + W_DATA[2] = 0x1C; + W_DATA[3] = 0x10; + W_DATA[4] = 0x08; + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5); +} + +void s2mm005_reconnect(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[3]; + printk("%s\n",__func__); + W_DATA[0] = 0x03; + W_DATA[1] = 0x02; + W_DATA[2] = 0x00; + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3); +} + +void s2mm005_manual_JIGON(struct s2mm005_data *usbpd_data, int mode) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[5]; + u8 R_DATA[1]; + int i; + + pr_info("usb: %s mode=%s (fw=0x%x)\n", __func__, mode? "High":"Low", usbpd_data->firm_ver[2]); + + /* for Wake up*/ + for(i=0; i<5; i++){ + R_DATA[0] = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read + } + + udelay(10); + W_DATA[0] = 0x0F; + if(mode) W_DATA[1] = 0x5; // JIGON High + else W_DATA[1] = 0x4; // JIGON Low + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); +} + +void s2mm005_manual_LPM(struct s2mm005_data *usbpd_data, int cmd) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[2]; + u8 R_DATA[1]; + int i; + pr_info("usb: %s cmd=0x%x (fw=0x%x)\n", __func__, cmd, usbpd_data->firm_ver[2]); + + /* for Wake up*/ + for(i=0; i<5; i++){ + R_DATA[0] = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read + } + udelay(10); + + W_DATA[0] = 0x0F; + W_DATA[1] = cmd; + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); +} +#if defined(CONFIG_CCIC_S2MM005_ANALOG_AUDIO) +void s2mm005_manual_ACC_LPM(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[2]; + printk("%s\n",__func__); + + W_DATA[0] = 0x0F; + W_DATA[1] = 0x20; + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); +} +#endif + +void s2mm005_control_option_command(struct s2mm005_data *usbpd_data, int cmd) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[2]; + u8 R_DATA[1]; + int i; + printk("usb: %s cmd=0x%x (fw=0x%x)\n", __func__, cmd, usbpd_data->firm_ver[2]); + + /* for Wake up*/ + for(i=0; i<5; i++){ + R_DATA[0] = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read + } + udelay(10); + +// 0x81 : Vconn control option command ON +// 0x82 : Vconn control option command OFF +// 0x83 : Water Detect option command ON +// 0x84 : Water Detect option command OFF + +#if defined(CONFIG_SEC_FACTORY) + if((cmd&0xF) == 0x3) + usbpd_data->fac_water_enable = 1; + else if ((cmd&0xF) == 0x4) + usbpd_data->fac_water_enable = 0; +#endif + + REG_ADD = 0x10; + W_DATA[0] = 0x03; + W_DATA[1] = 0x80 | (cmd&0xF); + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); +} + +static void s2mm005_new_toggling_control(struct s2mm005_data *usbpd_data, u8 mode) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[2]; + + pr_info("%s, mode=0x%x\n",__func__, mode); + + W_DATA[0] = 0x03; + W_DATA[1] = mode; // 0x12 : detach, 0x13 : SRC, 0x14 : SNK + + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2); +} + +static void s2mm005_toggling_control(struct s2mm005_data *usbpd_data, u8 mode) +{ + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + u8 W_DATA[5]; + + pr_info("%s, mode=0x%x\n",__func__, mode); + + W_DATA[0] = 0x02; + W_DATA[1] = 0x01; + W_DATA[2] = 0x00; + W_DATA[3] = 0x50; + W_DATA[4] = mode; // 0x1 : SRC, 0x2 : SNK, 0x3: DRP + + REG_ADD = 0x10; + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5); +} + +int s2mm005_fw_ver_check(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct s2mm005_version chip_swver, hwver; + + if ((usbpd_data->firm_ver[1] == 0xFF && usbpd_data->firm_ver[2] == 0xFF) + || (usbpd_data->firm_ver[1] == 0x00 && usbpd_data->firm_ver[2] == 0x00)) { + s2mm005_get_chip_hwversion(usbpd_data, &hwver); + pr_err("%s CHIP HWversion %2x %2x %2x %2x\n", __func__, + hwver.main[2] , hwver.main[1], hwver.main[0], hwver.boot); + + s2mm005_get_chip_swversion(usbpd_data, &chip_swver); + pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__, + chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot); + + if ((chip_swver.main[0] == 0xFF && chip_swver.main[1] == 0xFF) + || (chip_swver.main[0] == 0x00 && chip_swver.main[1] == 0x00)) { + pr_err("%s Invalid FW version\n", __func__); + return CCIC_FW_VERSION_INVALID; + } + + store_ccic_version(&hwver.main[0], &chip_swver.main[0], &chip_swver.boot); + usbpd_data->firm_ver[0] = chip_swver.main[2]; + usbpd_data->firm_ver[1] = chip_swver.main[1]; + usbpd_data->firm_ver[2] = chip_swver.main[0]; + usbpd_data->firm_ver[3] = chip_swver.boot; + } + return 0; +} + +void s2mm005_set_upsm_mode(void) +{ + struct s2mm005_data *usbpd_data; + u8 W_DATA[2]; + + if(!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if(!usbpd_data) + return; + + W_DATA[0] =0x3; + W_DATA[1] =0x40; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + + pr_info("%s : current status is upsm! \n", __func__); +} + +void s2mm005_set_cabletype_as_TA(void) +{ + struct s2mm005_data *usbpd_data; + u8 W_DATA[2]; + + if(!ccic_device) + return; + usbpd_data = dev_get_drvdata(ccic_device); + if(!usbpd_data) + return; + + W_DATA[0] =0x3; + W_DATA[1] =0x41; + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2); + + pr_info("%s : set_cabletype_as_TA! \n", __func__); +} + +void s2mm005_rprd_mode_change(struct s2mm005_data *usbpd_data, u8 mode) +{ + pr_info("%s, mode=0x%x\n",__func__, mode); + + switch(mode) + { + case TYPE_C_ATTACH_DFP: // SRC + s2mm005_new_toggling_control(usbpd_data, 0x12); + msleep(1000); + s2mm005_new_toggling_control(usbpd_data, 0x13); + break; + case TYPE_C_ATTACH_UFP: // SNK + s2mm005_new_toggling_control(usbpd_data, 0x12); + msleep(1000); + s2mm005_new_toggling_control(usbpd_data, 0x14); + break; + case TYPE_C_ATTACH_DRP: // DRP + s2mm005_toggling_control(usbpd_data, TYPE_C_ATTACH_DRP); + break; + }; +} + +#if TEMP_CODE +static irqreturn_t s2mm005_init_detect_irq(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + int irq_gpio_status[2]; + u8 plug_attach_done; + u8 pdic_attach = 0; + uint32_t *pPRT_MSG = NULL; + + MSG_IRQ_STATUS_Type MSG_IRQ_State; + + if(!is_irq_thread_func_called) + { + printk("%s : irq_thread is already registered\n",__func__); + } + dev_err(&i2c->dev, "%d times\n", ++usbpd_data->wq_times); + + // Function State + irq_gpio_status[0] = gpio_get_value(usbpd_data->irq_gpio); + dev_info(&i2c->dev, "IRQ0:%02d\n", irq_gpio_status[0]); + + // Send attach event + process_cc_attach(usbpd_data,&plug_attach_done); + + if(usbpd_data->water_det){ + process_cc_water_det(usbpd_data); + goto water_init; + } + + // Get staus interrupt register + process_cc_get_int_status(usbpd_data, pPRT_MSG ,&MSG_IRQ_State); + + // pd irq processing + process_pd(usbpd_data, plug_attach_done, &pdic_attach, &MSG_IRQ_State); + + // RID processing + process_cc_rid(usbpd_data); + +water_init: + /* ========================================== */ +// s2mm005_int_clear(usbpd_data); + irq_gpio_status[1] = gpio_get_value(usbpd_data->irq_gpio); + dev_info(&i2c->dev, "IRQ1:%02d", irq_gpio_status[1]); + /* ========================================== */ + return IRQ_HANDLED; + } +#endif + +static irqreturn_t s2mm005_usbpd_irq_thread(int irq, void *data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + int irq_gpio_status[2]; + u8 plug_attach_done; + u8 pdic_attach = 0; + uint32_t *pPRT_MSG = NULL; + + MSG_IRQ_STATUS_Type MSG_IRQ_State; +#if TEMP_CODE + is_irq_thread_func_called = 1; +#endif + dev_info(&i2c->dev, "%d times\n", ++usbpd_data->wq_times); + + // Function State + irq_gpio_status[0] = gpio_get_value(usbpd_data->irq_gpio); + dev_info(&i2c->dev, "IRQ0:%02d\n", irq_gpio_status[0]); + wake_lock_timeout(&usbpd_data->wlock, HZ); + + if (s2mm005_fw_ver_check(usbpd_data) == CCIC_FW_VERSION_INVALID) { + goto ver_err; + } + + // Send attach event + process_cc_attach(usbpd_data,&plug_attach_done); + + if(usbpd_data->water_det || !usbpd_data->run_dry || !usbpd_data->booting_run_dry){ + process_cc_water_det(usbpd_data); + goto water; + } + + // Get staus interrupt register + process_cc_get_int_status(usbpd_data, pPRT_MSG ,&MSG_IRQ_State); + + // pd irq processing + process_pd(usbpd_data, plug_attach_done, &pdic_attach, &MSG_IRQ_State); + + // RID processing + process_cc_rid(usbpd_data); + +ver_err: +water: + /* ========================================== */ + // s2mm005_int_clear(usbpd_data); + irq_gpio_status[1] = gpio_get_value(usbpd_data->irq_gpio); + dev_info(&i2c->dev, "IRQ1:%02d", irq_gpio_status[1]); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_OF) +static int of_s2mm005_usbpd_dt(struct device *dev, + struct s2mm005_data *usbpd_data) +{ + struct device_node *np = dev->of_node; + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + /* WA for Water detection in LPM mode + If there is a oscillation in SBU line treated as water. + */ + if(lpcharge) + { + int gpio_dp_sw_oe; + + pr_info("aux_sw_oe pin set to high\n"); + gpio_dp_sw_oe = of_get_named_gpio(np, "usbpd,aux-en-gpio", 0); + gpio_direction_output(gpio_dp_sw_oe, 1); + } +#endif + + usbpd_data->irq_gpio = of_get_named_gpio(np, "usbpd,usbpd_int", 0); + usbpd_data->s2mm005_om = of_get_named_gpio(np, "usbpd,s2mm005_om", 0); + usbpd_data->s2mm005_sda = of_get_named_gpio(np, "usbpd,s2mm005_sda", 0); + usbpd_data->s2mm005_scl = of_get_named_gpio(np, "usbpd,s2mm005_scl", 0); + if(of_property_read_u32(np, "usbpd,s2mm005_fw_product_id", &usbpd_data->s2mm005_fw_product_id)) { + usbpd_data->s2mm005_fw_product_id = 0x12; + } + +#if defined (TEMP_BLOCK_USB) + usbpd_data->hw_rev = system_rev; +#else + usbpd_data->hw_rev = 0; +#endif + dev_err(dev, "hw_rev:%02d usbpd_irq = %d s2mm005_om = %d\n" + "s2mm005_sda = %d, s2mm005_scl = %d, fw_product_id=0x%02X\n", + usbpd_data->hw_rev, + usbpd_data->irq_gpio, usbpd_data->s2mm005_om, + usbpd_data->s2mm005_sda, usbpd_data->s2mm005_scl, + usbpd_data->s2mm005_fw_product_id); + + return 0; +} +#endif /* CONFIG_OF */ + +static int pdic_handle_usb_external_notifier_notification + (struct notifier_block *nb, unsigned long action, void *data) +{ +#if defined (CONFIG_CCIC_ALTERNATE_MODE) + struct s2mm005_data *usbpd_data = dev_get_drvdata(ccic_device); +#endif + int ret = 0; + int enable = *(int *)data; + + pr_info("%s : action=%lu , enable=%d\n", __func__, action, enable); + switch (action) { + case EXTERNAL_NOTIFY_HOSTBLOCK_PRE: + if (enable) { + set_enable_alternate_mode(ALTERNATE_MODE_STOP); +#if defined (CONFIG_CCIC_ALTERNATE_MODE) + if(usbpd_data->dp_is_connect) + dp_detach(usbpd_data); +#endif + } else { +#if defined (CONFIG_CCIC_ALTERNATE_MODE) + if(usbpd_data->dp_is_connect) + dp_detach(usbpd_data); +#endif + } + break; + case EXTERNAL_NOTIFY_HOSTBLOCK_POST: + if(enable) { + } else { + set_enable_alternate_mode(ALTERNATE_MODE_START); + } + break; + default: + break; + } + + return ret; +} + +static void delayed_external_notifier_init(struct work_struct *work) +{ + int ret = 0; + static int retry_count = 1; + int max_retry_count = 5; + struct s2mm005_data *usbpd_data = dev_get_drvdata(ccic_device); + + pr_info("%s : %d = times!\n",__func__,retry_count); + + // Register ccic handler to ccic notifier block list + ret = usb_external_notify_register(&usbpd_data->usb_external_notifier_nb, + pdic_handle_usb_external_notifier_notification,EXTERNAL_NOTIFY_DEV_PDIC); + if(ret < 0) { + pr_err("Manager notifier init time is %d.\n",retry_count); + if(retry_count++ != max_retry_count) + schedule_delayed_work(&usbpd_data->usb_external_notifier_register_work, msecs_to_jiffies(2000)); + else + pr_err("fail to init external notifier\n"); + + } else { + pr_info("%s : external notifier register done!\n",__func__); + } +} + +static int s2mm005_usbpd_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent); + struct s2mm005_data *usbpd_data; + int ret = 0; + u8 check[8] = {0,}; + uint16_t REG_ADD; + uint8_t MSG_BUF[32] = {0,}; +#if defined(CONFIG_SEC_GTS4LV_PROJECT) + SINK_VAR_SUPPLY_Typedef *pSINK_VAR_MSG; +#endif + MSG_HEADER_Typedef *pMSG_HEADER; +#if defined(CONFIG_SEC_FACTORY) + LP_STATE_Type Lp_DATA; +#endif + uint32_t * MSG_DATA; + uint8_t cnt; + struct s2mm005_version chip_swver, fw_swver, hwver; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + struct dual_role_phy_desc *desc; + struct dual_role_phy_instance *dual_role; +#endif +#if defined(CONFIG_USB_HOST_NOTIFY) + struct otg_notify *o_notify = get_otg_notify(); +#endif + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&i2c->dev, "i2c functionality check error\n"); + return -EIO; + } + usbpd_data = devm_kzalloc(&i2c->dev, sizeof(struct s2mm005_data), GFP_KERNEL); + if (!usbpd_data) { + dev_err(&i2c->dev, "Failed to allocate driver data\n"); + return -ENOMEM; + } + +#if defined(CONFIG_OF) + if (i2c->dev.of_node) + of_s2mm005_usbpd_dt(&i2c->dev, usbpd_data); + else { + dev_err(&i2c->dev, "not found ccic dt! ret:%d\n", ret); + return -ENODEV; + } +#endif + ret = gpio_request(usbpd_data->irq_gpio, "s2mm005_irq"); + if (ret) + goto err_free_irq_gpio; + + gpio_direction_input(usbpd_data->irq_gpio); + usbpd_data->irq = gpio_to_irq(usbpd_data->irq_gpio); + dev_info(&i2c->dev, "%s:IRQ NUM %d\n", __func__, usbpd_data->irq); + + usbpd_data->dev = &i2c->dev; + usbpd_data->i2c = i2c; + i2c_set_clientdata(i2c, usbpd_data); + dev_set_drvdata(ccic_device, usbpd_data); + device_init_wakeup(usbpd_data->dev, 1); + pd_noti.pusbpd = usbpd_data; + mutex_init(&usbpd_data->i2c_mutex); + + /* Init */ + usbpd_data->p_prev_rid = -1; + usbpd_data->prev_rid = -1; + usbpd_data->cur_rid = RID_OPEN; + usbpd_data->is_dr_swap = 0; + usbpd_data->is_pr_swap = 0; + usbpd_data->pd_state = 0; + usbpd_data->func_state = 0; + usbpd_data->data_role = 0; + usbpd_data->is_host = 0; + usbpd_data->is_client = 0; + usbpd_data->manual_lpm_mode = 0; + usbpd_data->water_det = 0; + usbpd_data->run_dry = 1; + usbpd_data->booting_run_dry = 1; + usbpd_data->short_detected = false; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->try_state_change = 0; +#endif +#if defined(CONFIG_SEC_FACTORY) + usbpd_data->fac_water_enable = 0; +#endif +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + init_completion(&usbpd_data->suspend_wait); + init_completion(&usbpd_data->resume_wait); +#endif + + wake_lock_init(&usbpd_data->wlock, WAKE_LOCK_SUSPEND, + "s2mm005-intr"); + +#if defined(CONFIG_CCIC_NOTIFIER) + /* Create a work queue for the ccic irq thread */ + usbpd_data->ccic_wq + = create_singlethread_workqueue("ccic_irq_event"); + if (!usbpd_data->ccic_wq) { + pr_err("%s failed to create work queue\n", __func__); + ret = -ENOMEM; + goto err_free_irq_gpio; + } +#endif + + dev_err(&i2c->dev, "probed, irq %d\n", usbpd_data->irq_gpio); + + for (cnt = 0; cnt < 32; cnt++) { + MSG_BUF[cnt] = 0; + } + + REG_ADD = REG_TX_SINK_CAPA_MSG; + ret = s2mm005_read_byte(i2c, REG_ADD, MSG_BUF, 32); + if (ret < 0) { + s2mm005_hard_reset(usbpd_data); + msleep(1000); + ret = s2mm005_read_byte(i2c, REG_ADD, MSG_BUF, 32); + if (ret < 0) { + /* to check wrong ccic chipsets, It will be removed after PRA */ + panic("Intentional Panic - ccic i2c error\n"); +// dev_err(&i2c->dev, "%s has i2c read error.\n", __func__); +// goto err_init_irq; + } + } + + s2mm005_get_chip_hwversion(usbpd_data, &hwver); + pr_err("%s CHIP HWversion %2x %2x %2x %2x\n", __func__, + hwver.main[2] , hwver.main[1], hwver.main[0], hwver.boot); + pr_err("%s CHIP HWversion2 %2x %2x %2x %2x \n", __func__, + hwver.ver2[3], hwver.ver2[2], hwver.ver2[1], hwver.ver2[0]); + + + s2mm005_get_chip_swversion(usbpd_data, &chip_swver); + pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__, + chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot); + pr_err("%s CHIP SWversion2 %2x %2x %2x %2x\n", __func__, + chip_swver.ver2[3], chip_swver.ver2[2], chip_swver.ver2[1], chip_swver.ver2[0]); + + s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id, + &fw_swver, chip_swver.boot, usbpd_data->hw_rev); + store_ccic_bin_version(&fw_swver.main[0], &fw_swver.boot); + pr_err("%s SRC SWversion:%2x,%2x,%2x,%2x\n",__func__, + fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot); + pr_err("%s: FW UPDATE boot:%01d hw_rev:%02d\n", __func__, + chip_swver.boot, usbpd_data->hw_rev); + + usbpd_data->fw_product_id = fw_swver.main[2]; + +#if defined(CONFIG_SEC_FACTORY) + s2mm005_read_byte(i2c, 0x60, Lp_DATA.BYTE, 4); + pr_err("%s: WATER reg:0x%02X BOOTING_RUN_DRY=%d\n", __func__, + Lp_DATA.BYTE[0], Lp_DATA.BITS.BOOTING_RUN_DRY); + + usbpd_data->fac_booting_dry_check = Lp_DATA.BITS.BOOTING_RUN_DRY; +#endif + if(chip_swver.boot == 0x7) { +#ifdef CONFIG_SEC_FACTORY + if ((chip_swver.main[0] != fw_swver.main[0]) /* main version */ + || (chip_swver.main[1] != fw_swver.main[1]) /* sub version */ + || (chip_swver.main[2] != fw_swver.main[2])) /* product id */ + { + if(s2mm005_flash_fw(usbpd_data,chip_swver.boot) < 0) { + pr_err("%s: s2mm005_flash_fw 1st fail, try again \n", __func__); + if(s2mm005_flash_fw(usbpd_data,chip_swver.boot) < 0) { + pr_err("%s: s2mm005_flash_fw 2st fail, panic \n", __func__); + panic("infinite write fail!\n"); + } + } + } +#else + if ((chip_swver.main[0] < fw_swver.main[0]) + || ((chip_swver.main[0] == fw_swver.main[0]) && (chip_swver.main[1] < fw_swver.main[1])) + || (chip_swver.main[2] != fw_swver.main[2])) + s2mm005_flash_fw(usbpd_data, chip_swver.boot); + else if ((((chip_swver.main[2] == 0xff) && (chip_swver.main[1] == 0xa5)) //Factory Code + || chip_swver.main[2] == 0x00) //Old Version + && fw_swver.main[2] != 0x00) + s2mm005_flash_fw(usbpd_data, chip_swver.boot); +#endif + + s2mm005_get_chip_swversion(usbpd_data, &chip_swver); + pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__, + chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot); + } + else if (chip_swver.boot == 0x8) { +#if defined(CONFIG_SEC_FACTORY) + if ((chip_swver.main[0] != fw_swver.main[0]) /* main version */ + || (chip_swver.main[1] != fw_swver.main[1]) /* sub version */ + || (chip_swver.main[2] != fw_swver.main[2])) /* product id */ + { + if(s2mm005_flash_fw(usbpd_data,chip_swver.boot) < 0) { + pr_err("%s: s2mm005_flash_fw 1st fail, try again \n", __func__); + if(s2mm005_flash_fw(usbpd_data,chip_swver.boot) < 0) { + pr_err("%s: s2mm005_flash_fw 2st fail, panic \n", __func__); + panic("infinite write fail!\n"); + } + } + } +#else + if ((chip_swver.main[0] < fw_swver.main[0]) + || ((chip_swver.main[0] == fw_swver.main[0]) && (chip_swver.main[1] < fw_swver.main[1])) + || (chip_swver.main[2] != fw_swver.main[2])) + s2mm005_flash_fw(usbpd_data, chip_swver.boot); + else if ((((chip_swver.main[2] == 0xff) && (chip_swver.main[1] == 0xa5)) //Factory Code + || chip_swver.main[2] == 0x00) //Old Version + && fw_swver.main[2] != 0x00) + s2mm005_flash_fw(usbpd_data, chip_swver.boot); +#endif + + s2mm005_get_chip_swversion(usbpd_data, &chip_swver); + pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__, + chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot); + pr_err("%s CHIP SWversion2 %2x %2x %2x %2x \n", __func__, + chip_swver.ver2[3],chip_swver.ver2[2] ,chip_swver.ver2[1],chip_swver.ver2[0]); + } + store_ccic_version(&hwver.main[0], &chip_swver.main[0], &chip_swver.boot); + + usbpd_data->firm_ver[0] = chip_swver.main[2]; + usbpd_data->firm_ver[1] = chip_swver.main[1]; + usbpd_data->firm_ver[2] = chip_swver.main[0]; + usbpd_data->firm_ver[3] = chip_swver.boot; + + /* update Sink PDO */ + MSG_DATA = (uint32_t *)&MSG_BUF[0]; + dev_info(&i2c->dev, "--- Read Data on TX_SNK_CAPA_MSG(0x220)\n\r"); + for(cnt = 0; cnt < 8; cnt++) { + dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]); + } + + pMSG_HEADER = (MSG_HEADER_Typedef *)&MSG_BUF[0]; +#if defined(CONFIG_SEC_GTS4LV_PROJECT) + pMSG_HEADER->BITS.Number_of_obj -= 1; + pSINK_VAR_MSG = (SINK_VAR_SUPPLY_Typedef *)&MSG_BUF[12]; + pSINK_VAR_MSG->DATA = 0x8B4190C8; /* 5~9V, 2A */ +#endif + + dev_info(&i2c->dev, "--- Write DATA\n\r"); + for (cnt = 0; cnt < 8; cnt++) { + dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]); + } + + /* default value is written by CCIC FW. If you need others, overwrite it.*/ +#if defined(CONFIG_SEC_GTS4LV_PROJECT) + s2mm005_write_byte(i2c, REG_ADD, &MSG_BUF[0], 32); +#endif + + for (cnt = 0; cnt < 32; cnt++) { + MSG_BUF[cnt] = 0; + } + + for (cnt = 0; cnt < 8; cnt++) { + dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]); + } + ret = s2mm005_read_byte(i2c, REG_ADD, MSG_BUF, 32); + + dev_info(&i2c->dev, "--- Read 2 new Data on TX_SNK_CAPA_MSG(0x220)\n\r"); + for(cnt = 0; cnt < 8; cnt++) { + dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]); + } + +#ifdef CONFIG_CCIC_LPM_ENABLE + pr_err("S2MM005 LPM_ENABLE\n"); + check[0] = 0x0F; + check[1] = 0x06; + s2mm005_write_byte(i2c, 0x10, &check[0], 2); +#endif + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + desc = + devm_kzalloc(&i2c->dev, + sizeof(struct dual_role_phy_desc), GFP_KERNEL); + if (!desc) { + pr_err("unable to allocate dual role descriptor\n"); + goto err_init_irq; + } + + desc->name = "otg_default"; + desc->supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP; + desc->get_property = dual_role_get_local_prop; + desc->set_property = dual_role_set_prop; + desc->properties = fusb_drp_properties; + desc->num_properties = ARRAY_SIZE(fusb_drp_properties); + desc->property_is_writeable = dual_role_is_writeable; + dual_role = + devm_dual_role_instance_register(&i2c->dev, desc); + dual_role->drv_data = usbpd_data; + usbpd_data->dual_role = dual_role; + usbpd_data->desc = desc; + + init_completion(&usbpd_data->reverse_completion); + usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE; + INIT_DELAYED_WORK(&usbpd_data->role_swap_work, role_swap_check); +#elif defined(CONFIG_TYPEC) + usbpd_data->typec_cap.revision = USB_TYPEC_REV_1_2; + usbpd_data->typec_cap.pd_revision = 0x300; + usbpd_data->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + usbpd_data->typec_cap.pr_set = s2mm005_pr_set; + usbpd_data->typec_cap.dr_set = s2mm005_dr_set; + usbpd_data->typec_cap.port_type_set = s2mm005_port_type_set; + usbpd_data->typec_cap.type = TYPEC_PORT_DRP; + + usbpd_data->typec_power_role = TYPEC_SINK; + usbpd_data->typec_data_role = TYPEC_DEVICE; + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; + + usbpd_data->port = typec_register_port(usbpd_data->dev, &usbpd_data->typec_cap); + if (IS_ERR(usbpd_data->port)) + pr_err("%s : unable to register typec_register_port\n", __func__); + else + pr_err("%s : success typec_register_port port=%pK\n", __func__, usbpd_data->port); + + usbpd_data->partner = NULL; + init_completion(&usbpd_data->typec_reverse_completion); + INIT_DELAYED_WORK(&usbpd_data->typec_role_swap_work, typec_role_swap_check); +#endif + usbpd_data->pd_support = false; +#if defined(CONFIG_USB_HOST_NOTIFY) + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); +#endif +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + init_completion(&usbpd_data->uvdm_out_wait); + init_completion(&usbpd_data->uvdm_longpacket_in_wait); + usbpd_data->alternate_state = 0; + usbpd_data->acc_type = 0; + usbpd_data->dp_is_connect = 0; + usbpd_data->dp_hs_connect = 0; + usbpd_data->selected_pin = 0; + usbpd_data->pin_assignment = 0; + usbpd_data->is_samsung_accessory_enter_mode = 0; + usbpd_data->Vendor_ID = 0; + usbpd_data->Product_ID = 0; + usbpd_data->Device_Version = 0; + usbpd_data->host_turn_on_wait_time = 20; + usbpd_data->is_sent_pin_configuration = 0; + ccic_register_switch_device(1); + INIT_DELAYED_WORK(&usbpd_data->acc_detach_work, acc_detach_check); + init_waitqueue_head(&usbpd_data->host_turn_on_wait_q); + set_host_turn_on_event(0); + ret = ccic_misc_init(); + if (ret) { + dev_err(&i2c->dev, "ccic misc register is failed, error %d\n", ret); + goto err_init_irq; + } +#endif + +#if TEMP_CODE + is_irq_thread_func_called = 0; +#else + s2mm005_int_clear(usbpd_data); +#endif + fp_select_pdo = s2mm005_select_pdo; + + ret = request_threaded_irq(usbpd_data->irq, NULL, s2mm005_usbpd_irq_thread, + (IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_ONESHOT), "s2mm005-usbpd", usbpd_data); + if (ret) { + dev_err(&i2c->dev, "Failed to request IRQ %d, error %d\n", usbpd_data->irq, ret); + goto err_init_irq; + } + +#if defined(CONFIG_BATTERY_SAMSUNG) + if(usbpd_data->fw_product_id == PRODUCT_NUM_DREAM) { + u8 W_CHG_INFO[3] = {0,}; + + W_CHG_INFO[0] = 0x0f; + W_CHG_INFO[1] = 0x0c; + if (lpcharge) + W_CHG_INFO[2] = 0x1; // lpcharge + else + W_CHG_INFO[2] = 0x0; // normal + + s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_CHG_INFO[0], 3); // send info to ccic + } +#endif + + INIT_DELAYED_WORK(&usbpd_data->usb_external_notifier_register_work, + delayed_external_notifier_init); + + // Register ccic handler to ccic notifier block list + ret = usb_external_notify_register(&usbpd_data->usb_external_notifier_nb, + pdic_handle_usb_external_notifier_notification,EXTERNAL_NOTIFY_DEV_PDIC); + if(ret < 0) { + schedule_delayed_work(&usbpd_data->usb_external_notifier_register_work, msecs_to_jiffies(2000)); + } else { + pr_info("%s : external notifier register done!\n",__func__); + } + +#if TEMP_CODE + printk("%s : current_irq_status = %d\n",__func__, gpio_get_value(usbpd_data->irq_gpio)); +#if 0 // implement hard reset codes. + if(!gpio_get_value(usbpd_data->irq_gpio) && !is_irq_thread_func_called) +#else + if(!is_irq_thread_func_called) +#endif + s2mm005_init_detect_irq(usbpd_data); +#else + s2mm005_reconnect(usbpd_data); +#endif + + // s2mm005_int_clear(usbpd_data); + + return ret; + +err_init_irq: + if (usbpd_data->irq) { + free_irq(usbpd_data->irq, usbpd_data); + usbpd_data->irq = 0; + } +err_free_irq_gpio: + wake_lock_destroy(&usbpd_data->wlock); + gpio_free(usbpd_data->irq_gpio); + ccic_misc_exit(); + return ret; +} + +static int s2mm005_usbpd_remove(struct i2c_client *i2c) +{ + struct s2mm005_data *usbpd_data = dev_get_drvdata(ccic_device); + + process_cc_detach(usbpd_data); + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + devm_dual_role_instance_unregister(usbpd_data->dev, usbpd_data->dual_role); + devm_kfree(usbpd_data->dev, usbpd_data->desc); +#elif defined(CONFIG_TYPEC) + typec_unregister_port(usbpd_data->port); +#endif + + sysfs_remove_group(&ccic_device->kobj, &ccic_sysfs_group); + + if (usbpd_data->irq) { + free_irq(usbpd_data->irq, usbpd_data); + usbpd_data->irq = 0; + } + + if (usbpd_data->i2c) { + disable_irq_wake(usbpd_data->i2c->irq); + free_irq(usbpd_data->i2c->irq, usbpd_data); + + mutex_destroy(&usbpd_data->i2c_mutex); + i2c_set_clientdata(usbpd_data->i2c, NULL); + } + + wake_lock_destroy(&usbpd_data->wlock); + + return 0; +} + +static void s2mm005_usbpd_shutdown(struct i2c_client *i2c) +{ + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + struct device_node *np; + int gpio_dp_sw_oe; +#endif + disable_irq(usbpd_data->irq); + + if ((usbpd_data->cur_rid != RID_523K) && + (usbpd_data->cur_rid != RID_619K) && + (!usbpd_data->manual_lpm_mode)) { + + pr_info("%s: pd_state=%d, water=%d, dry=%d\n", __func__, + usbpd_data->pd_state, usbpd_data->water_det, usbpd_data->run_dry); + + if (usbpd_data->water_det) { + s2mm005_hard_reset(usbpd_data); + } else { + if (usbpd_data->pd_state) { +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (usbpd_data->dp_is_connect) { + pr_info("aux_sw_oe pin set to high\n"); + np = of_find_node_by_name(NULL, "qcom,dp_ctrl"); + gpio_dp_sw_oe = of_get_named_gpio(np, "qcom,aux-en-gpio", 0); + gpio_direction_output(gpio_dp_sw_oe, 1); + } +#endif + s2mm005_manual_LPM(usbpd_data, 0xB); + mdelay(110); + } + s2mm005_reset(usbpd_data); + } + } +} + +#if defined(CONFIG_PM) +static int s2mm005_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + pr_info("%s:%s\n", USBPD005_DEV_NAME, __func__); +#endif /* CONFIG_SAMSUNG_PRODUCT_SHIP */ + + if (device_may_wakeup(dev)) + enable_irq_wake(usbpd_data->irq); + + disable_irq(usbpd_data->irq); + + return 0; +} + +static int s2mm005_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + pr_info("%s:%s\n", USBPD005_DEV_NAME, __func__); +#endif /* CONFIG_SAMSUNG_PRODUCT_SHIP */ + + if (device_may_wakeup(dev)) + disable_irq_wake(usbpd_data->irq); + + enable_irq(usbpd_data->irq); + + return 0; +} +#else +#define s2mm005_suspend NULL +#define s2mm005_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id s2mm005_usbpd_id[] = { + { USBPD005_DEV_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, s2mm005_usbpd_id); + +#if defined(CONFIG_OF) +static struct of_device_id s2mm005_i2c_dt_ids[] = { + { .compatible = "sec-s2mm005,i2c" }, + { } +}; +#endif /* CONFIG_OF */ + +#if defined(CONFIG_PM) +const struct dev_pm_ops s2mm005_pm = { + .suspend = s2mm005_suspend, + .resume = s2mm005_resume, +}; +#endif /* CONFIG_PM */ + +static struct i2c_driver s2mm005_usbpd_driver = { + .driver = { + .name = USBPD005_DEV_NAME, +#if defined(CONFIG_PM) + .pm = &s2mm005_pm, +#endif /* CONFIG_PM */ +#if defined(CONFIG_OF) + .of_match_table = s2mm005_i2c_dt_ids, +#endif /* CONFIG_OF */ + }, + .probe = s2mm005_usbpd_probe, + //.remove = __devexit_p(s2mm005_usbpd_remove), + .remove = s2mm005_usbpd_remove, + .shutdown = s2mm005_usbpd_shutdown, + .id_table = s2mm005_usbpd_id, +}; + +static int __init s2mm005_usbpd_init(void) +{ + return i2c_add_driver(&s2mm005_usbpd_driver); +} +module_init(s2mm005_usbpd_init); + +static void __exit s2mm005_usbpd_exit(void) +{ + i2c_del_driver(&s2mm005_usbpd_driver); +} +module_exit(s2mm005_usbpd_exit); + +MODULE_DESCRIPTION("s2mm005 USB PD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ccic/s2mm005_cc.c b/drivers/ccic/s2mm005_cc.c new file mode 100644 index 000000000000..d0831c48e446 --- /dev/null +++ b/drivers/ccic/s2mm005_cc.c @@ -0,0 +1,1339 @@ +/* + * driver/../s2mm005.c - S2MM005 USB CC function driver + * + * Copyright (C) 2015 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#if defined(CONFIG_CCIC_NOTIFIER) +#include +#endif +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +#include +#endif +#include +#if defined(CONFIG_COMBO_REDRIVER) +#include +#endif + +#if defined(CONFIG_BATTERY_SAMSUNG) +extern unsigned int lpcharge; +#endif + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +#include +#elif defined(CONFIG_TYPEC) +#include +#endif + +int CC_DIR; +EXPORT_SYMBOL(CC_DIR); + +extern struct pdic_notifier_struct pd_noti; +//////////////////////////////////////////////////////////////////////////////// +// function definition +//////////////////////////////////////////////////////////////////////////////// +void process_cc_water(void * data, LP_STATE_Type *Lp_DATA); +void process_cc_attach(void * data, u8 *plug_attach_done); +void process_cc_detach(void * data); +#if defined(CONFIG_TYPEC) +void process_message_role(void *data); +#endif +void process_cc_get_int_status(void *data, uint32_t *pPRT_MSG, MSG_IRQ_STATUS_Type *MSG_IRQ_State); +void process_cc_rid(void * data); +void ccic_event_work(void *data, int dest, int id, int attach, int event, int sub); +void process_cc_water_det(void * data); + +#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO +extern int muic_GPIO_control(int gpio); +#endif +#if defined(CONFIG_MUIC_SUPPORT_KEYBOARDDOCK) +extern void muic_ADC_rescan(void); +int adc_rescan_done = 0; +#endif + +#if defined(CONFIG_USB_DWC3) +extern void dwc3_set_selfpowered(u8 enable); +#endif + +//////////////////////////////////////////////////////////////////////////////// +// modified by khoonk 2015.05.18 +//////////////////////////////////////////////////////////////////////////////// +// s2mm005.c --> s2mm005_cc.h +//////////////////////////////////////////////////////////////////////////////// +static char MSG_IRQ_Print[32][40] = +{ + {"bFlag_Ctrl_Reserved"}, + {"bFlag_Ctrl_GoodCRC"}, + {"bFlag_Ctrl_GotoMin"}, + {"bFlag_Ctrl_Accept"}, + {"bFlag_Ctrl_Reject"}, + {"bFlag_Ctrl_Ping"}, + {"bFlag_Ctrl_PS_RDY"}, + {"bFlag_Ctrl_Get_Source_Cap"}, + {"bFlag_Ctrl_Get_Sink_Cap"}, + {"bFlag_Ctrl_DR_Swap"}, + {"bFlag_Ctrl_PR_Swap"}, + {"bFlag_Ctrl_VCONN_Swap"}, + {"bFlag_Ctrl_Wait"}, + {"bFlag_Ctrl_Soft_Reset"}, + {"bFlag_Ctrl_Reserved_b14"}, + {"bFlag_Ctrl_Reserved_b15"}, + {"bFlag_Data_Reserved_b16"}, + {"bFlag_Data_SRC_Capability"}, + {"bFlag_Data_Request"}, + {"bFlag_Data_BIST"}, + {"bFlag_Data_SNK_Capability"}, + {"bFlag_Data_Reserved_05"}, + {"bFlag_Data_Reserved_06"}, + {"bFlag_Data_Reserved_07"}, + {"bFlag_Data_Reserved_08"}, + {"bFlag_Data_Reserved_09"}, + {"bFlag_Data_Reserved_10"}, + {"bFlag_Data_Reserved_11"}, + {"bFlag_Data_Reserved_12"}, + {"bFlag_Data_Reserved_13"}, + {"bFlag_Data_Reserved_14"}, + {"bFlag_Data_Vender_Defined"}, +}; + +#if defined(CONFIG_CCIC_NOTIFIER) +static void ccic_event_notifier(struct work_struct *data) +{ + struct ccic_state_work *event_work = + container_of(data, struct ccic_state_work, ccic_work); + CC_NOTI_TYPEDEF ccic_noti; + + switch(event_work->dest){ + case CCIC_NOTIFY_DEV_USB : + pr_info("usb:%s, dest=%s, id=%s, attach=%s, drp=%s\n", __func__, + CCIC_NOTI_DEST_Print[event_work->dest], + CCIC_NOTI_ID_Print[event_work->id], + event_work->sub1? "Attached": "Detached", + CCIC_NOTI_USB_STATUS_Print[event_work->sub2]); + break; + default : + pr_info("usb:%s, dest=%s, id=%s, sub1=%d, sub2=%d, sub3=%d\n", __func__, + CCIC_NOTI_DEST_Print[event_work->dest], + CCIC_NOTI_ID_Print[event_work->id], + event_work->sub1, + event_work->sub2, + event_work->sub3); + break; + } + + ccic_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_noti.dest = event_work->dest; + ccic_noti.id = event_work->id; + ccic_noti.sub1 = event_work->sub1; + ccic_noti.sub2 = event_work->sub2; + ccic_noti.sub3 = event_work->sub3; +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + ccic_noti.pd = &pd_noti; +#endif + ccic_notifier_notify((CC_NOTI_TYPEDEF*)&ccic_noti, NULL, 0); + + kfree(event_work); +} + +void ccic_event_work(void *data, int dest, int id, int attach, int event, int sub) +{ + struct s2mm005_data *usbpd_data = data; + struct ccic_state_work * event_work; + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +#if defined(CONFIG_USB_HOST_NOTIFY) + struct otg_notify *o_notify = get_otg_notify(); +#endif +#elif defined(CONFIG_TYPEC) + struct typec_partner_desc desc; + enum typec_pwr_opmode mode = TYPEC_PWR_MODE_USB; +#endif + + pr_info("usb: %s\n", __func__); + event_work = kmalloc(sizeof(struct ccic_state_work), GFP_ATOMIC); + INIT_WORK(&event_work->ccic_work, ccic_event_notifier); + + event_work->dest = dest; + event_work->id = id; + event_work->sub1 = attach; + event_work->sub2 = event; + event_work->sub3 = sub; + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (id == CCIC_NOTIFY_ID_USB) { + pr_info("usb: %s, dest=%d, sub2(event)=%d, usbpd_data->data_role=%d, usbpd_data->try_state_change=%d\n", + __func__, dest, event, usbpd_data->data_role, usbpd_data->try_state_change); +#if defined(CONFIG_USB_HOST_NOTIFY) + if(sub1 == CCIC_NOTIFY_ATTACH && event == USB_STATUS_NOTIFY_ATTACH_DFP && o_notify) + { + o_notify->host_super = 0; + o_notify->host_high = 0; + init_waitqueue_head(&o_notify->host_device_recognition_wait_q); + } +#endif + usbpd_data->data_role = event; + if (usbpd_data->dual_role != NULL) + dual_role_instance_changed(usbpd_data->dual_role); + + if (usbpd_data->try_state_change && + (usbpd_data->data_role != USB_STATUS_NOTIFY_DETACH)) { + // Role change try and new mode detected + pr_info("usb: %s, reverse_completion\n", __func__); + complete(&usbpd_data->reverse_completion); + } +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if(!event_work->sub3) { + if(usbpd_data->dp_is_connect) + event_work->sub3 = 1; + } +#endif + } + else if (id == CCIC_NOTIFY_ID_ROLE_SWAP ) { + if (usbpd_data->dual_role != NULL) + dual_role_instance_changed(usbpd_data->dual_role); + } +#elif defined(CONFIG_TYPEC) + if (id == CCIC_NOTIFY_ID_USB) { + if (usbpd_data->partner == NULL) { + pr_info("%s: typec_register_partner power_role=%d data_role=%d event=%d", + __func__, usbpd_data->typec_power_role,usbpd_data->typec_data_role, event); + if (event == USB_STATUS_NOTIFY_ATTACH_UFP) { + mode = s2mm005_get_pd_support(usbpd_data); + typec_set_pwr_opmode(usbpd_data->port, mode); + desc.usb_pd = mode == TYPEC_PWR_MODE_PD; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ + desc.identity = NULL; + usbpd_data->typec_data_role = TYPEC_DEVICE; + typec_set_data_role(usbpd_data->port, TYPEC_DEVICE); + usbpd_data->partner = typec_register_partner(usbpd_data->port, &desc); + } else if (event == USB_STATUS_NOTIFY_ATTACH_DFP) { + mode = s2mm005_get_pd_support(usbpd_data); + typec_set_pwr_opmode(usbpd_data->port, mode); + desc.usb_pd = mode == TYPEC_PWR_MODE_PD; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ + desc.identity = NULL; + usbpd_data->typec_data_role = TYPEC_HOST; + typec_set_data_role(usbpd_data->port, TYPEC_HOST); + usbpd_data->partner = typec_register_partner(usbpd_data->port, &desc); + } else + pr_info("%s detach case\n", __func__); + }else { + pr_info("%s: data_role changed, power_role=%d data_role=%d, event=%d", + __func__, usbpd_data->typec_power_role,usbpd_data->typec_data_role, event); + if (event == USB_STATUS_NOTIFY_ATTACH_UFP) { + usbpd_data->typec_data_role = TYPEC_DEVICE; + typec_set_data_role(usbpd_data->port, usbpd_data->typec_data_role); + } else if (event == USB_STATUS_NOTIFY_ATTACH_DFP) { + usbpd_data->typec_data_role = TYPEC_HOST; + typec_set_data_role(usbpd_data->port, usbpd_data->typec_data_role); + } else + pr_info("%s detach case\n", __func__); + } + if (usbpd_data->typec_try_state_change && + (event != USB_STATUS_NOTIFY_DETACH)) { + // Role change try and new mode detected + pr_info("usb: %s, typec_reverse_completion\n", __func__); + complete(&usbpd_data->typec_reverse_completion); + } + } +#endif + queue_work(usbpd_data->ccic_wq, &event_work->ccic_work); +} +#endif + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +void role_swap_check(struct work_struct *wk) +{ + struct delayed_work *delay_work = + container_of(wk, struct delayed_work, work); + struct s2mm005_data *usbpd_data = + container_of(delay_work, struct s2mm005_data, role_swap_work); + int mode; + + pr_info("%s: ccic_set_dual_role check again usbpd_data->pd_state=%d\n", + __func__, usbpd_data->pd_state); + + usbpd_data->try_state_change = 0; + + if (usbpd_data->pd_state == State_PE_Initial_detach) { + pr_err("%s: ccic_set_dual_role reverse failed, set mode to DRP\n", __func__); + disable_irq(usbpd_data->irq); + /* exit from Disabled state and set mode to DRP */ + mode = TYPE_C_ATTACH_DRP; + s2mm005_rprd_mode_change(usbpd_data, mode); + enable_irq(usbpd_data->irq); + } +} + +static int ccic_set_dual_role(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val) +{ + struct s2mm005_data *usbpd_data = dual_role_get_drvdata(dual_role); + struct i2c_client *i2c; + + USB_STATUS attached_state; + int mode; + int timeout = 0; + int ret = 0; + + if (!usbpd_data) { + pr_err("%s : usbpd_data is null \n", __func__); + return -EINVAL; + } + + i2c = usbpd_data->i2c; + + // Get Current Role // + attached_state = usbpd_data->data_role; + pr_info("%s : request prop = %d , attached_state = %d\n", __func__, prop, attached_state); + + if (attached_state != USB_STATUS_NOTIFY_ATTACH_DFP + && attached_state != USB_STATUS_NOTIFY_ATTACH_UFP) { + pr_err("%s : current mode : %d - just return \n",__func__, attached_state); + return 0; + } + + if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP + && *val == DUAL_ROLE_PROP_MODE_DFP) { + pr_err("%s : current mode : %d - request mode : %d just return \n", + __func__, attached_state, *val); + return 0; + } + + if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP + && *val == DUAL_ROLE_PROP_MODE_UFP) { + pr_err("%s : current mode : %d - request mode : %d just return \n", + __func__, attached_state, *val); + return 0; + } + + if ( attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) { + /* Current mode DFP and Source */ + pr_info("%s: try reversing, from Source to Sink\n", __func__); + /* turns off VBUS first */ + vbus_turn_on_ctrl(0); +#if defined(CONFIG_CCIC_NOTIFIER) + /* muic */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*sub1:attach*/, 0/*sub2:rprd*/, 0/*sub3:reserved*/); +#endif + /* exit from Disabled state and set mode to UFP */ + mode = TYPE_C_ATTACH_UFP; + usbpd_data->try_state_change = TYPE_C_ATTACH_UFP; + s2mm005_rprd_mode_change(usbpd_data, mode); + } else { + // Current mode UFP and Sink // + pr_info("%s: try reversing, from Sink to Source\n", __func__); + /* exit from Disabled state and set mode to UFP */ + mode = TYPE_C_ATTACH_DFP; + usbpd_data->try_state_change = TYPE_C_ATTACH_DFP; + s2mm005_rprd_mode_change(usbpd_data, mode); + } + + reinit_completion(&usbpd_data->reverse_completion); + timeout = + wait_for_completion_timeout(&usbpd_data->reverse_completion, + msecs_to_jiffies + (DUAL_ROLE_SET_MODE_WAIT_MS)); + + if (!timeout) { + usbpd_data->try_state_change = 0; + pr_err("%s: reverse failed, set mode to DRP\n", __func__); + disable_irq(usbpd_data->irq); + /* exit from Disabled state and set mode to DRP */ + mode = TYPE_C_ATTACH_DRP; + s2mm005_rprd_mode_change(usbpd_data, mode); + enable_irq(usbpd_data->irq); + ret = -EIO; + } else { + pr_err("%s: reverse success, one more check\n", __func__); + schedule_delayed_work(&usbpd_data->role_swap_work, msecs_to_jiffies(DUAL_ROLE_SET_MODE_WAIT_MS)); + } + + dev_info(&i2c->dev, "%s -> data role : %d\n", __func__, *val); + return ret; +} + +/* Decides whether userspace can change a specific property */ +int dual_role_is_writeable(struct dual_role_phy_instance *drp, + enum dual_role_property prop) +{ + if (prop == DUAL_ROLE_PROP_MODE) + return 1; + else + return 0; +} + +/* Callback for "cat /sys/class/dual_role_usb/otg_default/" */ +int dual_role_get_local_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val) +{ + struct s2mm005_data *usbpd_data = dual_role_get_drvdata(dual_role); + + USB_STATUS attached_state; + int power_role; + + if (!usbpd_data) { + pr_err("%s : usbpd_data is null : request prop = %d \n",__func__, prop); + return -EINVAL; + } + attached_state = usbpd_data->data_role; + power_role = usbpd_data->power_role; + + pr_info("%s : request prop = %d , attached_state = %d, power_role = %d\n", + __func__, prop, attached_state, power_role); + + if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) { + if (prop == DUAL_ROLE_PROP_MODE) + *val = DUAL_ROLE_PROP_MODE_DFP; + else if (prop == DUAL_ROLE_PROP_PR) + *val = power_role; + else if (prop == DUAL_ROLE_PROP_DR) + *val = DUAL_ROLE_PROP_DR_HOST; + else + return -EINVAL; + } else if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP) { + if (prop == DUAL_ROLE_PROP_MODE) + *val = DUAL_ROLE_PROP_MODE_UFP; + else if (prop == DUAL_ROLE_PROP_PR) + *val = power_role; + else if (prop == DUAL_ROLE_PROP_DR) + *val = DUAL_ROLE_PROP_DR_DEVICE; + else + return -EINVAL; + } else { + if (prop == DUAL_ROLE_PROP_MODE) + *val = DUAL_ROLE_PROP_MODE_NONE; + else if (prop == DUAL_ROLE_PROP_PR) + *val = DUAL_ROLE_PROP_PR_NONE; + else if (prop == DUAL_ROLE_PROP_DR) + *val = DUAL_ROLE_PROP_DR_NONE; + else + return -EINVAL; + } + + return 0; +} + +/* Callback for "echo > + * /sys/class/dual_role_usb//" + * Block until the entire final state is reached. + * Blocking is one of the better ways to signal when the operation + * is done. + * This function tries to switch to Attached.SRC or Attached.SNK + * by forcing the mode into SRC or SNK. + * On failure, we fall back to Try.SNK state machine. + */ +int dual_role_set_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val) +{ + pr_info("%s : request prop = %d , *val = %d \n",__func__, prop, *val); + if (prop == DUAL_ROLE_PROP_MODE) + return ccic_set_dual_role(dual_role, prop, val); + else + return -EINVAL; +} +#elif defined(CONFIG_TYPEC) +int s2mm005_dr_set(const struct typec_capability *cap, enum typec_data_role role) +{ + struct s2mm005_data *usbpd_data = container_of(cap, struct s2mm005_data, typec_cap); + if (!usbpd_data) + return -EINVAL; + + pr_info("%s : typec_power_role=%d, typec_data_role=%d, role=%d\n", __func__, + usbpd_data->typec_power_role, usbpd_data->typec_data_role, role); + + if (usbpd_data->typec_data_role != TYPEC_DEVICE + && usbpd_data->typec_data_role != TYPEC_HOST) + return -EPERM; + else if (usbpd_data->typec_data_role == role) + return -EPERM; + + reinit_completion(&usbpd_data->typec_reverse_completion); + if (role == TYPEC_DEVICE) { + pr_info("%s :try reversing, from DFP to UFP\n", __func__); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_DR; + usbpd_data->is_dr_swap++; + send_role_swap_message(usbpd_data, 1); + } else if (role == TYPEC_HOST) { + pr_info("%s :try reversing, from UFP to DFP\n", __func__); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_DR; + usbpd_data->is_dr_swap++; + send_role_swap_message(usbpd_data, 1); + } else { + pr_info("%s :invalid typec_role\n", __func__); + return -EIO; + } + + if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion, + msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) { + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; + return -ETIMEDOUT; + } + + return 0; +} + +int s2mm005_pr_set(const struct typec_capability *cap, enum typec_role role) +{ + struct s2mm005_data *usbpd_data = container_of(cap, struct s2mm005_data, typec_cap); + + if (!usbpd_data) + return -EINVAL; + + pr_info("%s : typec_power_role=%d, typec_data_role=%d, role=%d\n", __func__, + usbpd_data->typec_power_role, usbpd_data->typec_data_role, role); + + if (usbpd_data->typec_power_role != TYPEC_SINK + && usbpd_data->typec_power_role != TYPEC_SOURCE) + return -EPERM; + else if (usbpd_data->typec_power_role == role) + return -EPERM; + + reinit_completion(&usbpd_data->typec_reverse_completion); + if (role == TYPEC_SINK) { + pr_info("%s :try reversing, from Source to Sink\n", __func__); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_PR; + usbpd_data->is_pr_swap++; + send_role_swap_message(usbpd_data, 0); + } else if (role == TYPEC_SOURCE) { + pr_info("%s :try reversing, from Sink to Source\n", __func__); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_PR; + usbpd_data->is_pr_swap++; + send_role_swap_message(usbpd_data, 0); + } else { + pr_info("%s :invalid typec_role\n", __func__); + return -EIO; + } + + if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion, + msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) { + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; + if (usbpd_data->typec_power_role != role) + return -ETIMEDOUT; + } + + return 0; +} + +void typec_role_swap_check(struct work_struct *wk) +{ + struct delayed_work *delay_work = + container_of(wk, struct delayed_work, work); + struct s2mm005_data *usbpd_data = + container_of(delay_work, struct s2mm005_data, typec_role_swap_work); + + pr_info("%s: s2mm005_port_type_set check again usbpd_data->pd_state=%d\n", + __func__, usbpd_data->pd_state); + + usbpd_data->typec_try_state_change = 0; + + if (usbpd_data->pd_state == State_PE_Initial_detach) { + pr_err("%s: ccic_set_dual_role reverse failed, set mode to DRP\n", __func__); + disable_irq(usbpd_data->irq); + /* exit from Disabled state and set mode to DRP */ + s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP); + enable_irq(usbpd_data->irq); + } +} + +int s2mm005_port_type_set(const struct typec_capability *cap, enum typec_port_type port_type) +{ + struct s2mm005_data *usbpd_data = container_of(cap, struct s2mm005_data, typec_cap); + int timeout = 0; + + if (!usbpd_data) { + pr_err("%s : usbpd_data is null\n", __func__); + return -EINVAL; + } + + pr_info("%s : typec_power_role=%d, typec_data_role=%d, port_type=%d\n", + __func__, usbpd_data->typec_power_role, usbpd_data->typec_data_role, port_type); + + switch (port_type) { + case TYPEC_PORT_DFP: + pr_info("%s : try reversing, from UFP(Sink) to DFP(Source)\n", __func__); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_TYPE; + s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DFP); + break; + case TYPEC_PORT_UFP: + pr_info("%s : try reversing, from DFP(Source) to UFP(Sink)\n", __func__); + /* turns off VBUS first */ + vbus_turn_on_ctrl(0); +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, + 0/*attach*/, 0/*rprd*/, 0); +#endif + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_TYPE; + s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_UFP); + break; + case TYPEC_PORT_DRP: + pr_info("%s : set to DRP\n", __func__); + return 0; + default : + pr_info("%s : invalid typec_role\n", __func__); + return -EINVAL; + } + + if (usbpd_data->typec_try_state_change) { + reinit_completion(&usbpd_data->typec_reverse_completion); + timeout = + wait_for_completion_timeout(&usbpd_data->typec_reverse_completion, + msecs_to_jiffies + (DUAL_ROLE_SET_MODE_WAIT_MS)); + + if (!timeout) { + pr_err("%s: reverse failed, set mode to DRP\n", __func__); + disable_irq(usbpd_data->irq); + /* exit from Disabled state and set mode to DRP */ + usbpd_data->typec_try_state_change = 0; + s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP); + enable_irq(usbpd_data->irq); + return -EIO; + } else { + pr_err("%s: reverse success, one more check\n", __func__); + schedule_delayed_work(&usbpd_data->typec_role_swap_work, msecs_to_jiffies(DUAL_ROLE_SET_MODE_WAIT_MS)); + } + } + + return 0; +} + +int s2mm005_get_pd_support(struct s2mm005_data *usbpd_data) +{ + bool support_pd_role_swap = false; + struct device_node *np = NULL; + + np = of_find_compatible_node(NULL, NULL, "sec-s2mm005,i2c"); + + if (np) + support_pd_role_swap = of_property_read_bool(np, "support_pd_role_swap"); + else + pr_info("%s : np is null\n", __func__); + + pr_info("%s : TYPEC_CLASS: support_pd_role_swap is %d, usbc_data->pd_support : %d\n", __func__, + support_pd_role_swap, usbpd_data->pd_support); + + if (support_pd_role_swap && usbpd_data->pd_support) + return TYPEC_PWR_MODE_PD; + + return usbpd_data->pwr_opmode; +} +#endif + +void process_cc_water_det(void * data) +{ + struct s2mm005_data *usbpd_data = data; + + pr_info("%s\n",__func__); + s2mm005_int_clear(usbpd_data); // interrupt clear +#if defined(CONFIG_SEC_FACTORY) + if(!usbpd_data->fac_water_enable) +#endif + { + if(usbpd_data->water_det) + s2mm005_manual_LPM(usbpd_data, 0x9); + } +} + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +void dp_detach(void *data) +{ + struct s2mm005_data *usbpd_data = data; + pr_info("%s: dp_is_connect %d\n",__func__, usbpd_data->dp_is_connect); + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_USB_DP, + CCIC_NOTIFY_ID_USB_DP, 0/*attach*/, usbpd_data->dp_hs_connect/*drp*/, 0); + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_DP, + CCIC_NOTIFY_ID_DP_CONNECT, 0/*attach*/, 0/*drp*/, 0); + + usbpd_data->dp_is_connect = 0; + usbpd_data->dp_hs_connect = 0; + usbpd_data->is_sent_pin_configuration = 0; + return; +} +#endif + +//////////////////////////////////////////// //////////////////////////////////// +// Moisture detection processing +//////////////////////////////////////////////////////////////////////////////// +void process_cc_water(void * data, LP_STATE_Type *Lp_DATA) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint32_t R_len; + uint16_t REG_ADD; + u8 R_DATA[1]; + int i; + + pr_info("%s\n",__func__); + /* read reg for water and dry state */ + for(i=0; i<1; i++){ + R_DATA[0] = 0x00; + REG_ADD = 0x8; + s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read + } + REG_ADD = 0x60; + R_len = 4; + s2mm005_read_byte(i2c, REG_ADD, Lp_DATA->BYTE, R_len); + dev_info(&i2c->dev, "%s: WATER reg:0x%02X WATER=%d DRY=%d\n", __func__, + Lp_DATA->BYTE[0], + Lp_DATA->BITS.WATER_DET, + Lp_DATA->BITS.RUN_DRY); + +#if defined(CONFIG_BATTERY_SAMSUNG) + if (lpcharge) { + dev_info(&i2c->dev, "%s: BOOTING_RUN_DRY=%d\n", __func__, + Lp_DATA->BITS.BOOTING_RUN_DRY); + usbpd_data->booting_run_dry = Lp_DATA->BITS.BOOTING_RUN_DRY; + } +#endif + +#if defined(CONFIG_SEC_FACTORY) + if (!Lp_DATA->BITS.WATER_DET) { + Lp_DATA->BITS.RUN_DRY = 1; + } +#endif + + /* check for dry case */ + if (Lp_DATA->BITS.RUN_DRY && !usbpd_data->run_dry) { + dev_info(&i2c->dev, "== WATER RUN-DRY DETECT ==\n"); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER, + 0/*attach*/, 0, 0); + } + + usbpd_data->run_dry = Lp_DATA->BITS.RUN_DRY; + + /* check for water case */ + if ((Lp_DATA->BITS.WATER_DET & !usbpd_data->water_det)) { + dev_info(&i2c->dev, "== WATER DETECT ==\n"); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER, + 1/*attach*/, 0, 0); + } + + usbpd_data->water_det = Lp_DATA->BITS.WATER_DET; +} +//////////////////////////////////////////////////////////////////////////////// +// ATTACH processing +//////////////////////////////////////////////////////////////////////////////// +void process_cc_attach(void * data,u8 *plug_attach_done) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + LP_STATE_Type Lp_DATA; + FUNC_STATE_Type Func_DATA; + static int prev_pd_state = State_PE_Initial_detach; + uint32_t R_len; + uint16_t REG_ADD; + struct otg_notify *o_notify = get_otg_notify(); +#if defined(CONFIG_CCIC_S2MM005_ANALOG_AUDIO) + static int earphone_state = 0; +#endif + int is_dfp = 0; + int is_src = 0; + + pr_info("%s\n",__func__); + + // Check for moisture + process_cc_water(usbpd_data, &Lp_DATA); + + if (usbpd_data->water_det || !usbpd_data->run_dry) { + /* Moisture detection is only handled in the disconnected state(LPM). */ + return; + } else if(!usbpd_data->booting_run_dry) { + dev_info(&i2c->dev, " Water? No Dry\n"); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER, 1/*attach*/, 0, 0); + + REG_ADD = 0x20; + R_len = 4; + s2mm005_read_byte(i2c, REG_ADD, Func_DATA.BYTE, R_len); + dev_info(&i2c->dev, "Rsvd_H:0x%02X PD_Nxt_State:0x%02X Rsvd_L:0x%02X PD_State:%02d\n", + Func_DATA.BYTES.RSP_BYTE2, + Func_DATA.BYTES.PD_Next_State, + Func_DATA.BYTES.RSP_BYTE1, + Func_DATA.BYTES.PD_State); + + return; + } else { + REG_ADD = 0x20; + R_len = 4; + + s2mm005_read_byte(i2c, REG_ADD, Func_DATA.BYTE, R_len); + dev_info(&i2c->dev, "Rsvd_H:0x%02X PD_Nxt_State:0x%02X Rsvd_L:0x%02X PD_State:%02d\n", + Func_DATA.BYTES.RSP_BYTE2, + Func_DATA.BYTES.PD_Next_State, + Func_DATA.BYTES.RSP_BYTE1, + Func_DATA.BYTES.PD_State); + + dev_info(&i2c->dev, "CC direction info: %d\n", Func_DATA.BYTES.RSP_BYTE1); +// CC_DIR = Func_DATA.BYTES.RSP_BYTE1; + if (Func_DATA.BYTES.RSP_BYTE1 == 66) + CC_DIR = 1; + else CC_DIR = 0; + dev_info(&i2c->dev, "CC_DIR: %d\n", CC_DIR); + +#if defined(CONFIG_USB_HW_PARAM) + if (!usbpd_data->pd_state && Func_DATA.BYTES.PD_State && Func_DATA.BITS.VBUS_CC_Short) + inc_hw_param(o_notify, USB_CCIC_VBUS_CC_SHORT_COUNT); +#endif + usbpd_data->pd_state = Func_DATA.BYTES.PD_State; + usbpd_data->func_state = Func_DATA.DATA; + + is_dfp = usbpd_data->func_state & (0x1 << 26) ? 1 : 0; + is_src = usbpd_data->func_state & (0x1 << 25) ? 1 : 0; + dev_info(&i2c->dev, "func_state :0x%X, is_dfp : %d, is_src : %d\n", usbpd_data->func_state, \ + is_dfp, is_src); + + if (Func_DATA.BITS.RESET) { + dev_info(&i2c->dev, "ccic reset detected\n"); + if (!Lp_DATA.BITS.AUTO_LP_ENABLE_BIT) { + /* AUTO LPM Enable */ + s2mm005_manual_LPM(usbpd_data, 6); + } + + set_enable_alternate_mode(ALTERNATE_MODE_START); + } + if (usbpd_data->pd_state == State_PE_SRC_Wait_New_Capabilities && Lp_DATA.BITS.Sleep_Cable_Detect) { + s2mm005_manual_LPM(usbpd_data, 0x0D); + return; + } +#if defined(CONFIG_CCIC_S2MM005_ANALOG_AUDIO) + dev_info(&i2c->dev, "%s : Lp_DATA.BITS.ACC_DETECTION:%d\n", __func__, Lp_DATA.BITS.ACC_DETECTION); + + if (usbpd_data->pd_state == State_PE_SRC_Wait_New_Capabilities && Lp_DATA.BITS.ACC_DETECTION) { + dev_info(&i2c->dev, "Type-C Earjack detected\n"); + s2mm005_manual_ACC_LPM(usbpd_data); + return; + } +#if 0 // for a8s + dev_info(&i2c->dev, "Func_DATA.BITS.ATTACH_DONE:0x%02X Func_DATA.BITS.IS_SOURCE:0x%02X\n", + Func_DATA.BITS.ATTACH_DONE, Func_DATA.BITS.IS_SOURCE); + if(Func_DATA.BITS.ATTACH_DONE == 1 && Func_DATA.BITS.IS_SOURCE == 1 && Func_DATA.BYTES.RSP_BYTE1 == 0x44){ + dev_info(&i2c->dev, "Type-C Analog Headset detected\n"); + /* muic */ + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_EARJACK, 1/*attach*/, 0/*rprd*/, 0); + /* audio */ + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_AUDIO, CCIC_NOTIFY_ID_EARJACK, 1/*attach*/, 0/*rprd*/, 0); + earphone_state = 1; + return; + } +#endif +#endif + } +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + store_usblog_notify(NOTIFY_FUNCSTATE, (void*)&usbpd_data->pd_state, NULL); +#endif + + if(usbpd_data->pd_state != State_PE_Initial_detach) + { + *plug_attach_done = 1; + usbpd_data->plug_rprd_sel = 1; + if (usbpd_data->pd_state == State_PE_PRS_SRC_SNK_Transition_to_off) { + pr_info("%s State_PE_PRS_SRC_SNK_Transition_to_off! \n", __func__); + vbus_turn_on_ctrl(0); + } else if (usbpd_data->pd_state == State_PE_PRS_SNK_SRC_Source_on) { + pr_info("%s State_PE_PRS_SNK_SRC_Source_on! \n", __func__); + pd_noti.event = PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC; + pd_noti.sink_status.selected_pdo_num = 0; + pd_noti.sink_status.available_pdo_num = 0; + pd_noti.sink_status.current_pdo_num = 0; + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY, + CCIC_NOTIFY_ID_POWER_STATUS, 0, 0, 0); + vbus_turn_on_ctrl(1); + } + +#if defined(CONFIG_TYPEC) + if (usbpd_data->pd_state == State_PE_SRC_Ready || usbpd_data->pd_state == State_PE_SNK_Ready) + { + usbpd_data->pd_support = true; +#if defined(CONFIG_USB_DWC3) + dwc3_set_selfpowered(1); +#endif + typec_set_pwr_opmode(usbpd_data->port, TYPEC_PWR_MODE_PD); +#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO + pr_info("%s call muic_GPIO_control(0)\n", __func__); + muic_GPIO_control(0); +#endif + } +#endif + + if (usbpd_data->is_dr_swap || usbpd_data->is_pr_swap) { + dev_info(&i2c->dev, "%s - ignore all pd_state by %s\n", __func__,(usbpd_data->is_dr_swap ? "dr_swap" : "pr_swap")); + return; + } + + switch (usbpd_data->pd_state) { + case State_PE_SRC_Send_Capabilities: + case State_PE_SRC_Negotiate_Capability: + case State_PE_SRC_Transition_Supply: + case State_PE_SRC_Ready: + case State_PE_SRC_Disabled: + dev_info(&i2c->dev, "%s %d: pd_state:%02d, is_host = %d, is_client = %d\n", + __func__, __LINE__, usbpd_data->pd_state, usbpd_data->is_host, usbpd_data->is_client); + if (usbpd_data->is_client == CLIENT_ON) { + dev_info(&i2c->dev, "%s %d: pd_state:%02d, turn off client\n", + __func__, __LINE__, usbpd_data->pd_state); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 0/*rprd*/, 0); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE; +#endif + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); + usbpd_data->is_client = CLIENT_OFF; + msleep(300); + } + if (usbpd_data->is_host == HOST_OFF) { + /* muic */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/, 0); + /* otg */ + usbpd_data->is_host = HOST_ON_BY_RD; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_SRC; +#elif defined(CONFIG_TYPEC) + usbpd_data->typec_power_role = TYPEC_SOURCE; + typec_set_pwr_role(usbpd_data->port, TYPEC_SOURCE); +#endif + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1); + /* add to turn on external 5V */ + vbus_turn_on_ctrl(1); + + if (is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST)) + s2mm005_set_upsm_mode(); + + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0); +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + // only start alternate mode at DFP state +// send_alternate_message(usbpd_data, VDM_DISCOVER_ID); + if (usbpd_data->acc_type != CCIC_DOCK_DETACHED) { + pr_info("%s: cancel_delayed_work_sync - pd_state : %d\n", __func__, usbpd_data->pd_state); + cancel_delayed_work_sync(&usbpd_data->acc_detach_work); + } +#endif + } + break; + case State_PE_SNK_Wait_for_Capabilities: + case State_PE_SNK_Evaluate_Capability: + case State_PE_SNK_Ready: + case State_ErrorRecovery: + dev_info(&i2c->dev, "%s %d: pd_state:%02d, is_host = %d, is_client = %d\n", + __func__, __LINE__, usbpd_data->pd_state, usbpd_data->is_host, usbpd_data->is_client); + + if (usbpd_data->is_host == HOST_ON_BY_RD) { + dev_info(&i2c->dev, "%s %d: pd_state:%02d, turn off host\n", + __func__, __LINE__, usbpd_data->pd_state); + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (usbpd_data->dp_is_connect == 1) { + dp_detach(usbpd_data); + } +#endif + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 1/*rprd*/, 0); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE; +#endif + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); + /* add to turn off external 5V */ + vbus_turn_on_ctrl(0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); + usbpd_data->is_host = HOST_OFF; + msleep(300); + } + + if (Lp_DATA.BITS.PDSTATE29_SBU_DONE) { + dev_info(&i2c->dev, "%s SBU check done\n", __func__); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, + 1/*attach*/, 0/*rprd*/, + (Func_DATA.BITS.VBUS_CC_Short || Func_DATA.BITS.VBUS_SBU_Short) ? Rp_Abnormal:Func_DATA.BITS.RP_CurrentLvl); + + if (Func_DATA.BITS.VBUS_CC_Short || Func_DATA.BITS.VBUS_SBU_Short) + usbpd_data->short_detected = true; + else + usbpd_data->short_detected = false; + + dev_info(&i2c->dev, "%s short_detected: %s\n", __func__, usbpd_data->short_detected ? "true" : "false"); + } else { + /* muic */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, + 1/*attach*/, 0/*rprd*/, Rp_Sbu_check); + } + + if (usbpd_data->is_client == CLIENT_OFF && usbpd_data->is_host == HOST_OFF) { + /* usb */ + usbpd_data->is_client = CLIENT_ON; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_SNK; +#elif defined(CONFIG_TYPEC) + usbpd_data->typec_power_role = TYPEC_SINK; + typec_set_pwr_role(usbpd_data->port, TYPEC_SINK); +#endif + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/, 0); + } + break; + default : + break; + } + } else { + *plug_attach_done = 0; + usbpd_data->plug_rprd_sel = 0; + usbpd_data->is_dr_swap = 0; + usbpd_data->is_pr_swap = 0; + usbpd_data->short_detected = false; +#if defined(CONFIG_CCIC_S2MM005_ANALOG_AUDIO) + if(earphone_state == 1){ + printk("%s : Type-C Analog Headset detached\n",__func__); + + /* muic */ + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_EARJACK, 0/*attach*/, 0/*rprd*/, 0); + /* audio */ + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_AUDIO, CCIC_NOTIFY_ID_EARJACK, 0/*attach*/, 0/*rprd*/, 0); + earphone_state = 0; + return; + } +#endif + if(prev_pd_state == State_PE_Initial_detach) // if detach -> detach event is ignored + { + printk("%s : detach event is ignored\n",__func__); + return; + } + /* muic */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 0/*rprd*/, 0); +#if defined(CONFIG_COMBO_REDRIVER) + ptn36502_config(SAFE_STATE, 0); +#endif + + if(usbpd_data->is_host > HOST_OFF || usbpd_data->is_client > CLIENT_OFF) { +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (usbpd_data->dp_is_connect == 1) { + dp_detach(usbpd_data); + } + + if (usbpd_data->acc_type != CCIC_DOCK_DETACHED) { + pr_info("%s: schedule_delayed_work - pd_state : %d\n", __func__, usbpd_data->pd_state); + if (usbpd_data->acc_type == CCIC_DOCK_HMT) { + schedule_delayed_work(&usbpd_data->acc_detach_work, msecs_to_jiffies(GEAR_VR_DETACH_WAIT_MS)); +// } +// else if(usbpd_data->acc_type == CCIC_DOCK_DP) { +// acc_detach_process(usbpd_data); + }else { + // Changed the sequence of acc detach work before calling usb detach for DP drvier panic problem in case if qcom by jjuny79.kim + schedule_delayed_work(&usbpd_data->acc_detach_work, msecs_to_jiffies(0)); + } + } +#endif +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if(usbpd_data->is_host > HOST_OFF || usbpd_data->power_role == DUAL_ROLE_PROP_PR_SRC) + vbus_turn_on_ctrl(0); +#elif defined(CONFIG_TYPEC) + if(usbpd_data->is_host > HOST_OFF || usbpd_data->typec_power_role == TYPEC_SOURCE) + vbus_turn_on_ctrl(0); +#endif + /* usb or otg */ + dev_info(&i2c->dev, "%s %d: pd_state:%02d, is_host = %d, is_client = %d\n", + __func__, __LINE__, usbpd_data->pd_state, usbpd_data->is_host, usbpd_data->is_client); + usbpd_data->is_host = HOST_OFF; + usbpd_data->is_client = CLIENT_OFF; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE; +#elif defined(CONFIG_TYPEC) + if (usbpd_data->partner) { + pr_info("%s : typec_unregister_partner\n", __func__); + if (!IS_ERR(usbpd_data->partner)) + typec_unregister_partner(usbpd_data->partner); + usbpd_data->partner = NULL; + usbpd_data->typec_power_role = TYPEC_SINK; + usbpd_data->typec_data_role = TYPEC_DEVICE; + usbpd_data->pwr_opmode = TYPEC_PWR_MODE_USB; + } + if (usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_PR || + usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_DR) { + /* Role change try and new mode detected */ + pr_info("%s : typec_reverse_completion, detached while pd_swap\n", __func__); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; + complete(&usbpd_data->typec_reverse_completion); + } +#endif + usbpd_data->pd_support = false; +#if defined(CONFIG_USB_DWC3) + dwc3_set_selfpowered(0); +#endif + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (!usbpd_data->try_state_change) + s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP); +#elif defined(CONFIG_TYPEC) + if (!usbpd_data->typec_try_state_change) + s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP); +#endif + } + usbpd_data->detach_done_wait = 1; + } + prev_pd_state = usbpd_data->pd_state; +} + +//////////////////////////////////////////// //////////////////////////////////// +// Detach processing +// 1. Used when the s2mm005 unbind case +//////////////////////////////////////////////////////////////////////////////// +void process_cc_detach(void * data) +{ + struct s2mm005_data *usbpd_data = data; + struct otg_notify *o_notify = get_otg_notify(); + if (usbpd_data->pd_state) { + usbpd_data->pd_state = State_PE_Initial_detach; +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 0/*rprd*/, 0); +#endif + if(usbpd_data->is_host > HOST_OFF) + vbus_turn_on_ctrl(0); + usbpd_data->is_host = HOST_OFF; + usbpd_data->is_client = CLIENT_OFF; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE; +#elif defined(CONFIG_TYPEC) + usbpd_data->typec_power_role = TYPEC_SINK; + typec_set_pwr_role(usbpd_data->port, TYPEC_SINK); + typec_set_data_role(usbpd_data->port, TYPEC_DEVICE); + typec_set_pwr_opmode(usbpd_data->port, TYPEC_PWR_MODE_USB); +#endif + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Get staus interrupt register +//////////////////////////////////////////////////////////////////////////////// +void process_cc_get_int_status(void *data, uint32_t *pPRT_MSG, MSG_IRQ_STATUS_Type *MSG_IRQ_State) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint8_t R_INT_STATUS[48]; + uint16_t REG_ADD; + uint32_t cnt; + uint32_t IrqPrint; + VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; + SSM_MSG_IRQ_STATUS_Type SSM_MSG_IRQ_State; + AP_REQ_GET_STATUS_Type AP_REQ_GET_State; + + pr_info("%s\n",__func__); + for(cnt = 0;cnt < 48;cnt++) + { + R_INT_STATUS[cnt] = 0; + } + + REG_ADD = 0x30; + s2mm005_read_byte(i2c, REG_ADD, R_INT_STATUS, 48); // sram : + + s2mm005_int_clear(usbpd_data); // interrupt clear + pPRT_MSG = (uint32_t *)&R_INT_STATUS[0]; + dev_info(&i2c->dev, "SYNC Status = 0x%08X\n",pPRT_MSG[0]); + dev_info(&i2c->dev, "CTRL MSG Status = 0x%08X\n",pPRT_MSG[1]); + dev_info(&i2c->dev, "DATA MSG Status = 0x%08X\n",pPRT_MSG[2]); + dev_info(&i2c->dev, "EXTD MSG Status = 0x%08X\n",pPRT_MSG[3]); + dev_info(&i2c->dev, "MSG IRQ Status = 0x%08X\n",pPRT_MSG[4]); + dev_info(&i2c->dev, "VDM IRQ Status = 0x%08X\n",pPRT_MSG[5]); + dev_info(&i2c->dev, "SSM_MSG IRQ Status = 0x%08X\n",pPRT_MSG[6]); + dev_info(&i2c->dev, "AP REQ GET Status = 0x%08X\n",pPRT_MSG[7]); + + dev_info(&i2c->dev, "0x50 IRQ Status = 0x%08X\n",pPRT_MSG[8]); + dev_info(&i2c->dev, "0x54 IRQ Status = 0x%08X\n",pPRT_MSG[9]); + dev_info(&i2c->dev, "0x58 IRQ Status = 0x%08X\n",pPRT_MSG[10]); + MSG_IRQ_State->DATA = pPRT_MSG[4]; + VDM_MSG_IRQ_State.DATA = pPRT_MSG[5]; + SSM_MSG_IRQ_State.DATA = pPRT_MSG[6]; + AP_REQ_GET_State.DATA = pPRT_MSG[7]; + +#if defined(CONFIG_SEC_FACTORY) + if((AP_REQ_GET_State.BYTES[0] >> 5) > 0) { + dev_info(&i2c->dev, "FAC: Repeat_State:%d, Repeat_RID:%d, RID0:%d\n", + AP_REQ_GET_State.BITS.FAC_Abnormal_Repeat_State, + AP_REQ_GET_State.BITS.FAC_Abnormal_Repeat_RID, + AP_REQ_GET_State.BITS.FAC_Abnormal_RID0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_CCIC, CCIC_NOTIFY_ID_FAC, + AP_REQ_GET_State.BYTES[0] >> 5, 0, 0); // b5~b7 + } +#endif + + IrqPrint = 1; + for(cnt=0;cnt<32;cnt++) + { + if((MSG_IRQ_State->DATA&IrqPrint) != 0) + { + dev_info(&i2c->dev, " - IRQ %s \n",&MSG_IRQ_Print[cnt][0]); + } + IrqPrint = (IrqPrint<<1); + } + if (MSG_IRQ_State->BITS.Ctrl_Flag_DR_Swap) + { + usbpd_data->is_dr_swap++; + dev_info(&i2c->dev, "is_dr_swap count : 0x%x\n", usbpd_data->is_dr_swap); +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (usbpd_data->dp_is_connect) + { + dev_info(&i2c->dev, "dr_swap is skiped, current status is dp mode !!\n"); + } + else +#endif + { + if (usbpd_data->is_host == HOST_ON_BY_RD) { + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); + msleep(300); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 0/*rprd*/,0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/, 0); + usbpd_data->is_host = HOST_OFF; + usbpd_data->is_client = CLIENT_ON; + } else if (usbpd_data->is_client == CLIENT_ON) { + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); + msleep(300); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/,0); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0); + usbpd_data->is_host = HOST_ON_BY_RD; + usbpd_data->is_client = CLIENT_OFF; + } + } + } + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if(VDM_MSG_IRQ_State.DATA) + receive_alternate_message(usbpd_data, &VDM_MSG_IRQ_State); + if(SSM_MSG_IRQ_State.BITS.Ssm_Flag_Unstructured_Data) + receive_unstructured_vdm_message(usbpd_data, &SSM_MSG_IRQ_State); + if(!AP_REQ_GET_State.BITS.Alt_Mode_By_I2C) + set_enable_alternate_mode(ALTERNATE_MODE_RESET); + if (!AP_REQ_GET_State.BITS.DPM_START_ON) + set_enable_alternate_mode(ALTERNATE_MODE_RESET); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// RID processing +//////////////////////////////////////////////////////////////////////////////// +void process_cc_rid(void *data) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + static int prev_rid = RID_OPEN; + u8 rid; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + pr_info("%s\n",__func__); + s2mm005_read_byte_16(i2c, 0x50, &rid); // fundtion read , 0x20 , 0x0:detach , not 0x0 attach : source 3,6,7 / sink 16:17:21:29(decimanl) + dev_info(&i2c->dev, "prev_rid:%x , RID:%x\n",prev_rid, rid); + if(usbpd_data->pd_state == State_PE_Initial_detach && rid != RID_OPEN) { + // workaround codes + dev_info(&i2c->dev, "function_state mismatch with rid, forcely set it as rid open!\n"); + rid = RID_OPEN; + } + + if(rid > 7) + usbpd_data->cur_rid = RID_OPEN; + else + usbpd_data->cur_rid = rid; + + if(rid) { +#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO + if ((rid == RID_000K) || (rid == RID_001K) || (rid == RID_523K) || (rid == RID_619K) + || (rid == RID_255K) || (rid == RID_301K)) { + muic_GPIO_control(1); + } else if ((rid == RID_UNDEFINED) || (rid == RID_OPEN)) { + muic_GPIO_control(0); +#if defined(CONFIG_MUIC_SUPPORT_KEYBOARDDOCK) + if (!adc_rescan_done) { + muic_ADC_rescan(); + adc_rescan_done = 1; + } +#endif + } +#endif + if(prev_rid != rid) + { +#if defined(CONFIG_CCIC_NOTIFIER) + /* rid */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_RID, rid/*rid*/, 0, 0); + + if (rid == RID_000K) { + /* otg */ + dev_info(&i2c->dev, "%s %d: RID_000K\n", __func__, __LINE__); + if (usbpd_data->is_client) { + /* usb or otg */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); + } + usbpd_data->is_host = HOST_ON_BY_RID000K; + usbpd_data->is_client = CLIENT_OFF; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_SRC; + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1); +#endif + /* add to turn on external 5V */ + vbus_turn_on_ctrl(1); + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0); + } if(rid == RID_OPEN || rid == RID_UNDEFINED || rid == RID_523K || rid == RID_619K) { + if (prev_rid == RID_000K) { + /* add to turn off external 5V */ + vbus_turn_on_ctrl(0); + } + usbpd_data->is_host = HOST_OFF; + usbpd_data->is_client = CLIENT_OFF; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE; + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); +#endif + /* usb or otg */ + ccic_event_work(usbpd_data, + CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0); + } +#endif + } + prev_rid = rid; + } + return; +} + diff --git a/drivers/ccic/s2mm005_fw.c b/drivers/ccic/s2mm005_fw.c new file mode 100644 index 000000000000..5dc932796794 --- /dev/null +++ b/drivers/ccic/s2mm005_fw.c @@ -0,0 +1,607 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#define S2MM005_FIRMWARE_PATH "usbpd/s2mm005.bin" + +#define FW_CHECK_RETRY 5 +#define VALID_FW_BOOT_VERSION(fw_boot) (fw_boot == 0x7) +#define VALID_FW_MAIN_VERSION(fw_main) \ + (!((fw_main[0] == 0xff) && (fw_main[1] == 0xff)) \ + && !((fw_main[0] == 0x00) && (fw_main[1] == 0x00))) + +const char *flashmode_to_string(u32 mode) +{ + switch (mode) { +#define FLASH_MODE_STR(mode) case mode: return(#mode) + FLASH_MODE_STR(FLASH_MODE_NORMAL); + FLASH_MODE_STR(FLASH_MODE_FLASH); + FLASH_MODE_STR(FLASH_MODE_ERASE); + } + return "?"; +} + +int s2mm005_sram_write(const struct i2c_client *i2c) +{ + int ret = 0; + struct i2c_msg msg[1]; + struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); + + pr_err("%s size:%d\n", __func__, BOOT_SRAM_FW_SIZE); + mutex_lock(&usbpd_data->i2c_mutex); + msg[0].addr = 0x3B; /* Slave addr 0x76 */ + msg[0].flags = 0; + msg[0].len = BOOT_SRAM_FW_SIZE; + msg[0].buf = (u8 *)&BOOT_SRAM_FW[0]; + + ret = i2c_transfer(i2c->adapter, msg, 1); + if (ret < 0) + dev_err(&i2c->dev, "i2c write fail error %d\n", ret); + mutex_unlock(&usbpd_data->i2c_mutex); + + return ret; +} + +void s2mm005_write_flash(const struct i2c_client *i2c, + unsigned int fAddr, unsigned int fData) { + u8 data[8]; + + data[0] = 0x42; + data[1] = 0x04; /* long write */ + + data[2] = (u8)(fAddr & 0xFF); + data[3] = (u8)((fAddr >> 8) & 0xFF); + + data[4] = (uint8_t)(fData & 0xFF); + data[5] = (uint8_t)((fData>>8) & 0xFF); + + data[6] = (uint8_t)((fData>>16) & 0xFF); + data[7] = (uint8_t)((fData>>24) & 0xFF); +/* pr_info("Flash Write Address :0x%08X Data:0x%08X\n", fAddr, fData);*/ + s2mm005_write_byte(i2c, 0x10, &data[0], 8); +} + +void s2mm005_verify_flash(const struct i2c_client *i2c, + uint32_t fAddr, uint32_t *fData) { + uint16_t REG_ADD; + uint8_t W_DATA[8]; + uint8_t R_DATA[4]; + + uint32_t fRead[3]; + + uint32_t fCnt; + + for (fCnt = 0; fCnt < 2; fCnt++) { + W_DATA[0] = 0x42; + W_DATA[1] = 0x40; /* Long Read */ + + W_DATA[2] = (uint8_t)(fAddr & 0xFF); + W_DATA[3] = (uint8_t)((fAddr>>8) & 0xFF); + + + REG_ADD = 0x10; + udelay(10); + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 4); + REG_ADD = 0x14; + udelay(10); + s2mm005_read_byte_flash(i2c, REG_ADD, &R_DATA[0], 4); + + fRead[fCnt] = 0; + + fRead[fCnt] |= R_DATA[0]; + fRead[fCnt] |= (R_DATA[1]<<8); + fRead[fCnt] |= (R_DATA[2]<<16); + fRead[fCnt] |= (R_DATA[3]<<24); + + } + + if (fRead[0] == fRead[1]) { + *fData = fRead[0]; + /* pr_err("> Flash Read Address : 0x%08X Data : 0x%08X\n",fAddr, *fData); */ + } else { + W_DATA[0] = 0x42; + W_DATA[1] = 0x40; /* Long Read */ + + W_DATA[2] = (uint8_t)(fAddr & 0xFF); + W_DATA[3] = (uint8_t)((fAddr>>8) & 0xFF); + + + REG_ADD = 0x10; + udelay(10); + s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 4); + REG_ADD = 0x14; + udelay(10); + s2mm005_read_byte_flash(i2c, REG_ADD, &R_DATA[0], 4); + + fRead[fCnt] = 0; + + fRead[2] |= R_DATA[0]; + fRead[2] |= (R_DATA[1]<<8); + fRead[2] |= (R_DATA[2]<<16); + fRead[2] |= (R_DATA[3]<<24); + + if (fRead[2] == fRead[0]) { + *fData = fRead[0]; + pr_err("> Flash Read[0] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData); + } else if (fRead[2] == fRead[1]) { + *fData = fRead[1]; + pr_err("> Flash Read[1] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData); + } else { + *fData = 0; + pr_err("> Flash Read[FAIL] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData); + } + } +} + +static int s2mm005_flash_write(struct s2mm005_data *usbpd_data, unsigned char *fw_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + u8 val, reg, fError; + uint32_t *pFlash_FW, *pFlash_FWCS; + uint32_t LopCnt, fAddr, fData, fRData, sLopCnt; + uint32_t recheck_count = 0; + struct s2mm005_fw *fw_hd; + unsigned int size; + + reg = FLASH_WRITE_0x42; + s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1); + reg = FLASH_WRITING_BYTE_SIZE_0x4; + s2mm005_write_byte(i2c, CMD_HOST_0x11, ®, 1); + s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1); + + pFlash_FW = (uint32_t *)(fw_data + 12); + pFlash_FWCS = (uint32_t *)fw_data; + fw_hd = (struct s2mm005_fw*)fw_data; + size = fw_hd -> size; + if(fw_hd -> boot < 6) + sLopCnt = 0x1000/4; + else if (fw_hd -> boot == 6) + sLopCnt = 0x8000/4; + else if (fw_hd -> boot == 7) + sLopCnt = 0x7000/4; + else if (fw_hd -> boot >= 8) + sLopCnt = 0x5000/4; + + /* Flash write */ + for (LopCnt = sLopCnt; LopCnt < (size/4); LopCnt++) { + fAddr = LopCnt*4; + fData = pFlash_FW[LopCnt]; + udelay(10); + s2mm005_write_flash(i2c, fAddr, fData); + } + usleep_range(10 * 1000, 10 * 1000); + /* Partial verify */ + while(1) { + for (LopCnt = sLopCnt; LopCnt < (size/4); LopCnt++) { + fError = 1; + fAddr = LopCnt*4; + fData = pFlash_FW[LopCnt]; + s2mm005_verify_flash(i2c, fAddr, &fRData); + if (fData != fRData) { + recheck_count++; + LopCnt = (fAddr & 0xffffff00) / 4; + sLopCnt = LopCnt; + fError = 0; + pr_err("%s partial verify fail!! recheck count : %d\n", __func__, recheck_count); + pr_err("Verify Error Address = 0x%08X WData = 0x%08X VData = 0x%08X\n", fAddr, fData, fRData); + s2mm005_write_flash(i2c, fAddr, fData); + msleep(20); + if(recheck_count == 1000) + return -EFLASH_VERIFY; + break; + } + } + if(fError) + break; + } + pr_err("%s verify success!! recheck count : %d\n", __func__, recheck_count); + + if (LopCnt >= (size / 4)) { + if (fw_hd -> boot >= 6) { + /* SW version from header */ + recheck_count = 0; + while(1) { + recheck_count++; + fAddr = 0xEFF0; + pr_err("SW version = 0x%08X\n", pFlash_FWCS[0]); + fData = pFlash_FWCS[0]; + s2mm005_write_flash(i2c, fAddr, fData); + fRData = 0; + s2mm005_verify_flash(i2c, fAddr, &fRData); + if(fData == fRData) + break; + else { + if (recheck_count == 30) + return -EFLASH_WRITE_SWVERSION; + } + } + /* Size from header */ + recheck_count = 0; + while(1) { + recheck_count++; + fAddr = 0xEFF4; + pr_err("SW Size = 0x%08X\n", pFlash_FWCS[2]); + fData = pFlash_FWCS[2]; + s2mm005_write_flash(i2c, fAddr, fData); + fRData = 0; + s2mm005_verify_flash(i2c, fAddr, &fRData); + if(fData == fRData) + break; + else { + if (recheck_count == 30) + return -EFLASH_WRITE_SIZE; + } + } + /* CRC Check sum */ + recheck_count = 0; + while(1) { + recheck_count++; + fAddr = 0xEFF8; + pr_err("SW CheckSum = 0x%08X\n", pFlash_FWCS[((size + 16) / 4) - 1]); + fData = pFlash_FWCS[((size + 16) / 4) - 1]; + s2mm005_write_flash(i2c, fAddr, fData); + fRData = 0; + s2mm005_verify_flash(i2c, fAddr, &fRData); + if(fData == fRData) + break; + else { + if (recheck_count == 30) + return -EFLASH_WRITE_CRC; + } + } + } + + /* flash done */ + recheck_count = 0; + while(1) + { + recheck_count++; + fAddr = 0xEFFC; + fData = 0x1; + s2mm005_write_flash(i2c, fAddr, fData); + fRData = 0; + s2mm005_verify_flash(i2c, fAddr, &fRData); + pr_err("0xeffc = %d\n", fRData); + if(fData == fRData) + break; + else { + if (recheck_count == 30) + return -EFLASH_WRITE_DONE; + } + } + pr_err("%s flash write succesfully done!!\n", __func__); + } + + return 0; +} + +void s2mm005_flash_ready(struct s2mm005_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + u8 W_DATA[5]; + + /* FLASH_READY */ + W_DATA[0] = 0x02; + W_DATA[1] = 0x01; + W_DATA[2] = 0x30; + W_DATA[3] = 0x50; + W_DATA[4] = 0x01; + s2mm005_write_byte(i2c, CMD_MODE_0x10, &W_DATA[0], 5); +} + +int s2mm005_flash(struct s2mm005_data *usbpd_data, unsigned int input) +{ + struct i2c_client *i2c = usbpd_data->i2c; + u8 val, reg; + int ret = 0; + int retry = 0; + struct s2mm005_fw *fw_hd; + struct file *fp; + mm_segment_t old_fs; + long fw_size, nread; + int irq_gpio_status; + FLASH_STATE_Type Flash_DATA; + + switch (input) { + case FLASH_MODE_ENTER: { /* enter flash mode */ + /* FLASH_READY */ + s2mm005_flash_ready(usbpd_data); + do { + /* FLASH_MODE */ + reg = FLASH_MODE_ENTER_0x10; + s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1); + usleep_range(50 * 1000, 50 * 1000); + /* If irq status is not clear, CCIC can not enter flash mode. */ + irq_gpio_status = gpio_get_value(usbpd_data->irq_gpio); + dev_info(&i2c->dev, "%s IRQ0:%02d\n", __func__, irq_gpio_status); + if(!irq_gpio_status) { + s2mm005_int_clear(usbpd_data); // interrupt clear + usleep_range(10 * 1000, 10 * 1000); + } + s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1); + pr_err("%s %s retry %d\n", __func__, flashmode_to_string(val), retry); + usleep_range(50*1000, 50*1000); + + s2mm005_read_byte(i2c, 0x24, Flash_DATA.BYTE, 4); + dev_info(&i2c->dev, "Flash_State:0x%02X Reserved:0x%06X\n", + Flash_DATA.BITS.Flash_State, Flash_DATA.BITS.Reserved); + + retry++; + if(retry == 10) { + /* RESET */ + s2mm005_reset(usbpd_data); + msleep(3000); + s2mm005_flash_ready(usbpd_data); + } else if (retry == 20) { + panic("Flash mode change fail!\n"); + } + } while (val != FLASH_MODE_FLASH); + break; + } + case FLASH_ERASE: { /* erase flash */ + reg = FLASH_ERASE_0x44; + usleep_range(10 * 1000, 10 * 1000); + s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1); + msleep(200); + s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1); + pr_err("flash mode : %s\n", flashmode_to_string(val)); + break; + } + case FLASH_WRITE7: { /* write flash & verify */ + switch (usbpd_data->s2mm005_fw_product_id) { + case PRODUCT_NUM_ASTAR_DREAMLITE: + default: + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x0B_BOOT7[0]); + break; + } + break; + } + case FLASH_WRITE8: { /* write flash & verify */ + switch (usbpd_data->s2mm005_fw_product_id) { + case PRODUCT_NUM_KELLY: + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x0C_BOOT8[0]); + break; + case PRODUCT_NUM_A8S: + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x11_BOOT8[0]); + break; + case PRODUCT_NUM_TABS4LV: + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x12_BOOT8[0]); + break; + case PRODUCT_NUM_GTACTIVEXL: + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x14_BOOT8[0]); + break; + default: + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x12_BOOT8[0]); + break; + } + break; + } + case FLASH_WRITE_UMS: { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(CCIC_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR); + if (IS_ERR(fp)) { + pr_err("%s: failed to open %s.\n", __func__, + CCIC_DEFAULT_UMS_FW); + ret = -ENOENT; + set_fs(old_fs); + return ret; + } + + fw_size = fp->f_path.dentry->d_inode->i_size; + if (0 < fw_size) { + unsigned char *fw_data; + fw_data = kzalloc(fw_size, GFP_KERNEL); + nread = vfs_read(fp, (char __user *)fw_data, + fw_size, &fp->f_pos); + + pr_err("%s: start, file path %s, size %ld Bytes\n", + __func__, CCIC_DEFAULT_UMS_FW, fw_size); + + if (nread != fw_size) { + pr_err("%s: failed to read firmware file, nread %ld Bytes\n", + __func__, nread); + ret = -EIO; + } else { + fw_hd = (struct s2mm005_fw*)fw_data; + pr_err("%02X %02X %02X %02X size:%05d\n", fw_hd->boot, fw_hd->main[0], fw_hd->main[1], fw_hd->main[2], fw_hd->size); + /* TODO : DISABLE IRQ */ + /* TODO : FW UPDATE */ + ret = s2mm005_flash_write(usbpd_data, (unsigned char*)fw_data); + /* TODO : ENABLE IRQ */ + } + kfree(fw_data); + } + + filp_close(fp, NULL); + set_fs(old_fs); + break; + } + case FLASH_MODE_EXIT: { /* exit flash mode */ + reg = FLASH_MODE_EXIT_0x20; + s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1); + usleep_range(15 * 1000, 15 * 1000); + s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1); + pr_err("flash mode : %s\n", flashmode_to_string(val)); + break; + } + default: { + pr_err("Flash value does not matched menu\n"); + + } + } + return ret; +} + +void s2mm005_get_fw_version(int product_id, + struct s2mm005_version *version, u8 boot_version, u32 hw_rev) +{ + struct s2mm005_fw *fw_hd = NULL; + switch (boot_version) { + case 5: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_BOOT5; + break; + case 6: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_BOOT6; + break; + case 7: + switch (product_id) { + case PRODUCT_NUM_ASTAR_DREAMLITE: + default: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x0B_BOOT7; + break; + } + case 8: + switch (product_id) { + case PRODUCT_NUM_KELLY: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x0C_BOOT8; + break; + case PRODUCT_NUM_A8S: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x11_BOOT8; + break; + case PRODUCT_NUM_TABS4LV: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x12_BOOT8; + break; + case PRODUCT_NUM_GTACTIVEXL: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x14_BOOT8; + break; + default: + fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x12_BOOT8; + break; + } + break; + } + + if(fw_hd != NULL) + { + version->boot = fw_hd->boot; + version->main[0] = fw_hd->main[0]; + version->main[1] = fw_hd->main[1]; + version->main[2] = fw_hd->main[2]; + } else { + version->boot = 0; + version->main[0] = 0; + version->main[1] = 0; + version->main[2] = 0; + } +} + +void s2mm005_get_chip_hwversion(struct s2mm005_data *usbpd_data, + struct s2mm005_version *version) +{ + struct i2c_client *i2c = usbpd_data->i2c; + + s2mm005_read_byte_flash(i2c, 0x0, (u8 *)&version->boot, 1); + s2mm005_read_byte_flash(i2c, 0x1, (u8 *)&version->main, 3); + s2mm005_read_byte_flash(i2c, 0x4, (u8 *)&version->ver2, 4); +} + +void s2mm005_get_chip_swversion(struct s2mm005_data *usbpd_data, + struct s2mm005_version *version) +{ + struct i2c_client *i2c = usbpd_data->i2c; + int i; + + for(i=0; i < FW_CHECK_RETRY; i++) { + s2mm005_read_byte_flash(i2c, 0x8, (u8 *)&version->boot, 1); + if(VALID_FW_BOOT_VERSION(version->boot)) + break; + } + for(i=0; i < FW_CHECK_RETRY; i++) { + s2mm005_read_byte_flash(i2c, 0x9, (u8 *)&version->main, 3); + if(VALID_FW_MAIN_VERSION(version->main)) + break; + } + for (i = 0; i < FW_CHECK_RETRY; i++) + s2mm005_read_byte_flash(i2c, 0xc, (u8 *)&version->ver2, 4); +} + +int s2mm005_check_version(struct s2mm005_version *version1, + struct s2mm005_version *version2) +{ + if (version1->boot != version2->boot) { + return FLASH_FW_VER_BOOT; + } + if (memcmp(version1->main, version2->main, 3)) { + return FLASH_FW_VER_MAIN; + } + + return FLASH_FW_VER_MATCH; +} + +int s2mm005_flash_fw(struct s2mm005_data *usbpd_data, unsigned int input) +{ + int ret = 0; + u8 val = 0; + + if( usbpd_data->fw_product_id != usbpd_data->s2mm005_fw_product_id) + { + pr_err("FW_UPDATE fail, product number is different (%d)(%d) \n", usbpd_data->fw_product_id,usbpd_data->s2mm005_fw_product_id); + return 0; + } + + pr_err("FW_UPDATE %d\n", input); + switch (input) { + case FLASH_WRITE5: + case FLASH_WRITE6: + case FLASH_WRITE7: + case FLASH_WRITE8: + case FLASH_WRITE: { + s2mm005_flash(usbpd_data, FLASH_MODE_ENTER); + usleep_range(10 * 1000, 10 * 1000); + s2mm005_flash(usbpd_data, FLASH_ERASE); + msleep(200); + ret = s2mm005_flash(usbpd_data, input); + if (ret < 0) + return ret; + usleep_range(10 * 1000, 10 * 1000); + s2mm005_flash(usbpd_data, FLASH_MODE_EXIT); + usleep_range(10 * 1000, 10 * 1000); + s2mm005_reset(usbpd_data); + usleep_range(10 * 1000, 10 * 1000); + break; + } + case FLASH_WRITE_UMS: { + s2mm005_read_byte_flash(usbpd_data->i2c, FLASH_STATUS_0x24, &val, 1); + if(val != FLASH_MODE_NORMAL) { + pr_err("Can't CCIC FW update: cause by %s\n", flashmode_to_string(val)); + } + disable_irq(usbpd_data->irq); + s2mm005_manual_LPM(usbpd_data, 0x7); // LP Off + msleep(3000); + s2mm005_flash(usbpd_data, FLASH_MODE_ENTER); + usleep_range(10 * 1000, 10 * 1000); + s2mm005_flash(usbpd_data, FLASH_ERASE); + msleep(200); + ret = s2mm005_flash(usbpd_data, input); + if (ret < 0) + panic("infinite write fail!\n"); + usleep_range(10 * 1000, 10 * 1000); + s2mm005_flash(usbpd_data, FLASH_MODE_EXIT); + usleep_range(10 * 1000, 10 * 1000); + s2mm005_reset(usbpd_data); + usleep_range(10 * 1000, 10 * 1000); + s2mm005_manual_LPM(usbpd_data, 0x6); // LP On + enable_irq(usbpd_data->irq); + break; + } + default: { + break; + } + } + + return 0; +} diff --git a/drivers/ccic/s2mm005_pd.c b/drivers/ccic/s2mm005_pd.c new file mode 100644 index 000000000000..316057da7617 --- /dev/null +++ b/drivers/ccic/s2mm005_pd.c @@ -0,0 +1,444 @@ +/* + * driver/../s2mm005.c - S2MM005 USB PD function driver + * + * Copyright (C) 2015 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#if defined(CONFIG_BATTERY_NOTIFIER) +#include +#endif +#include +#if defined(CONFIG_CCIC_ALTERNATE_MODE) +#include +#endif + +#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO +extern int muic_GPIO_control(int gpio); +#endif +struct pdic_notifier_struct pd_noti; + +//////////////////////////////////////////////////////////////////////////////// +// function definition +//////////////////////////////////////////////////////////////////////////////// +void select_pdo(int num); +void s2mm005_select_pdo(int num); +void select_pdo(int num); +void (*fp_select_pdo)(int num); +void vbus_turn_on_ctrl(bool enable); +void process_pd(void *data, u8 plug_attach_done, u8 *pdic_attach, MSG_IRQ_STATUS_Type *MSG_IRQ_State); +//////////////////////////////////////////////////////////////////////////////// +// PD function will be merged +//////////////////////////////////////////////////////////////////////////////// +static inline struct power_supply *get_power_supply_by_name(char *name) +{ + if (!name) + return (struct power_supply *)NULL; + else + return power_supply_get_by_name(name); +} + +void s2mm005_select_pdo(int num) +{ + struct s2mm005_data *usbpd_data = pd_noti.pusbpd; + uint8_t CMD_DATA[3]; + + if (pd_noti.sink_status.selected_pdo_num == num) { + pr_info("%s num(%d)\n", __func__, num); + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY, + CCIC_NOTIFY_ID_POWER_STATUS, 1/*attach*/, 0, 0); + return; + } else if (num > pd_noti.sink_status.available_pdo_num) + pd_noti.sink_status.selected_pdo_num = pd_noti.sink_status.available_pdo_num; + else if (num < 1) + pd_noti.sink_status.selected_pdo_num = 1; + else + pd_noti.sink_status.selected_pdo_num = num; + pr_info(" %s : PDO(%d) is selected to change\n", __func__, pd_noti.sink_status.selected_pdo_num); + + CMD_DATA[0] = 0x3; + CMD_DATA[1] = 0x3; + CMD_DATA[2] = pd_noti.sink_status.selected_pdo_num; + s2mm005_write_byte(pd_noti.pusbpd->i2c, REG_I2C_SLV_CMD, &CMD_DATA[0], 3); + + CMD_DATA[0] = 0x3; + CMD_DATA[1] = 0x2; + CMD_DATA[2] = State_PE_SNK_Wait_for_Capabilities; + s2mm005_write_byte(pd_noti.pusbpd->i2c, REG_I2C_SLV_CMD, &CMD_DATA[0], 3); +} + +void select_pdo(int num) +{ + if (fp_select_pdo) + fp_select_pdo(num); +} + +void vbus_turn_on_ctrl(bool enable) +{ + struct power_supply *psy_otg; + union power_supply_propval val; + int on = !!enable; + int ret = 0; + +#if defined(CONFIG_USB_HOST_NOTIFY) + struct otg_notify *o_notify = get_otg_notify(); + bool must_block_host = 0; + bool unsupport_host = 0; + if (o_notify) + must_block_host = is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST); + + pr_info("%s : enable=%d, must_block_host=%d\n", + __func__, enable, must_block_host); + + if (enable) { + if (o_notify) + unsupport_host = !is_usb_host(o_notify); + pr_info("%s : unsupport_host=%d\n", __func__, unsupport_host); + + if (must_block_host || unsupport_host) { + enable = false; + pr_info("%s : turn off vbus because of blocked host\n", + __func__); + } + } else { + // don't turn off because of blocked (already off) + if (must_block_host) + return; + } +#endif + + pr_info("%s %d, enable=%d\n", __func__, __LINE__, enable); + psy_otg = get_power_supply_by_name("otg"); + if (psy_otg) { + val.intval = enable; + ret = psy_otg->desc->set_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val); + } else { + pr_err("%s: Fail to get psy battery\n", __func__); + } + if (ret) { + pr_err("%s: fail to set power_suppy ONLINE property(%d)\n", + __func__, ret); + } else { + pr_info("otg accessory power = %d\n", on); + } + +} + +static int s2mm005_src_capacity_information(const struct i2c_client *i2c, uint32_t *RX_SRC_CAPA_MSG, + PDIC_SINK_STATUS * pd_sink_status, uint8_t *do_power_nego) +{ + uint32_t RdCnt; + uint32_t PDO_cnt; + uint32_t PDO_sel; + int available_pdo_num = 0; + int num_of_obj = 0; + + MSG_HEADER_Type *MSG_HDR; + SRC_FIXED_SUPPLY_Typedef *MSG_FIXED_SUPPLY; + SRC_VAR_SUPPLY_Typedef *MSG_VAR_SUPPLY; + SRC_BAT_SUPPLY_Typedef *MSG_BAT_SUPPLY; + + dev_info(&i2c->dev, "\n"); + for(RdCnt=0;RdCnt<8;RdCnt++) + { + dev_info(&i2c->dev, "Rd_SRC_CAPA_%d : 0x%X\n", RdCnt, RX_SRC_CAPA_MSG[RdCnt]); + } + + MSG_HDR = (MSG_HEADER_Type *)&RX_SRC_CAPA_MSG[0]; + dev_info(&i2c->dev, "=======================================\n"); + dev_info(&i2c->dev, " MSG Header\n"); + + dev_info(&i2c->dev, " Rsvd_msg_header : %d\n",MSG_HDR->Rsvd_msg_header ); + dev_info(&i2c->dev, " Number_of_obj : %d\n",MSG_HDR->Number_of_obj ); + dev_info(&i2c->dev, " Message_ID : %d\n",MSG_HDR->Message_ID ); + dev_info(&i2c->dev, " Port_Power_Role : %d\n",MSG_HDR->Port_Power_Role ); + dev_info(&i2c->dev, " Specification_Revision : %d\n",MSG_HDR->Specification_Revision ); + dev_info(&i2c->dev, " Port_Data_Role : %d\n",MSG_HDR->Port_Data_Role ); + dev_info(&i2c->dev, " Rsvd2_msg_header : %d\n",MSG_HDR->Rsvd2_msg_header ); + dev_info(&i2c->dev, " Message_Type : %d\n",MSG_HDR->Message_Type ); + + num_of_obj = MSG_HDR->Number_of_obj > MAX_PDO_NUM ? MAX_PDO_NUM : MSG_HDR->Number_of_obj; + for (PDO_cnt = 0; PDO_cnt < num_of_obj; PDO_cnt++) + { + PDO_sel = (RX_SRC_CAPA_MSG[PDO_cnt + 1] >> 30) & 0x3; + dev_info(&i2c->dev, " =================\n"); + dev_info(&i2c->dev, " PDO_Num : %d\n", (PDO_cnt + 1)); + + if(PDO_sel == 0) // *MSG_FIXED_SUPPLY + { + MSG_FIXED_SUPPLY = (SRC_FIXED_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1]; + if(MSG_FIXED_SUPPLY->Voltage_Unit <= (AVAILABLE_VOLTAGE/UNIT_FOR_VOLTAGE)) + available_pdo_num = PDO_cnt + 1; + if (!(*do_power_nego) && + (pd_sink_status->power_list[PDO_cnt+1].max_voltage != MSG_FIXED_SUPPLY->Voltage_Unit * UNIT_FOR_VOLTAGE || + pd_sink_status->power_list[PDO_cnt+1].max_current != MSG_FIXED_SUPPLY->Maximum_Current * UNIT_FOR_CURRENT)) + *do_power_nego = 1; + pd_sink_status->power_list[PDO_cnt+1].max_voltage = MSG_FIXED_SUPPLY->Voltage_Unit * UNIT_FOR_VOLTAGE; + pd_sink_status->power_list[PDO_cnt+1].max_current = MSG_FIXED_SUPPLY->Maximum_Current * UNIT_FOR_CURRENT; + + dev_info(&i2c->dev, " PDO_Parameter(FIXED_SUPPLY) : %d\n",MSG_FIXED_SUPPLY->PDO_Parameter ); + dev_info(&i2c->dev, " Dual_Role_Power : %d\n",MSG_FIXED_SUPPLY->Dual_Role_Power ); + dev_info(&i2c->dev, " USB_Suspend_Support : %d\n",MSG_FIXED_SUPPLY->USB_Suspend_Support ); + dev_info(&i2c->dev, " Externally_POW : %d\n",MSG_FIXED_SUPPLY->Externally_POW ); + dev_info(&i2c->dev, " USB_Comm_Capable : %d\n",MSG_FIXED_SUPPLY->USB_Comm_Capable ); + dev_info(&i2c->dev, " Data_Role_Swap : %d\n",MSG_FIXED_SUPPLY->Data_Role_Swap ); + dev_info(&i2c->dev, " Reserved : %d\n",MSG_FIXED_SUPPLY->Reserved ); + dev_info(&i2c->dev, " Peak_Current : %d\n",MSG_FIXED_SUPPLY->Peak_Current ); + dev_info(&i2c->dev, " Voltage_Unit : %d\n",MSG_FIXED_SUPPLY->Voltage_Unit ); + dev_info(&i2c->dev, " Maximum_Current : %d\n",MSG_FIXED_SUPPLY->Maximum_Current ); + } + else if(PDO_sel == 2) // *MSG_VAR_SUPPLY + { + MSG_VAR_SUPPLY = (SRC_VAR_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1]; + + dev_info(&i2c->dev, " PDO_Parameter(VAR_SUPPLY) : %d\n",MSG_VAR_SUPPLY->PDO_Parameter ); + dev_info(&i2c->dev, " Maximum_Voltage : %d\n",MSG_VAR_SUPPLY->Maximum_Voltage ); + dev_info(&i2c->dev, " Minimum_Voltage : %d\n",MSG_VAR_SUPPLY->Minimum_Voltage ); + dev_info(&i2c->dev, " Maximum_Current : %d\n",MSG_VAR_SUPPLY->Maximum_Current ); + } + else if(PDO_sel == 1) // *MSG_BAT_SUPPLY + { + MSG_BAT_SUPPLY = (SRC_BAT_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1]; + + dev_info(&i2c->dev, " PDO_Parameter(BAT_SUPPLY) : %d\n",MSG_BAT_SUPPLY->PDO_Parameter ); + dev_info(&i2c->dev, " Maximum_Voltage : %d\n",MSG_BAT_SUPPLY->Maximum_Voltage ); + dev_info(&i2c->dev, " Minimum_Voltage : %d\n",MSG_BAT_SUPPLY->Minimum_Voltage ); + dev_info(&i2c->dev, " Maximum_Allow_Power : %d\n",MSG_BAT_SUPPLY->Maximum_Allow_Power ); + } + + } + + /* the number of available pdo list */ + pd_sink_status->available_pdo_num = available_pdo_num; + dev_info(&i2c->dev, "=======================================\n"); + return available_pdo_num; +} + +//////////////////////////////////////////////////////////////////////////////// +// Processing message role +//////////////////////////////////////////////////////////////////////////////// +void process_message_role(void *data) +{ + struct s2mm005_data *usbpd_data = data; + struct otg_notify *o_notify = get_otg_notify(); + int is_dfp = 0; + int is_src = 0; + + // 1. read pd state + is_dfp = usbpd_data->func_state & (0x1 << 26) ? 1 : 0; + is_src = usbpd_data->func_state & (0x1 << 25) ? 1 : 0; + pr_info("%s func_state :0x%X, is_dfp : %d, is_src : %d\n", __func__, + usbpd_data->func_state, is_dfp, is_src); + pr_info("%s current port data_role : %d, power_role : %d\n", __func__, + usbpd_data->typec_data_role, usbpd_data->typec_power_role); + + if (is_src == usbpd_data->typec_power_role) { + pr_info("%s skip. already power role is set.\n", __func__); + return; + } + +#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO + pr_info("%s call muic_GPIO_control(1) to keep usb path\n", __func__); + muic_GPIO_control(1); +#endif + // 2. process power role + if (usbpd_data->func_state != State_PE_PRS_SNK_SRC_Source_on) { +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (is_src && (usbpd_data->power_role == DUAL_ROLE_PROP_PR_SNK)) { + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY, + CCIC_NOTIFY_ID_ATTACH, 0, 0, 0); + } +#elif defined (CONFIG_TYPEC) + if (is_src && (usbpd_data->typec_power_role == TYPEC_SINK)) { + pd_noti.event = PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC; + pd_noti.sink_status.selected_pdo_num = 0; + pd_noti.sink_status.available_pdo_num = 0; + pd_noti.sink_status.current_pdo_num = 0; + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY, + CCIC_NOTIFY_ID_POWER_STATUS, 0, 0, 0); + } +#endif + } + + +#if defined(CONFIG_USB_HOST_NOTIFY) + if (is_src) + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1); + else + send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); +#endif + vbus_turn_on_ctrl(is_src); + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + usbpd_data->power_role = is_src ? + DUAL_ROLE_PROP_PR_SRC : DUAL_ROLE_PROP_PR_SNK; + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_PDIC, + CCIC_NOTIFY_ID_ROLE_SWAP, 0, 0, 0); +#elif defined (CONFIG_TYPEC) + usbpd_data->typec_power_role = is_src ? TYPEC_SOURCE : TYPEC_SINK; + typec_set_pwr_role(usbpd_data->port, usbpd_data->typec_power_role); +#endif + + if (usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_PR) { + pr_info("%s : power role is changed %s\n", + __func__, is_src ? "SOURCE" : "SINK"); + usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; + complete(&usbpd_data->typec_reverse_completion); + pr_info("%s typec_reverse_completion!\n", __func__); + } +} + +void process_pd(void *data, u8 plug_attach_done, u8 *pdic_attach, MSG_IRQ_STATUS_Type *MSG_IRQ_State) +{ + struct s2mm005_data *usbpd_data = data; + struct i2c_client *i2c = usbpd_data->i2c; + uint16_t REG_ADD; + uint8_t rp_currentlvl, is_src, i; + REQUEST_FIXED_SUPPLY_STRUCT_Typedef *request_power_number; + + pr_info("%s\n",__func__); + + if (usbpd_data->short_detected) + rp_currentlvl = RP_CURRENT_ABNORMAL; + else + rp_currentlvl = ((usbpd_data->func_state >> 27) & 0x3); + + is_src = (usbpd_data->func_state & (0x1 << 25) ? 1 : 0); + dev_info(&i2c->dev, "rp_currentlvl:0x%02X, is_source:0x%02X\n", rp_currentlvl, is_src); + + if (MSG_IRQ_State->BITS.Ctrl_Flag_PR_Swap) + { + usbpd_data->is_pr_swap++; + dev_info(&i2c->dev, "PR_Swap requested to %s, receive\n", is_src ? "SOURCE" : "SINK"); + process_message_role(usbpd_data); + } else if (usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_PR) { + dev_info(&i2c->dev, "PR_Swap requested to %s, send\n", is_src ? "SOURCE" : "SINK"); + process_message_role(usbpd_data); + } else ; + + if (MSG_IRQ_State->BITS.Data_Flag_SRC_Capability) + { + uint8_t ReadMSG[32]; + int available_pdo_num; + uint8_t do_power_nego = 0; + pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK; + + REG_ADD = REG_RX_SRC_CAPA_MSG; + s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + available_pdo_num = s2mm005_src_capacity_information(i2c, (uint32_t *)ReadMSG, &pd_noti.sink_status, &do_power_nego); + + REG_ADD = REG_TX_REQUEST_MSG; + s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32); + request_power_number = (REQUEST_FIXED_SUPPLY_STRUCT_Typedef *)&ReadMSG[4]; + + pr_info(" %s : Object_posision(%d), available_pdo_num(%d), selected_pdo_num(%d) \n", __func__, + request_power_number->Object_Position, available_pdo_num, pd_noti.sink_status.selected_pdo_num); + pd_noti.sink_status.current_pdo_num = request_power_number->Object_Position; + + if(available_pdo_num > 0) + { + if(request_power_number->Object_Position != pd_noti.sink_status.selected_pdo_num) + { + if (pd_noti.sink_status.selected_pdo_num == 0) + { + pr_info(" %s : PDO is not selected yet by default\n", __func__); + pd_noti.sink_status.selected_pdo_num = pd_noti.sink_status.current_pdo_num; + } + } else { + if (do_power_nego) { + pr_info(" %s : PDO(%d) is selected, but power negotiation is requested\n", + __func__, pd_noti.sink_status.selected_pdo_num); + pd_noti.sink_status.selected_pdo_num = 0; + pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK_CAP; + } else { + pr_info(" %s : PDO(%d) is selected, but same with previous list, so skip\n", + __func__, pd_noti.sink_status.selected_pdo_num); + } + } + *pdic_attach = 1; + } else { + pr_info(" %s : PDO is not selected\n", __func__); + } + } + + if (MSG_IRQ_State->BITS.Ctrl_Flag_Get_Sink_Cap) + { + pr_info(" %s : SRC requested SINK Cap\n", __func__); + } + + /* notify to battery */ +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + if (plug_attach_done) { + if (*pdic_attach) { + /* PD charger is detected by PDIC */ + } else if (!is_src && (usbpd_data->pd_state == State_PE_SNK_Wait_for_Capabilities || + usbpd_data->pd_state == State_ErrorRecovery) && + rp_currentlvl != pd_noti.sink_status.rp_currentlvl && + rp_currentlvl >= RP_CURRENT_LEVEL_DEFAULT) { + if (rp_currentlvl == RP_CURRENT_LEVEL3) { + /* 5V/3A RP charger is detected by CCIC */ + pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL3; + pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH; + } else if (rp_currentlvl == RP_CURRENT_LEVEL2) { + /* 5V/1.5A RP charger is detected by CCIC */ + pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL2; + pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH; + } else if (rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT) { + /* 5V/0.5A RP charger is detected by CCIC */ + pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_DEFAULT; + pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH; + } else if (rp_currentlvl == RP_CURRENT_ABNORMAL) { + /* ABNORMAL RP charger is detected by CCIC */ + pd_noti.sink_status.rp_currentlvl = RP_CURRENT_ABNORMAL; + pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH; + } else + return; + } else + return; +#ifdef CONFIG_SEC_FACTORY + pr_info(" %s : debug pdic_attach(%d) event(%d)\n", __func__, *pdic_attach, pd_noti.event); +#endif + ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_POWER_STATUS, *pdic_attach, 0, 0); + } else { + for (i = 0; i < MAX_PDO_NUM + 1; i++) { + pd_noti.sink_status.power_list[i].max_current = 0; + pd_noti.sink_status.power_list[i].max_voltage = 0; + } + pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE; + pd_noti.sink_status.available_pdo_num = 0; + pd_noti.sink_status.selected_pdo_num = 0; + pd_noti.sink_status.current_pdo_num = 0; + pd_noti.event = PDIC_NOTIFY_EVENT_DETACH; + } +#else + if(plug_attach_done) + { + /* PD notify */ + if(*pdic_attach) + pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK; + else + pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH; + } + else + { + pd_noti.sink_status.selected_pdo_num = 0; + pd_noti.event = PDIC_NOTIFY_EVENT_DETACH; + } + pdic_notifier_call(pd_noti); +#endif +} diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index cd481f49be3e..9d30d5edf841 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -340,6 +340,8 @@ struct fastrpc_apps { bool secure_flag; spinlock_t ctxlock; struct smq_invoke_ctx *ctxtable[FASTRPC_CTX_MAX]; + int tx_counter; + int rx_counter; }; struct fastrpc_mmap { @@ -1360,6 +1362,8 @@ static void context_free(struct smq_invoke_ctx *ctx) if (me->ctxtable[i] == ctx) { handle = me->ctxtable[i]->handle; ptr = me->ctxtable[i]->ptr; + if(ctx->fl->cid == 0x2) + me->tx_counter++; me->ctxtable[i] = NULL; break; } @@ -2065,6 +2069,7 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, int err = 0, cid = -1, interrupted = 0; struct timespec invoket = {0}; int64_t *perf_counter = NULL; + struct fastrpc_apps *me = &gfa; cid = fl->cid; VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS); @@ -3138,6 +3143,8 @@ static void fastrpc_glink_notify_rx(void *handle, const void *priv, spin_unlock_irqrestore(&me->ctxlock, irq_flags); goto bail; } + if (me->ctxtable[index]->fl->cid == 0x2) + me->rx_counter++; me->ctxtable[index]->handle = handle; me->ctxtable[index]->ptr = ptr; spin_unlock_irqrestore(&me->ctxlock, irq_flags); @@ -3417,6 +3424,8 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, char *fileinfo = NULL; char single_line[UL_SIZE] = "----------------"; char title[UL_SIZE] = "========================="; + single_line[UL_SIZE-1]='\0'; + title[UL_SIZE-1]='\0'; fileinfo = kzalloc(DEBUGFS_SIZE, GFP_KERNEL); if (!fileinfo) @@ -4704,6 +4713,8 @@ static int __init fastrpc_device_init(void) me->dev = NULL; me->glink = true; me->secure_flag = false; + me->tx_counter = 0; + me->rx_counter = 0; VERIFY(err, 0 == platform_driver_register(&fastrpc_driver)); if (err) goto register_bail; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 872c9d0746c6..c7f147ccb266 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -30,6 +30,9 @@ #include #include +#include +#include + #include "clk.h" #if defined(CONFIG_COMMON_CLK) @@ -94,6 +97,8 @@ struct clk_core { unsigned long *rate_max; int num_rate_max; }; +extern unsigned int sec_debug_level(void); +bool is_dbg_level_low; #define CREATE_TRACE_POINTS #include @@ -1811,6 +1816,8 @@ static int clk_change_rate(struct clk_core *core) } trace_clk_set_rate(core, core->new_rate); + sec_debug_clock_rate_log(core->name, core->new_rate, + raw_smp_processor_id()); /* Enforce vdd requirements for new frequency. */ if (core->prepare_count) { @@ -1847,6 +1854,8 @@ static int clk_change_rate(struct clk_core *core) } trace_clk_set_rate_complete(core, core->new_rate); + sec_debug_clock_rate_complete_log(core->name, core->new_rate, + raw_smp_processor_id()); /* Release vdd requirements for old frequency. */ if (core->prepare_count) @@ -2365,7 +2374,7 @@ EXPORT_SYMBOL_GPL(clk_list_frequency); static struct dentry *rootdir; static int inited = 0; -static u32 debug_suspend; +static u32 debug_suspend = 1; static DEFINE_MUTEX(clk_debug_lock); static HLIST_HEAD(clk_debug_list); @@ -3077,6 +3086,12 @@ static int __init clk_debug_init(void) rootdir = debugfs_create_dir("clk", NULL); + //ANDROID_DEBUG_LEVEL_LOW 0x4f4c + if (sec_debug_level() == 0x4f4c) + is_dbg_level_low = true; + else + is_dbg_level_low = false; + if (!rootdir) return -ENOMEM; diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index 48ea05d15dd3..f1e87c676866 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -36,6 +36,8 @@ #include #include +#include + #include "common.h" #include "clk-regmap.h" #include "clk-voter.h" @@ -296,6 +298,59 @@ static int clk_cpu_determine_rate(struct clk_hw *hw, return 0; } +#ifdef CONFIG_SEC_DEBUG_APPS_CLK_LOGGING + +typedef struct { + uint64_t ktime; + uint64_t qtime; + uint64_t rate; +} apps_clk_log_t; + +#define MAX_CLK_LOG_CNT (10) + +typedef struct { + uint32_t max_cnt; + uint32_t index; + apps_clk_log_t log[MAX_CLK_LOG_CNT]; +} cpuclk_log_t; + +cpuclk_log_t cpuclk_log[3] = { + [0] = {.max_cnt = MAX_CLK_LOG_CNT,}, + [1] = {.max_cnt = MAX_CLK_LOG_CNT,}, + [2] = {.max_cnt = MAX_CLK_LOG_CNT,}, +}; + +static void clk_osm_add_log(struct cpufreq_policy *policy, unsigned int index) +{ + struct clk_osm *c = policy->driver_data; + cpuclk_log_t *clk = NULL; + apps_clk_log_t *log = NULL; + uint64_t idx = 0; + uint32_t cluster = 0; + + cluster = policy->cpu / 4; + if (!WARN(cluster >= 2, "%s : invalid cluster_num(%u), dbg_name(%s)\n", + __func__, cluster, clk_hw_get_name(&c->hw))) { + if (cluster == 0) + clk = &cpuclk_log[PWR_CLUSTER]; + else + clk = &cpuclk_log[PERF_CLUSTER]; + idx = clk->index; + log = &clk->log[idx]; + log->ktime = local_clock(); + log->qtime = arch_counter_get_cntvct(); + log->rate = policy->freq_table[index].frequency; + clk->index = (clk->index + 1) % MAX_CLK_LOG_CNT; + } +} + +void *clk_osm_get_log_addr(void) +{ + return (void *)&cpuclk_log; +} +EXPORT_SYMBOL(clk_osm_get_log_addr); +#endif + static int l3_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -350,6 +405,22 @@ static int l3_clk_set_rate(struct clk_hw *hw, unsigned long rate, return -ETIMEDOUT; } cpuclk->rate = rate; + +#ifdef CONFIG_SEC_DEBUG_APPS_CLK_LOGGING + { + cpuclk_log_t *clk = NULL; + apps_clk_log_t *log = NULL; + uint64_t idx = 0; + + clk = &cpuclk_log[L3]; + idx = clk->index; + log = &clk->log[idx]; + log->ktime = local_clock(); + log->qtime = arch_counter_get_cntvct(); + log->rate = rate; + clk->index = (clk->index + 1) % MAX_CLK_LOG_CNT; + } +#endif return 0; } @@ -715,6 +786,9 @@ osm_cpufreq_target_index(struct cpufreq_policy *policy, unsigned int index) struct clk_osm *c = policy->driver_data; osm_set_index(c, index); +#ifdef CONFIG_SEC_DEBUG_APPS_CLK_LOGGING + clk_osm_add_log(policy, index); +#endif return 0; } diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c index 0c49fa4bd137..c24aa3c02307 100644 --- a/drivers/clk/qcom/dispcc-sdm845.c +++ b/drivers/clk/qcom/dispcc-sdm845.c @@ -201,6 +201,9 @@ static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { }; static const struct freq_tbl ftbl_disp_cc_mdss_dp_aux_clk_src[] = { +#if defined(CONFIG_DISPLAY_SAMSUNG) + F(12800000, P_BI_TCXO, 1.5, 0, 0), +#endif F(19200000, P_BI_TCXO, 1, 0, 0), { } }; diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index 7ea5d9d50d7a..914e9241a34d 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -966,13 +966,14 @@ static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { }; static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(300000, P_BI_TCXO, 32, 1, 2), F(400000, P_BI_TCXO, 12, 1, 4), F(9600000, P_BI_TCXO, 2, 0, 0), F(19200000, P_BI_TCXO, 1, 0, 0), F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), - F(201500000, P_GPLL4_OUT_MAIN, 4, 0, 0), + F(179100000, P_GPLL4_OUT_MAIN, 4.5, 0, 0), { } }; diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index d13d8897fcec..12a98903f2eb 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -163,8 +163,14 @@ static int qti_ice_setting_config(struct request *req, setting->encr_bypass = false; break; case READ: - if (!ice_fde_flag || (ice_fde_flag & QCOM_ICE_DECRYPT)) - setting->decr_bypass = false; + if (!ice_fde_flag || (ice_fde_flag & QCOM_ICE_DECRYPT)) { + if (unlikely(req->cmd_flags & REQ_BYPASS)) { + setting->encr_bypass = true; + setting->decr_bypass = true; + } else { + setting->decr_bypass = false; + } + } break; default: /* Should I say BUG_ON */ diff --git a/drivers/debug/Kconfig b/drivers/debug/Kconfig new file mode 100644 index 000000000000..c41adc1de826 --- /dev/null +++ b/drivers/debug/Kconfig @@ -0,0 +1,238 @@ +# When adding new entries keep the list in alphabetical order + +comment "Samsung Debugging Feature" +menuconfig SEC_DEBUG + bool "Samsung TN Ramdump Feature" + default n + help + Samsung TN Ramdump Feature. + Enables collection of ram dump. + Enables task history, debug level etc. + This feature is enabled in defconfig. + +if SEC_DEBUG + +config SEC_DEBUG_PRINTK_NOCACHE + bool "Samsung non-cached kernel printk" + default y + help + Samsung non-cached kernel printk + This ensures that printk is never stale + which is a good feature when debugging without + hardware debuggers. If unsure, keep it turned on. + +config SEC_DEBUG_SCHED_LOG + bool "Samsung Scheduler Logging Feature" + default n + help + Samsung Scheduler Logging Feature for Debug use. + Enables task scheduling history. + Enables IRQ scheduling history. + This feature is enabled in defconfig. + +config SEC_DEBUG_SEMAPHORE_LOG + bool "Samsung Semaphore Logging Feature" + default n + help + Samsung Semaphore Logging Feature for Debug use. + Enables semaphore debugging statistics. + Enables logging. + This feature is enabled in defconfig. + +config SEC_FILE_LEAK_DEBUG + bool "Samsung File Leak Debugging Feature" + default n + help + Samsung File Leak Debugging Feature for Debug use. + Enables the forced panic mode when EMFILE Eror occurs. + This feature is enabled in defconfig. + +config SEC_LOGGER_BUFFER_EXPANSION + bool "Samsung Logger Buffer Expansion Feature" + default n + help + This is used to expand buffers of logger. + This feature is enabled in defconfig. + +config SEC_LOGGER_BUFFER_EXPANSION_SIZE + int "Samsung Logger Buffer Expansion Size(MB)" + depends on SEC_LOGGER_BUFFER_EXPANSION + default 2 + help + This is used to expand main buffer of logger(MB). + +config SEC_DEBUG_USER + bool "Panic on Userspace fault" + default y + help + Panic on Userspace fault + This feature enables collection of ram dump, + on user fault. + Enabled native code debugging. + +config SEC_DEBUG_IRQ_EXIT_LOG + bool "Temporary Logging for IRQ delay" + default n + help + Verbose Logging for IRQ delay. + Helps indetification of irq enter and exit. + This is to track the current state of IRQ execution. + This is enabled in defconfig file. + +config SEC_DEBUG_MSG_LOG + bool "Message Log for ram dump debug" + default n + help + Verbose Logging for ram dump analysis. + Collects kernel debug log. + Log is collected in the no-cache area. + This feature is enabled in defconfig. + +config SEC_DEBUG_SUMMARY + bool "Debug summary" + depends on SEC_DEBUG_SCHED_LOG + default n + help + Subsystems debug summary feature. + When enabled provides kernel logs, modem logs, RPM registers, + Schedule and IRQ logs at the time of crash along with the + reason for crash, which can be extracted as a html in RAM dump mode. + +config SEC_DEBUG_DCVS_LOG + bool "Temporary Logging for DCVS" + default n + help + DCVS Logging Feature for Debug use. + The ACPU clock rate changes will be logged as a part + of secdbg_log structure along with the CPU time stamp. + The previous frequency and the new frequency for both the CPU along + with the CPU time stamp will be logged. + +config SEC_DEBUG_POWER_LOG + bool "Temporary Logging for MSM POWER" + default n + help + POWER Logging Feature for Debug use. + The power and clock gating will be logged as a part + of secdbg_log structure along with the CPU time stamp. + +config SEC_DEBUG_FUELGAUGE_LOG + bool "Temporary Logging for FuelGauge" + default n + help + FuelGauge Logging Feature for Debug use. + The FuelGauge values are logged as a part + of secdbg_log structure along with the CPU time stamp. + The voltage and soc values along with the CPU time will be logged. + +config SEC_DEBUG_LOW_LOG + bool "Kernel Message Logging for Debug Level Low" + default n + help + Kernel Message Logging Feature for Debug use. + The Kernel Messages are logged using file I/O + when an exception occurs, when the debug level is low. + The file I/O is added in kernel driver level + so that kernel messages are logged on next reboot. + +config SEC_DEBUG_MDM_FILE_INFO + bool "MDM filename and line number in UPLOAD mode" + default n + help + This feature enables display of MDM info in upload mode. + This feature enabled SSR in debug level low. + Collects MDM ram dump and then calls panic. + UPLOAD mode has MDM dump info to show it on LCD. + +config SEC_DEBUG_DOUBLE_FREE + bool "Enable double free detection" + default n + help + Detect erraneous codes that frees a kmalloced node + twice. When kfree(p) is called, p is not freed right away. + Instead, it is pushed into a circular buffer. What it frees + is the oldest pointer that was pushed into the buffer. + If someone tries to free the same pointer p, *p + is read and checked for a magic code that is written + when it was first freed. If it matches, the whole + circular buffer is searched. Panic is be called when + the match is found. + +config SEC_DEBUG_FORCE_ERROR + bool "enable force error" + default n + help + This option enable to force error by sysfs + +config SEC_DEBUG_SEC_WDOG_BITE + bool "Samsung fore secure bite simulation" + default n + depends on QCOM_SCM && SEC_DEBUG_FORCE_ERROR + help + simulation for secure bite. + This feature is enabled in defconfig. + +config SEC_LOG_LAST_KMSG + bool "Enable /proc/last_kmsg support: if EMBEDDED" + default n + help + This option enables /proc/last_kmsg support. + +config SEC_DEBUG_NOCACHE_LOG_IN_LEVEL_LOW + bool "Enable nocache logging in debug level LOW" + default n + help + Enable nocache logging in debug level LOW. + +config SEC_SSR_DEBUG_LEVEL_CHK + bool "PERIPHERAL SECURE check" + default n + help + To check the authentication of peripheral image. + +config USER_RESET_DEBUG + bool "reset reason debug feature in user version" + default n + help + This option provides reset history log in user version. + This option enable proc/reset_reason support + +config USER_RESET_DEBUG_TEST + bool "reset reason debug test feature in eng version" + depends on USER_RESET_DEBUG + default n + help + This option enable for test in eng version(KP, DP, TP, WP) + +config SEC_PERIPHERAL_SECURE_CHK + bool "PERIPHERAL SECURE check" + default n + depends on MSM_PIL + help + This option enables checking the authentication of peripheral image. + +config SEC_DEBUG_PWDT + bool "Platform Watchdog check" + default n + help + To check Platform Watchdog thread status +endif + +config SEC_QUEST + bool "Samsung QUEST Feature" + default n + help + Samsung QUEST Feature, to test chipset quality + +config SEC_QUEST_UEFI + bool "Samsung QUEST UEFI Feature" + default n + help + Samsung QUEST UEFI Feature, to test chipset quality + +config SEC_SKP + bool "Samsung SKP Feature" + default n + help + Samsung SKP Feature, to test chipset quality + diff --git a/drivers/debug/Makefile b/drivers/debug/Makefile new file mode 100644 index 000000000000..6571bfe65383 --- /dev/null +++ b/drivers/debug/Makefile @@ -0,0 +1,14 @@ +# When adding new entries keep the list in alphabetical order + +obj-$(CONFIG_SEC_DEBUG) += sec_debug.o sec_kcompat.o \ + sec_crashkey.o \ + sec_crashkey_long.o \ + sec_key_notifier.o \ + sec_debug_partition.o +obj-$(CONFIG_SEC_DEBUG_SCHED_LOG) += sec_debug_sched_log.o +obj-$(CONFIG_USER_RESET_DEBUG) += sec_debug_user_reset.o +obj-$(CONFIG_SEC_DEBUG_FORCE_ERROR) += sec_debug_force_err.o +obj-$(CONFIG_SEC_DEBUG_DOUBLE_FREE) += sec_debug-dfd.o +obj-$(CONFIG_SEC_DEBUG_SUMMARY) += sec_debug_summary.o +obj-$(CONFIG_SEC_QUEST) += sec_quest.o +obj-$(CONFIG_SEC_QUEST) += spinlock_test.o diff --git a/drivers/debug/sec_crashkey.c b/drivers/debug/sec_crashkey.c new file mode 100644 index 000000000000..aeb967c224ed --- /dev/null +++ b/drivers/debug/sec_crashkey.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/samsung/debug/sec_crashkey.c + * + * COPYRIGHT(C) 2019 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sec_debug_internal.h" +#include "sec_key_notifier.h" + +#define EVENT_KEY_PRESS(__keycode) \ + { .keycode = __keycode, .down = true, } +#define EVENT_KEY_RELEASE(__keycode) \ + { .keycode = __keycode, .down = false, } +#define EVENT_KEY_PRESS_AND_RELEASE(__keycode) \ + EVENT_KEY_PRESS(__keycode), \ + EVENT_KEY_RELEASE(__keycode) + +struct event_pattern { + unsigned int keycode; + bool down; +}; + +struct event_state { + struct ratelimit_state rs; + struct event_pattern *desired_pattern; + struct event_pattern *received_pattern; + size_t nr_pattern; + const char *msg; + int interval; + size_t sequence; +}; + +static struct event_pattern crashkey_pattern[] = { + EVENT_KEY_PRESS(KEY_VOLUMEDOWN), + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), +}; + +static struct event_pattern received_pattern[ARRAY_SIZE(crashkey_pattern)]; + +static struct event_state crashkey_state = { + .desired_pattern = crashkey_pattern, + .received_pattern = received_pattern, + .nr_pattern = ARRAY_SIZE(crashkey_pattern), + .msg = UPLOAD_MSG_CRASH_KEY, + .interval = 1 * HZ, +}; + +static struct event_pattern crashkey_user_pattern[] = { + EVENT_KEY_PRESS(KEY_VOLUMEDOWN), /* initial trigger */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 1 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 2 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 3 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 4 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 5 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 6 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 7 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 8 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 9 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 1 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 2 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 3 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 4 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 5 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 1 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 2 */ + EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 3 */ +}; + +static struct event_pattern received_user_pattern[ARRAY_SIZE(crashkey_user_pattern)]; + +static struct event_state crashkey_user_state = { + .desired_pattern = crashkey_user_pattern, + .received_pattern = received_user_pattern, + .nr_pattern = ARRAY_SIZE(crashkey_user_pattern), + .msg = UPLOAD_MSG_USER_CRASH_KEY, + .interval = 7 * HZ, +}; + +static struct event_state *key_event_state; + +static __always_inline int __crashkey_test_pattern(size_t len) +{ + return memcmp(key_event_state->desired_pattern, + key_event_state->received_pattern, + len * sizeof(struct event_pattern)); +} + +static __always_inline void __crashkey_clear_received_pattern(void) +{ + key_event_state->sequence = 0; + memset(key_event_state->received_pattern, 0x0, + key_event_state->nr_pattern * sizeof(struct event_pattern)); +} + +static int sec_crashkey_notifier_call(struct notifier_block *this, + unsigned long type, void *data) +{ + struct sec_key_notifier_param *param = data; + size_t idx = key_event_state->sequence; + + if (idx >= key_event_state->nr_pattern) + goto clear_state; + + key_event_state->received_pattern[idx].keycode = param->keycode; + key_event_state->received_pattern[idx].down = !!param->down; + key_event_state->sequence++; + + if (!__ratelimit(&(key_event_state->rs))) { + if (!__crashkey_test_pattern(key_event_state->nr_pattern)) { +#ifdef CONFIG_SEC_USER_RESET_DEBUG + sec_debug_store_extc_idx(false); +#endif + panic(key_event_state->msg); + } else + goto clear_state; + } else if (__crashkey_test_pattern(key_event_state->sequence)) + goto clear_state; + + return NOTIFY_DONE; + +clear_state: + __crashkey_clear_received_pattern(); + + return NOTIFY_DONE; +} + +static struct notifier_block sec_crashkey_notifier = { + .notifier_call = sec_crashkey_notifier_call, +}; + +static unsigned int *carashkey_used_event; +static size_t crashkey_nr_used_event; + +static int __init __crashkey_init_used_event(void) +{ + bool is_new; + size_t i; + size_t j; + + carashkey_used_event = kmalloc_array(key_event_state->nr_pattern, + sizeof(*carashkey_used_event), GFP_KERNEL); + if (!carashkey_used_event) + return -ENOMEM; + + carashkey_used_event[0] = + key_event_state->desired_pattern[0].keycode; + crashkey_nr_used_event = 1; + + for (i = 1; i < key_event_state->nr_pattern; i++) { + for (j = 0, is_new = true; j < crashkey_nr_used_event; j++) { + if (carashkey_used_event[j] == + key_event_state->desired_pattern[i].keycode) + is_new = false; + } + + if (is_new) + carashkey_used_event[crashkey_nr_used_event++] = + key_event_state->desired_pattern[i].keycode; + } + + return 0; +} + +static void __sec_crashkey_parse_dt_replace_keymap(void) +{ + struct device_node *parent, *node; + int err; + size_t i; + u32 resin_keycode, pwr_keycode; + + parent = of_find_node_by_path("/soc"); + if (!parent) + goto no_dt; + + node = of_find_node_by_name(parent, "sec_key_crash"); + if (!node) + goto no_dt; + + err = of_property_read_u32(node, "resin-keycode", &resin_keycode); + if (err) + goto no_dt; + + err = of_property_read_u32(node, "pwr-keycode", &pwr_keycode); + if (err) + goto no_dt; + + for (i = 0; i < key_event_state->nr_pattern; i++) { + struct event_pattern *desired_pattern = + &key_event_state->desired_pattern[i]; + + switch (desired_pattern->keycode) { + case KEY_VOLUMEDOWN: + if (resin_keycode != KEY_VOLUMEDOWN) + desired_pattern->keycode = resin_keycode; + break; + case KEY_POWER: + if (pwr_keycode != KEY_POWER) + desired_pattern->keycode = pwr_keycode; + break; + } + } + pr_info("use dt keymap"); + return; + +no_dt: + pr_info("use default keymap"); +} + +static int __init sec_crashkey_init(void) +{ + int err; + + if (!sec_debug_is_enabled()) + key_event_state = &crashkey_user_state; + else + key_event_state = &crashkey_state; + + __sec_crashkey_parse_dt_replace_keymap(); + + err = __crashkey_init_used_event(); + if (err) { + pr_warn("crashkey can not be enabled! (%d)", err); + return err; + } + + ratelimit_state_init(&(key_event_state->rs), + key_event_state->interval, + key_event_state->nr_pattern - 1); + + sec_kn_register_notifier(&sec_crashkey_notifier, + carashkey_used_event, crashkey_nr_used_event); + + return 0; +} +arch_initcall_sync(sec_crashkey_init); diff --git a/drivers/debug/sec_crashkey_long.c b/drivers/debug/sec_crashkey_long.c new file mode 100644 index 000000000000..4653c4b6ca47 --- /dev/null +++ b/drivers/debug/sec_crashkey_long.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/samsung/debug/sec_crashkey_long.c + * + * COPYRIGHT(C) 2019 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sec_debug_internal.h" +#include "sec_key_notifier.h" +#include "sec_kcompat.h" + +#define EXPIRE_MSEC 6600 + +static unsigned int crashkey_long_used_event[] = { + KEY_VOLUMEDOWN, + KEY_POWER, +}; + +static size_t crashkey_long_nr_used_event = + ARRAY_SIZE(crashkey_long_used_event); + +struct long_pressed_event_state { + unsigned long *bitmap_recieved; + size_t sz_bitmap; +}; + +static struct long_pressed_event_state __long_key_state; +static struct long_pressed_event_state *long_key_state = &__long_key_state; + +static inline void __reset_pon_s2_ctrl_reset(void) +{ + qpnp_control_s2_reset_onoff(0); + udelay(1000); + qpnp_control_s2_reset_onoff(1); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +static void sec_crashkey_long_timer_handler(struct timer_list *tl) +#else +static void sec_crashkey_long_timer_handler(unsigned long data) +#endif +{ + __pr_err("*** Force trigger kernel panic before triggering hard reset ***\n"); + + __reset_pon_s2_ctrl_reset(); + panic(UPLOAD_MSG_LONG_KEY_PRESS); +} + +static struct timer_list sec_crashkey_long_tl; + +static int sec_crashkey_long_notifier_call(struct notifier_block *this, + unsigned long type, void *data) +{ + struct sec_key_notifier_param *param = data; + int i; + bool matching; + + if (param->down) + set_bit(param->keycode, long_key_state->bitmap_recieved); + else + clear_bit(param->keycode, long_key_state->bitmap_recieved); + + for (i = 0, matching = true; i < crashkey_long_nr_used_event; i++) { + if (!test_bit(crashkey_long_used_event[i], long_key_state->bitmap_recieved)) { + matching = false; + break; + } + } + + if (matching) { + if (sec_debug_get_upload_cause() == UPLOAD_CAUSE_INIT) + sec_debug_set_upload_cause(UPLOAD_CAUSE_POWER_LONG_PRESS); + + if (unlikely(sec_debug_is_enabled())) { + if (!timer_pending(&sec_crashkey_long_tl)) { + timer_setup(&sec_crashkey_long_tl, + sec_crashkey_long_timer_handler, 0); + mod_timer(&sec_crashkey_long_tl, + jiffies + msecs_to_jiffies(EXPIRE_MSEC)); + pr_info("long key timer - start"); + } + } + } else { + if (sec_debug_get_upload_cause() == UPLOAD_CAUSE_POWER_LONG_PRESS) + sec_debug_set_upload_cause(UPLOAD_CAUSE_INIT); + + if (timer_pending(&sec_crashkey_long_tl)) { + del_timer(&sec_crashkey_long_tl); + pr_info("long key timer - cancel"); + } + } + + return NOTIFY_DONE; +} + +static struct notifier_block sec_crashkey_long_notifier = { + .notifier_call = sec_crashkey_long_notifier_call, +}; + +/* for retail group's request */ +#if defined(CONFIG_SEC_PM) +void do_keyboard_notifier(int onoff) +{ + pr_info("%s: onoff(%d)\n", __func__, onoff); + + if (onoff) + sec_kn_register_notifier(&sec_crashkey_long_notifier, + crashkey_long_used_event, crashkey_long_nr_used_event); + else + sec_kn_unregister_notifier(&sec_crashkey_long_notifier, + crashkey_long_used_event, crashkey_long_nr_used_event); +} +EXPORT_SYMBOL(do_keyboard_notifier); +#endif + +static void __sec_crashkey_parse_dt_replace_keymap(void) +{ + struct device_node *parent, *node; + int err; + size_t i; + u32 resin_keycode, pwr_keycode; + + parent = of_find_node_by_path("/soc"); + if (!parent) + goto no_dt; + + node = of_find_node_by_name(parent, "sec_key_crash"); + if (!node) + goto no_dt; + + err = of_property_read_u32(node, "resin-keycode", &resin_keycode); + if (err) + goto no_dt; + + err = of_property_read_u32(node, "pwr-keycode", &pwr_keycode); + if (err) + goto no_dt; + + for (i = 0; i < crashkey_long_nr_used_event; i++) { + unsigned int *keycode = &crashkey_long_used_event[i]; + + switch (*keycode) { + case KEY_VOLUMEDOWN: + if (resin_keycode != KEY_VOLUMEDOWN) + *keycode = resin_keycode; + break; + case KEY_POWER: + if (pwr_keycode != KEY_POWER) + *keycode = pwr_keycode; + break; + } + } + pr_info("use dt keymap"); + return; + +no_dt: + pr_info("use default keymap"); +} + +static int sec_crashkey_long_init(void) +{ + unsigned long *bitmap_recieved; + + bitmap_recieved = bitmap_zalloc(KEY_MAX, GFP_KERNEL); + if (!bitmap_recieved) { + kfree(bitmap_recieved); + return -ENOMEM; + } + + __sec_crashkey_parse_dt_replace_keymap(); + + long_key_state->bitmap_recieved = bitmap_recieved; + long_key_state->sz_bitmap = + BITS_TO_LONGS(KEY_MAX) * sizeof(unsigned long); + + sec_kn_register_notifier(&sec_crashkey_long_notifier, + crashkey_long_used_event, crashkey_long_nr_used_event); + + return 0; +} +arch_initcall_sync(sec_crashkey_long_init); diff --git a/drivers/debug/sec_debug-dfd.c b/drivers/debug/sec_debug-dfd.c new file mode 100644 index 000000000000..ce9a96a64e10 --- /dev/null +++ b/drivers/debug/sec_debug-dfd.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int dfd_enable(const char *val, struct kernel_param *kp); +module_param_call(enable, dfd_enable, NULL, NULL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +/* Double free detector(dfd) can use lots of memory because + * it needs to hold on the freed slabs, otherwise + * the the freeing node will be put into the kmem cache + * of it's size and the slab allocator will soon re-allocate + * this slab when the slab of that size is requested. + * So to alleviate the pressure of the other shrinkers when + * there is a memory pressure, enable DFD_HAS_SHRINKER below. + */ +#define DFD_HAS_SHRINKER + +#ifdef DFD_HAS_SHRINKER +/* Using DFD shrinker will keep the DFD buffer entries low + * but at a cost. The page allocator will often go into + * the slowpath and try to reclaim pages and eventually call + * the shrinker. + * If you want to avoid this overhead, enable below feature + * to completely flush out the circular buffer and disable the + * DFD when there is a memory pressure */ +//#define DFD_SHRINKER_DISABLE_DFD_ON_MEM_PRESSURE +#endif + +#define KFREE_HOOK_BYPASS_MASK 0x1 +/* The average size of a slab object is about 256 bytes. + * 1<<15 number of slab objects take about 8MB to 10MB + * (This average was mesaured with a min slab size of 64) */ +#define KFREE_CIRC_BUF_SIZE (1<<15) +#define KFREE_FREE_MAGIC 0x65655266 + +static int dfd_panic = 1; +static int dfd_disabled; + +static DEFINE_SPINLOCK(dfd_list_lock); + +struct dfd_node { + void *addr; + void *caller; +}; + +struct dfd_node_list { + int head; + int tail; + struct dfd_node entry[KFREE_CIRC_BUF_SIZE]; +}; + +struct dfd_node_list dfd_node_list; + +static int __init setup_dfd_panic_disable(char *str) +{ + dfd_panic = 0; + return 1; +} +__setup("dfd_panic_disable", setup_dfd_panic_disable); + +/* the caller must hold the dfd_list_lock */ +static void *circ_buf_lookup(struct dfd_node_list *circ_buf, void *addr) +{ + int i; + for (i = circ_buf->tail; i != circ_buf->head ; + i = (i + 1) & (KFREE_CIRC_BUF_SIZE - 1)) { + if (circ_buf->entry[i].addr == addr) + return &circ_buf->entry[i]; + } + + return NULL; +} + +/* the caller must hold the dfd_list_lock and must check + * for the buffer status before calling */ +static void *circ_buf_get(struct dfd_node_list *circ_buf) +{ + void *entry; + entry = &circ_buf->entry[circ_buf->tail]; + smp_rmb(); + circ_buf->tail = (circ_buf->tail + 1) & + (KFREE_CIRC_BUF_SIZE - 1); + return entry; +} + +/* the caller must hold the dfd_list_lock and must check + * for the buffer status before calling */ +static void *circ_buf_put(struct dfd_node_list *circ_buf, + struct dfd_node *entry) +{ + memcpy(&circ_buf->entry[circ_buf->head], entry, sizeof(*entry)); + smp_wmb(); + circ_buf->head = (circ_buf->head + 1) & + (KFREE_CIRC_BUF_SIZE - 1); + return entry; +} + +static int dfd_flush(void) +{ + struct dfd_node *pentry; + unsigned long cnt; + unsigned long flags; + + spin_lock_irqsave(&dfd_list_lock, flags); + cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE); + spin_unlock_irqrestore(&dfd_list_lock, flags); + pr_debug("cnt=%lu\n", cnt); + +do_flush: + while (cnt) { + void *tofree = NULL; + /* we want to keep the lock region as short as possible + * so we will re-read the buf count every loop */ + spin_lock_irqsave(&dfd_list_lock, flags); + cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE); + if (cnt == 0) { + spin_unlock_irqrestore(&dfd_list_lock, flags); + break; + } + if ((pentry = circ_buf_get(&dfd_node_list)) != NULL) + tofree = pentry->addr; + spin_unlock_irqrestore(&dfd_list_lock, flags); + if (tofree) + kfree((void *)((unsigned long)tofree | + KFREE_HOOK_BYPASS_MASK)); + cnt--; + } + + spin_lock_irqsave(&dfd_list_lock, flags); + cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE); + spin_unlock_irqrestore(&dfd_list_lock, flags); + + if (!dfd_disabled) + goto out; + + if (cnt) + goto do_flush; + +out: + return cnt; +} + +static int dfd_enable(const char *val, struct kernel_param *kp) +{ + if (!strncmp(val, "1", 1)) { + dfd_disabled = 0; + pr_info("double free detection is enabled\n"); + } else if (!strncmp(val, "0", 1)) { + dfd_disabled = 1; + dfd_flush(); + pr_info("double free detection is disabled\n"); + } + + return 0; +} + +#ifdef DFD_HAS_SHRINKER +int dfd_shrink(struct shrinker *shrinker, struct shrink_control *sc) +{ +#ifndef DFD_SHRINKER_DISABLE_DFD_ON_MEM_PRESSURE + struct dfd_node *pentry; + unsigned long nr = sc->nr_to_scan; +#endif + unsigned long flags; + unsigned long nr_objs; + + spin_lock_irqsave(&dfd_list_lock, flags); + nr_objs = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE); + spin_unlock_irqrestore(&dfd_list_lock, flags); + + /* nothing to reclaim from here */ + if (nr_objs == 0) { + nr_objs = -1; + goto out; + } + +#ifdef DFD_SHRINKER_DISABLE_DFD_ON_MEM_PRESSURE + /* disable double free detection. This will flush + * the entire circular buffer out. */ + dfd_disable(); +#else + /* return max slab objects freeable */ + if (nr == 0) + return nr_objs; + + if (nr > nr_objs) + nr = nr_objs; + + pr_debug("nr_objs=%lu\n", nr_objs); + while (nr) { + unsigned long cnt; + void *tofree = NULL; + spin_lock_irqsave(&dfd_list_lock, flags); + cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE); + if (cnt > 0) { + if ((pentry = circ_buf_get(&dfd_node_list)) != NULL) + tofree = pentry->addr; + } + spin_unlock_irqrestore(&dfd_list_lock, flags); + if (tofree) + kfree((void *)((unsigned long)tofree | + KFREE_HOOK_BYPASS_MASK)); + nr--; + } +#endif + spin_lock_irqsave(&dfd_list_lock, flags); + nr_objs = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE); + spin_unlock_irqrestore(&dfd_list_lock, flags); + if (nr_objs == 0) { + pr_info("nothing more to reclaim from here!\n"); + nr_objs = -1; + } + +out: + return nr_objs; +} + +static struct shrinker dfd_shrinker = { + .shrink = dfd_shrink, + .seeks = DEFAULT_SEEKS +}; + +static int __init dfd_shrinker_init(void) +{ + register_shrinker(&dfd_shrinker); + return 0; +} + +static void __exit dfd_shrinker_exit(void) +{ + unregister_shrinker(&dfd_shrinker); +} + +module_init(dfd_shrinker_init); +module_exit(dfd_shrinker_exit); +#endif + +static inline int dfd_check_magic_any(void *addr) +{ + return (((unsigned int *)addr)[0] == KFREE_FREE_MAGIC || + ((unsigned int *)addr)[1] == KFREE_FREE_MAGIC || + ((unsigned int *)addr)[2] == KFREE_FREE_MAGIC || + ((unsigned int *)addr)[3] == KFREE_FREE_MAGIC); +} + +static inline int dfd_check_magic_all(void *addr) +{ + return (((unsigned int *)addr)[0] == KFREE_FREE_MAGIC && + ((unsigned int *)addr)[1] == KFREE_FREE_MAGIC && + ((unsigned int *)addr)[2] == KFREE_FREE_MAGIC && + ((unsigned int *)addr)[3] == KFREE_FREE_MAGIC); +} + +static inline void dfd_set_magic(void *addr) +{ + BUILD_BUG_ON(KMALLOC_MIN_SIZE < 16); + ((unsigned long *)addr)[0] = KFREE_FREE_MAGIC; + ((unsigned long *)addr)[1] = KFREE_FREE_MAGIC; + ((unsigned long *)addr)[2] = KFREE_FREE_MAGIC; + ((unsigned long *)addr)[3] = KFREE_FREE_MAGIC; +} + +static inline void dfd_clear_magic(void *addr) +{ + BUILD_BUG_ON(KMALLOC_MIN_SIZE < 16); + ((unsigned long *)addr)[0] = 0; + ((unsigned long *)addr)[1] = 0; + ((unsigned long *)addr)[2] = 0; + ((unsigned long *)addr)[3] = 0; +} + +static void __hexdump(void *mem, unsigned long size) +{ + #define WORDS_PER_LINE 4 + #define WORD_SIZE 4 + #define LINE_SIZE (WORDS_PER_LINE * WORD_SIZE) + #define LINE_BUF_SIZE (WORDS_PER_LINE * WORD_SIZE * 3 \ + + WORDS_PER_LINE + 4) + unsigned long addr; + char linebuf[LINE_BUF_SIZE]; + int numline = size / LINE_SIZE; + int i; + for (i = 0; i < numline; i++) { + addr = (unsigned long)mem + i * LINE_SIZE; + hex_dump_to_buffer((const void *)addr, + LINE_SIZE, LINE_SIZE, + WORD_SIZE, linebuf, sizeof(linebuf), 1); + pr_info(" %lx : %s\n", addr, linebuf); + } + +} + +void *kfree_hook(void *p, void *caller) +{ + unsigned long flags; + struct dfd_node *match = NULL; + void *tofree = NULL; + unsigned long addr = (unsigned long)p; + struct dfd_node entry; + struct dfd_node *pentry; + + if (!virt_addr_valid(addr)) { + /* there are too many NULL pointers so don't print for NULL */ + if (addr) + pr_debug("trying to free an invalid addr %lx" \ + "from %pS\n", addr, caller); + return NULL; + } + + if (addr & KFREE_HOOK_BYPASS_MASK || dfd_disabled) { + /* return original address to free */ + return (void *)(addr&~(KFREE_HOOK_BYPASS_MASK)); + } + + spin_lock_irqsave(&dfd_list_lock, flags); + + if (dfd_node_list.head == 0) + pr_debug("circular buffer head rounded to zero."); + + /* We can detect all the double free in the circular buffer time frame + * if we scan the whole circular buffer all the time, but to minimize + * the performance degradation we will just check for the magic values + * (the number of magic values can be up to KMALLOC_MIN_SIZE/4) */ + if (dfd_check_magic_any(p)) { + /* memory that is to be freed may originally have had magic + * value, so search the whole circ buf for an actual match */ + match = circ_buf_lookup(&dfd_node_list, p); + if (!match) { + pr_debug("magic set but not in circ buf\n"); + } + } + + if (match) { + pr_err("0x%08lx was already freed by %pS()\n", + (unsigned long)p, match->caller); + spin_unlock_irqrestore(&dfd_list_lock, flags); + if (dfd_panic) + panic("double free detected!"); + /* if we don't panic we just return without adding this entry + * to the circular buffer. This means that this kfree is ommited + * and we are just forgiving the double free */ + dump_stack(); + return NULL; + } + + /* mark free magic on the freeing node */ + dfd_set_magic(p); + + /* do an actual kfree for the oldest entry + * if the circular buffer is full */ + if (CIRC_SPACE(dfd_node_list.head, dfd_node_list.tail, + KFREE_CIRC_BUF_SIZE) == 0) { + pentry = circ_buf_get(&dfd_node_list); + if (pentry) + tofree = pentry->addr; + } + + /* add the new entry to the circular buffer */ + entry.addr = p; + entry.caller = caller; + circ_buf_put(&dfd_node_list, &entry); + if (tofree) { + if (unlikely(!dfd_check_magic_all(tofree))) { + pr_emerg("Use after free detected on the node " \ + "0x%lx which was freed by %pS.\n", \ + (unsigned long)tofree, + pentry->caller); + __hexdump((void *)tofree, KMALLOC_MIN_SIZE); + pr_err("\n"); + } + dfd_clear_magic(tofree); + spin_unlock_irqrestore(&dfd_list_lock, flags); + /* do the real kfree */ + kfree((void *)((unsigned long)tofree | KFREE_HOOK_BYPASS_MASK)); + return NULL; + } + + spin_unlock_irqrestore(&dfd_list_lock, flags); + return NULL; +} diff --git a/drivers/debug/sec_debug.c b/drivers/debug/sec_debug.c new file mode 100644 index 000000000000..18f42d01e3ad --- /dev/null +++ b/drivers/debug/sec_debug.c @@ -0,0 +1,1349 @@ +/* + * drivers/debug/sec_debug.c + * + * COPYRIGHT(C) 2006-2018 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +// [ SEC_SELINUX_PORTING_QUALCOMM +#include +// ] SEC_SELINUX_PORTING_QUALCOMM + +#define CREATE_TRACE_POINTS +#include + +#include "sec_debug_internal.h" + +#ifdef CONFIG_ARM64 +static inline void outer_flush_all(void) { } +#endif + +/* enable sec_debug feature */ +static unsigned int sec_dbg_level; +static int force_upload; + +static unsigned int enable = 1; +module_param_named(enable, enable, uint, 0644); + +static unsigned int enable_user = 1; +module_param_named(enable_user, enable_user, uint, 0644); + +static unsigned int runtime_debug_val; +module_param_named(runtime_debug_val, runtime_debug_val, uint, 0644); + +#ifdef CONFIG_SEC_SSR_DEBUG_LEVEL_CHK +static unsigned int enable_cp_debug = 1; +module_param_named(enable_cp_debug, enable_cp_debug, uint, 0644); +#endif + +#ifdef CONFIG_SEC_DEBUG_PWDT // checking platform watchdog +static unsigned long pwdt_start_ms = 0; +module_param_named(pwdt_start_ms, pwdt_start_ms, ulong, 0644); + +static unsigned long pwdt_end_ms = 0; +module_param_named(pwdt_end_ms, pwdt_end_ms, ulong, 0644); + +static unsigned int pwdt_pid = 0; +module_param_named(pwdt_pid, pwdt_pid, uint, 0644); + +static unsigned long pwdt_sync_cnt = 0; +module_param_named(pwdt_sync_cnt, pwdt_sync_cnt, ulong, 0644); +#endif + +/* This is shared with msm-power off module. */ +void __iomem *restart_reason; +static void __iomem *upload_cause; + +DEFINE_PER_CPU(struct sec_debug_core_t, sec_debug_core_reg); +DEFINE_PER_CPU(struct sec_debug_mmu_reg_t, sec_debug_mmu_reg); +DEFINE_PER_CPU(enum sec_debug_upload_cause_t, sec_debug_upload_cause); + +static long *g_allocated_phys_mem; +static long *g_allocated_virt_mem; + +static int sec_alloc_virtual_mem(const char *val, const struct kernel_param *kp) +{ + long *mem; + char *str = (char *) val; + size_t size = (size_t)memparse(str, &str); + + if (size) { + mem = vmalloc(size); + if (!mem) { + pr_err("Failed to allocate virtual memory of size: 0x%zx bytes\n", + size); + } else { + pr_info("Allocated virtual memory of size: 0x%zx bytes\n", + size); + *mem = (long)g_allocated_virt_mem; + g_allocated_virt_mem = mem; + + return 0; + } + } + + pr_info("Invalid size: %s bytes\n", val); + + return -EAGAIN; +} +static const struct kernel_param_ops alloc_virtual_mem_ops = { + .set = sec_alloc_virtual_mem, + .get = NULL, +}; +module_param_cb(alloc_virtual_mem, &alloc_virtual_mem_ops, NULL, 0644); + +static int sec_free_virtual_mem(const char *val, const struct kernel_param *kp) +{ + long *mem; + char *str = (char *) val; + size_t free_count = (size_t)memparse(str, &str); + + if (!free_count) { + if (strncmp(val, "all", 4)) { + free_count = 10; + } else { + pr_err("Invalid free count: %s\n", val); + return -EAGAIN; + } + } + + if (free_count > 10) + free_count = 10; + + if (!g_allocated_virt_mem) { + pr_err("No virtual memory chunk to free.\n"); + return 0; + } + + while (g_allocated_virt_mem && free_count--) { + mem = (long *) *g_allocated_virt_mem; + vfree(g_allocated_virt_mem); + g_allocated_virt_mem = mem; + } + + pr_info("Freed previously allocated virtual memory chunks.\n"); + + if (g_allocated_virt_mem) + pr_info("Still, some virtual memory chunks are not freed. Try again.\n"); + + return 0; +} +static const struct kernel_param_ops sec_free_virtual_mem_ops = { + .set = sec_free_virtual_mem, + .get = NULL, +}; +module_param_cb(free_virtual_mem, &sec_free_virtual_mem_ops, NULL, 0644); + +static int sec_alloc_physical_mem(const char *val, + const struct kernel_param *kp) +{ + long *mem; + char *str = (char *) val; + size_t size = (size_t)memparse(str, &str); + + if (size) { + mem = kmalloc(size, GFP_KERNEL); + if (!mem) { + pr_err("Failed to allocate physical memory of size: 0x%zx bytes\n", + size); + } else { + pr_info("Allocated physical memory of size: 0x%zx bytes\n", + size); + *mem = (long) g_allocated_phys_mem; + g_allocated_phys_mem = mem; + + return 0; + } + } + + pr_info("Invalid size: %s bytes\n", val); + + return -EAGAIN; +} +static const struct kernel_param_ops sec_alloc_physical_mem_ops = { + .set = sec_alloc_physical_mem, + .get = NULL, +}; +module_param_cb(alloc_physical_mem, &sec_alloc_physical_mem_ops, NULL, 0644); + +static int sec_free_physical_mem(const char *val, const struct kernel_param *kp) +{ + long *mem; + char *str = (char *) val; + size_t free_count = (size_t)memparse(str, &str); + + if (!free_count) { + if (strncmp(val, "all", 4)) { + free_count = 10; + } else { + pr_info("Invalid free count: %s\n", val); + return -EAGAIN; + } + } + + if (free_count > 10) + free_count = 10; + + if (!g_allocated_phys_mem) { + pr_info("No physical memory chunk to free.\n"); + return 0; + } + + while (g_allocated_phys_mem && free_count--) { + mem = (long *) *g_allocated_phys_mem; + kfree(g_allocated_phys_mem); + g_allocated_phys_mem = mem; + } + + pr_info("Freed previously allocated physical memory chunks.\n"); + + if (g_allocated_phys_mem) + pr_info("Still, some physical memory chunks are not freed. Try again.\n"); + + return 0; +} +static const struct kernel_param_ops sec_free_physical_mem_ops = { + .set = sec_free_physical_mem, + .get = NULL, +}; +module_param_cb(free_physical_mem, &sec_free_physical_mem_ops, NULL, 0644); + +static int dbg_set_cpu_affinity(const char *val, const struct kernel_param *kp) +{ + char *endptr; + pid_t pid; + int cpu; + struct cpumask mask; + long ret; + + pid = (pid_t)memparse(val, &endptr); + if (*endptr != '@') { + pr_info("invalid input strin: %s\n", val); + return -EINVAL; + } + + cpu = (int)memparse(++endptr, &endptr); + cpumask_clear(&mask); + cpumask_set_cpu(cpu, &mask); + pr_info("Setting %d cpu affinity to cpu%d\n", pid, cpu); + + ret = sched_setaffinity(pid, &mask); + pr_info("sched_setaffinity returned %ld\n", ret); + + return 0; +} +static const struct kernel_param_ops dbg_set_cpu_affinity_ops = { + .set = dbg_set_cpu_affinity, + .get = NULL, +}; +module_param_cb(setcpuaff, &dbg_set_cpu_affinity_ops, NULL, 0644); + +static void sec_debug_set_qc_dload_magic(int on) +{ + pr_info("on=%d\n", on); + set_dload_mode(on); +} + +#define PON_RESTART_REASON_NOT_HANDLE PON_RESTART_REASON_MAX +#define RESTART_REASON_NOT_HANDLE RESTART_REASON_END + +/* This is shared with 'msm-poweroff.c' module. */ +static enum sec_restart_reason_t __iomem *qcom_restart_reason; + +static void sec_debug_set_upload_magic(unsigned int magic) +{ + __pr_err("(%s) %x\n", __func__, magic); + + if (magic) + sec_debug_set_qc_dload_magic(1); + __raw_writel(magic, qcom_restart_reason); + + flush_cache_all(); + outer_flush_all(); +} + +static int sec_debug_normal_reboot_handler(struct notifier_block *nb, + unsigned long action, void *data) +{ + char recovery_cause[256]; + + set_dload_mode(0); /* set defalut (not upload mode) */ + + sec_debug_set_upload_magic(RESTART_REASON_NORMAL); + + if (unlikely(!data)) + return 0; + + if ((action == SYS_RESTART) && + !strncmp((char *)data, "recovery", 8)) { + sec_get_param(param_index_reboot_recovery_cause, + recovery_cause); + if (!recovery_cause[0] || !strlen(recovery_cause)) { + snprintf(recovery_cause, sizeof(recovery_cause), + "%s:%d ", current->comm, task_pid_nr(current)); + sec_set_param(param_index_reboot_recovery_cause, + recovery_cause); + } + } + + return 0; +} + +void sec_debug_update_dload_mode(const int restart_mode, const int in_panic) +{ +#ifdef CONFIG_SEC_DEBUG_LOW_LOG + if (sec_debug_is_enabled() && + ((restart_mode == RESTART_DLOAD) || in_panic)) + set_dload_mode(1); + else + set_dload_mode(0); +#else + /* FIXME: dead code? */ + /* set_dload_mod((RESTART_DLOAD == restart_mode) || in_panic); */ +#endif +} + +static inline void __sec_debug_set_restart_reason( + enum sec_restart_reason_t __r) +{ + __raw_writel((u32)__r, qcom_restart_reason); +} + +static enum pon_restart_reason __pon_restart_pory_start( + unsigned long opt_code) +{ + return (PON_RESTART_REASON_RORY_START | opt_code); +} + +static enum pon_restart_reason __pon_restart_set_debug_level( + unsigned long opt_code) +{ + switch (opt_code) { + case ANDROID_DEBUG_LEVEL_LOW: + return PON_RESTART_REASON_DBG_LOW; + case ANDROID_DEBUG_LEVEL_MID: + return PON_RESTART_REASON_DBG_MID; + case ANDROID_DEBUG_LEVEL_HIGH: + return PON_RESTART_REASON_DBG_HIGH; + } + + return PON_RESTART_REASON_UNKNOWN; +} + +static enum pon_restart_reason __pon_restart_set_cpdebug( + unsigned long opt_code) +{ + if (opt_code == ANDROID_CP_DEBUG_ON) + return PON_RESTART_REASON_CP_DBG_ON; + else if (opt_code == ANDROID_CP_DEBUG_OFF) + return PON_RESTART_REASON_CP_DBG_OFF; + + return PON_RESTART_REASON_UNKNOWN; +} + +static enum pon_restart_reason __pon_restart_force_upload( + unsigned long opt_code) +{ + return (opt_code) ? + PON_RESTART_REASON_FORCE_UPLOAD_ON : + PON_RESTART_REASON_FORCE_UPLOAD_OFF; +} + +#ifdef CONFIG_MUIC_SUPPORT_RUSTPROOF +static enum pon_restart_reason __pon_restart_swsel( + unsigned long opt_code) +{ + unsigned long value = + (((opt_code & 0x8) >> 1) | opt_code) & 0x7; + + return (PON_RESTART_REASON_SWITCHSEL | value); +} +#endif + +void sec_debug_update_restart_reason(const char *cmd, const int in_panic) +{ + struct __magic { + const char *cmd; + enum pon_restart_reason pon_rr; + enum sec_restart_reason_t sec_rr; + enum pon_restart_reason (*func)(unsigned long opt_code); + } magic[] = { + { "sec_debug_hw_reset", + PON_RESTART_REASON_NOT_HANDLE, + RESTART_REASON_SEC_DEBUG_MODE, NULL }, + { "download", + PON_RESTART_REASON_DOWNLOAD, + RESTART_REASON_NOT_HANDLE, NULL }, + { "nvbackup", + PON_RESTART_REASON_NVBACKUP, + RESTART_REASON_NOT_HANDLE, NULL }, + { "nvrestore", + PON_RESTART_REASON_NVRESTORE, + RESTART_REASON_NOT_HANDLE, NULL }, + { "nverase", + PON_RESTART_REASON_NVERASE, + RESTART_REASON_NOT_HANDLE, NULL }, + { "nvrecovery", + PON_RESTART_REASON_NVRECOVERY, + RESTART_REASON_NOT_HANDLE, NULL }, + { "cpmem_on", + PON_RESTART_REASON_CP_MEM_RESERVE_ON, + RESTART_REASON_NOT_HANDLE, NULL }, + { "cpmem_off", + PON_RESTART_REASON_CP_MEM_RESERVE_OFF, + RESTART_REASON_NOT_HANDLE, NULL }, + { "mbsmem_on", + PON_RESTART_REASON_MBS_MEM_RESERVE_ON, + RESTART_REASON_NOT_HANDLE, NULL }, + { "mbsmem_off", + PON_RESTART_REASON_MBS_MEM_RESERVE_OFF, + RESTART_REASON_NOT_HANDLE, NULL }, + { "GlobalActions restart", + PON_RESTART_REASON_NORMALBOOT, + RESTART_REASON_NOT_HANDLE, NULL }, + { "userrequested", + PON_RESTART_REASON_NORMALBOOT, + RESTART_REASON_NOT_HANDLE, NULL }, + { "silent.sec", + PON_RESTART_REASON_NORMALBOOT, + RESTART_REASON_NOT_HANDLE, NULL }, + { "oem-", + PON_RESTART_REASON_UNKNOWN, + RESTART_REASON_NORMAL, NULL }, + { "sud", + PON_RESTART_REASON_UNKNOWN, + RESTART_REASON_NORMAL, __pon_restart_pory_start }, + { "debug", + PON_RESTART_REASON_UNKNOWN, + RESTART_REASON_NORMAL, __pon_restart_set_debug_level }, + { "cpdebug", + PON_RESTART_REASON_UNKNOWN, + RESTART_REASON_NORMAL, __pon_restart_set_cpdebug }, + { "forceupload", + PON_RESTART_REASON_UNKNOWN, + RESTART_REASON_NORMAL, __pon_restart_force_upload }, +#ifdef CONFIG_MUIC_SUPPORT_RUSTPROOF + { "swsel", + PON_RESTART_REASON_UNKNOWN, + RESTART_REASON_NORMAL, __pon_restart_swsel }, +#endif + }; + enum pon_restart_reason pon_rr = (!in_panic) ? + PON_RESTART_REASON_NORMALBOOT : + PON_RESTART_REASON_KERNEL_PANIC; + enum sec_restart_reason_t sec_rr = RESTART_REASON_NORMAL; + char cmd_buf[16]; + size_t i; + + if (!cmd || !strlen(cmd)) + goto __done; + + for (i = 0; i < ARRAY_SIZE(magic); i++) { + size_t len = strlen(magic[i].cmd); + + if (strncmp(cmd, magic[i].cmd, len)) + continue; + + pon_rr = magic[i].pon_rr; + sec_rr = magic[i].sec_rr; + + if (magic[i].func != NULL) { + unsigned long opt_code; + + if (!kstrtoul(cmd + len, 0, &opt_code)) + pon_rr = magic[i].func(opt_code); + } + + goto __done; + } + + + strlcpy(cmd_buf, cmd, ARRAY_SIZE(cmd_buf)); + __pr_err("(%s) unknown reboot command : %s\n", + __func__, cmd_buf); + + __sec_debug_set_restart_reason(RESTART_REASON_REBOOT); + + return; + +__done: + if (pon_rr != PON_RESTART_REASON_NOT_HANDLE) + qpnp_pon_set_restart_reason(pon_rr); + if (sec_rr != RESTART_REASON_NOT_HANDLE) + __sec_debug_set_restart_reason(sec_rr); + __pr_err("(%s) restart_reason = 0x%x(0x%x)\n", + __func__, sec_rr, pon_rr); +} + +void sec_debug_set_upload_cause(enum sec_debug_upload_cause_t type) +{ + if (unlikely(!upload_cause)) { + pr_err("upload cause address unmapped.\n"); + } else { + per_cpu(sec_debug_upload_cause, smp_processor_id()) = type; + __raw_writel(type, upload_cause); + + pr_emerg("%x\n", type); + } +} +EXPORT_SYMBOL(sec_debug_set_upload_cause); + +enum sec_debug_upload_cause_t sec_debug_get_upload_cause(void) +{ + if (unlikely(!upload_cause)) { + pr_err("upload cause address unmapped.\n"); + return UPLOAD_CAUSE_INIT; + } + + return readl(upload_cause); +} +EXPORT_SYMBOL(sec_debug_get_upload_cause); + +static inline void sec_debug_pm_restart(const char *cmd) +{ + __pr_err("(%s) %s %s\n", __func__, + init_uts_ns.name.release, init_uts_ns.name.version); + __pr_err("(%s) rebooting...\n", __func__); + flush_cache_all(); + outer_flush_all(); + + arm_pm_restart(REBOOT_COLD, cmd); + + /* while (1) ; */ + asm volatile ("b ."); +} + +void sec_debug_hw_reset(void) +{ + sec_debug_pm_restart("sec_debug_hw_reset"); +} +EXPORT_SYMBOL(sec_debug_hw_reset); + +#ifdef CONFIG_SEC_PERIPHERAL_SECURE_CHK +void sec_peripheral_secure_check_fail(void) +{ + if (!sec_debug_is_enabled()) { + sec_debug_set_qc_dload_magic(0); + sec_debug_pm_restart("peripheral_hw_reset"); + /* never reach here */ + } + + panic("subsys - modem secure check fail"); +} +EXPORT_SYMBOL(sec_peripheral_secure_check_fail); +#endif + +void sec_debug_set_thermal_upload(void) +{ + pr_emerg("set thermal upload cause\n"); + sec_debug_set_upload_magic(0x776655ee); + sec_debug_set_upload_cause(UPLOAD_CAUSE_POWER_THERMAL_RESET); +} +EXPORT_SYMBOL(sec_debug_set_thermal_upload); + +void sec_debug_print_model(struct seq_file *m, const char *cpu_name) +{ + u32 cpuid = read_cpuid_id(); + + seq_printf(m, "model name\t: %s rev %d (%s)\n", + cpu_name, cpuid & 15, ELF_PLATFORM); +} + +#ifdef CONFIG_USER_RESET_DEBUG +static inline void sec_debug_store_backtrace(void) +{ + unsigned long flags; + + local_irq_save(flags); + sec_debug_backtrace(); + local_irq_restore(flags); +} +#endif /* CONFIG_USER_RESET_DEBUG */ + +enum sec_debug_strncmp_func { + SEC_STRNCMP = 0, + SEC_STRNSTR, + SEC_STRNCASECMP, +}; + +static inline bool __sec_debug_strncmp(const char *s1, const char *s2, + size_t len, enum sec_debug_strncmp_func func) +{ + switch (func) { + case SEC_STRNCMP: + return !strncmp(s1, s2, len); + case SEC_STRNSTR: + return !!strnstr(s1, s2, len); + case SEC_STRNCASECMP: + return !strncasecmp(s1, s2, len); + } + + pr_warn("%d is not a valid strncmp function!\n", (int)func); + + return false; +} + +static int sec_debug_panic_handler(struct notifier_block *nb, + unsigned long l, void *buf) +{ +#define MAX_STR_LEN 80 + size_t len, i; + int timeout = 100; /* means timeout * 100ms */ + struct __upload_cause { + const char *msg; + enum sec_debug_upload_cause_t type; + enum sec_debug_strncmp_func func; + } upload_cause[] = { + { UPLOAD_MSG_USER_FAULT, UPLOAD_CAUSE_USER_FAULT, SEC_STRNCMP }, + { UPLOAD_MSG_CRASH_KEY, UPLOAD_CAUSE_FORCED_UPLOAD, + SEC_STRNCMP }, + { UPLOAD_MSG_USER_CRASH_KEY, UPLOAD_CAUSE_USER_FORCED_UPLOAD, + SEC_STRNCMP }, + { UPLOAD_MSG_LONG_KEY_PRESS, UPLOAD_CAUSE_POWER_LONG_PRESS, + SEC_STRNCMP }, + { "CP Crash", UPLOAD_CAUSE_CP_ERROR_FATAL, SEC_STRNCMP }, + { "MDM Crash", UPLOAD_CAUSE_MDM_ERROR_FATAL, SEC_STRNCMP }, + { "external_modem", UPLOAD_CAUSE_MDM_ERROR_FATAL, SEC_STRNSTR }, + { "esoc0 crashed", UPLOAD_CAUSE_MDM_ERROR_FATAL, SEC_STRNSTR }, + { "modem", UPLOAD_CAUSE_MODEM_RST_ERR, SEC_STRNSTR }, + { "riva", UPLOAD_CAUSE_RIVA_RST_ERR, SEC_STRNSTR }, + { "lpass", UPLOAD_CAUSE_LPASS_RST_ERR, SEC_STRNSTR }, + { "dsps", UPLOAD_CAUSE_DSPS_RST_ERR, SEC_STRNSTR }, + { "subsys", UPLOAD_CAUSE_PERIPHERAL_ERR, SEC_STRNCASECMP }, +#if defined(CONFIG_SEC_QUEST) + { "crypto_test", UPLOAD_CAUSE_QUEST_CRYPTO, SEC_STRNCMP }, + { "icache_test", UPLOAD_CAUSE_QUEST_ICACHE, SEC_STRNCMP }, + { "cachecoherency_test", UPLOAD_CAUSE_QUEST_CACHECOHERENCY, + SEC_STRNCMP }, + { "suspend_test", UPLOAD_CAUSE_QUEST_SUSPEND, SEC_STRNCMP }, + { "vddmin_test", UPLOAD_CAUSE_QUEST_VDDMIN, SEC_STRNCMP }, + { "qmesa_ddr_test", UPLOAD_CAUSE_QUEST_QMESADDR, SEC_STRNCMP }, + { "qmesa_ddr_test_cache_centric", UPLOAD_CAUSE_QUEST_QMESACACHE, + SEC_STRNCMP }, + { "pmic_test", UPLOAD_CAUSE_QUEST_PMIC, SEC_STRNCMP }, + { "ufs_test", UPLOAD_CAUSE_QUEST_UFS, SEC_STRNCMP }, + { "sdcard_test", UPLOAD_CAUSE_QUEST_SDCARD, SEC_STRNCMP }, + { "sensor_test", UPLOAD_CAUSE_QUEST_SENSOR, SEC_STRNCMP }, + { "gfx_test", UPLOAD_CAUSE_QUEST_GFX, SEC_STRNCMP }, + { "qdaf_fail", UPLOAD_CAUSE_QUEST_QDAF_FAIL, SEC_STRNCMP }, + { "nad_fail", UPLOAD_CAUSE_QUEST_FAIL, SEC_STRNCMP }, +#endif + }; + + emerg_pet_watchdog(); /* CTC-should be modify */ +#ifdef CONFIG_USER_RESET_DEBUG + sec_debug_store_backtrace(); +#endif + sec_debug_set_upload_magic(RESTART_REASON_SEC_DEBUG_MODE); + + __pr_err("%s :%s\n", __func__, (char *)buf); + + for (i = 0; i < ARRAY_SIZE(upload_cause); i++) { + len = strnlen(buf, MAX_STR_LEN); + if (__sec_debug_strncmp(buf, upload_cause[i].msg, len, + upload_cause[i].func)) { + sec_debug_set_upload_cause(upload_cause[i].type); + break; + } + } + + if (i == ARRAY_SIZE(upload_cause)) + sec_debug_set_upload_cause(UPLOAD_CAUSE_KERNEL_PANIC); + + if (!sec_debug_is_enabled()) { +#ifdef CONFIG_SEC_DEBUG_LOW_LOG + sec_debug_hw_reset(); +#endif + /* SEC will get reset_summary.html in debug low. + * reset_summary.html need more information about abnormal reset + * or kernel panic. + * So we skip as below + */ + /* return -EPERM; */ + } + + /* enable after SSR feature */ + /* ssr_panic_handler_for_sec_dbg(); */ + + /* wait for all cpus to be deactivated */ + while (num_active_cpus() > 1 && timeout--) { + touch_nmi_watchdog(); + mdelay(100); + } + + /* save context here so that function call after this point doesn't + * corrupt stacks below the saved sp + */ + sec_debug_save_context(); + sec_debug_hw_reset(); + + return 0; +} + +void sec_debug_prepare_for_wdog_bark_reset(void) +{ + sec_debug_set_upload_magic(RESTART_REASON_SEC_DEBUG_MODE); + sec_debug_set_upload_cause(UPLOAD_CAUSE_NON_SECURE_WDOG_BARK); +} + +static struct notifier_block nb_reboot_block = { + .notifier_call = sec_debug_normal_reboot_handler, +}; + +static struct notifier_block nb_panic_block = { + .notifier_call = sec_debug_panic_handler, + .priority = -1, +}; + +#ifdef CONFIG_OF +static int __init __sec_debug_dt_addr_init(void) +{ + struct device_node *np; + + /* Using bottom of sec_dbg DDR address range + * for writing restart reason + */ + np = of_find_compatible_node(NULL, NULL, + "qcom,msm-imem-restart_reason"); + if (unlikely(!np)) { + pr_err("unable to find DT imem restart reason node\n"); + return -ENODEV; + } + + qcom_restart_reason = of_iomap(np, 0); + if (unlikely(!qcom_restart_reason)) { + pr_err("unable to map imem restart reason offset\n"); + return -ENODEV; + } + + /* check restart_reason address here */ + pr_emerg("restart_reason addr : 0x%p(0x%llx)\n", + qcom_restart_reason, + (uint64_t)virt_to_phys(qcom_restart_reason)); + + /* Using bottom of sec_dbg DDR address range for writing upload_cause */ + np = of_find_compatible_node(NULL, NULL, "qcom,msm-imem-upload_cause"); + if (unlikely(!np)) { + pr_err("unable to find DT imem upload cause node\n"); + return -ENODEV; + } + + upload_cause = of_iomap(np, 0); + if (unlikely(!upload_cause)) { + pr_err("unable to map imem upload_cause offset\n"); + return -ENODEV; + } + + /* check upload_cause here */ + pr_emerg("upload_cause addr : 0x%p(0x%llx)\n", upload_cause, + (uint64_t)virt_to_phys(upload_cause)); + + return 0; +} +#else /* CONFIG_OF */ +static int __init __sec_debug_dt_addr_init(void) { return 0; } +#endif /* CONFIG_OF */ + +#define SCM_WDOG_DEBUG_BOOT_PART 0x9 + +void sec_do_bypass_sdi_execution_in_low(void) +{ + int ret; + struct scm_desc desc = { + .args[0] = 1, + .args[1] = 0, + .arginfo = SCM_ARGS(2), + }; + + /* Needed to bypass debug image on some chips */ + if (!is_scm_armv8()) + ret = scm_call_atomic2(SCM_SVC_BOOT, + SCM_WDOG_DEBUG_BOOT_PART, 1, 0); + else + ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT, + SCM_WDOG_DEBUG_BOOT_PART), &desc); + if (ret) + pr_err("Failed to disable wdog debug: %d\n", ret); +} + +static char ap_serial_from_cmdline[20]; + +static int __init ap_serial_setup(char *str) +{ + snprintf(ap_serial_from_cmdline, + sizeof(ap_serial_from_cmdline), "%s", &str[2]); + return 1; +} +__setup("androidboot.ap_serial=", ap_serial_setup); + +static int __init force_upload_setup(char *en) +{ + get_option(&en, &force_upload); + return 1; +} +__setup("androidboot.force_upload=", force_upload_setup); + +/* for sec debug level */ +static int __init sec_debug_level_setup(char *str) +{ + get_option(&str, &sec_dbg_level); + return 1; +} +__setup("androidboot.debug_level=", sec_debug_level_setup); + +struct bus_type chip_id_subsys = { + .name = "chip-id", + .dev_name = "chip-id", +}; + +static ssize_t ap_serial_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ +#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP + pr_info("%s: ap_serial:[%s]\n", __func__, ap_serial_from_cmdline); +#endif + return snprintf(buf, sizeof(ap_serial_from_cmdline), + "%s\n", ap_serial_from_cmdline); +} + +static struct kobj_attribute sysfs_SVC_AP_attr = +__ATTR(SVC_AP, 0444, ap_serial_show, NULL); + +static struct kobj_attribute chipid_unique_id_attr = +__ATTR(unique_id, 0444, ap_serial_show, NULL); + +static struct attribute *chip_id_attrs[] = { + &chipid_unique_id_attr.attr, + NULL, +}; + +static struct attribute_group chip_id_attr_group = { + .attrs = chip_id_attrs, +}; + +static const struct attribute_group *chip_id_attr_groups[] = { + &chip_id_attr_group, + NULL, +}; + +void create_ap_serial_node(void) +{ + int ret; + struct kernfs_node *svc_sd; + struct kobject *svc; + struct kobject *AP; + + /* create /sys/devices/system/chip-id/unique_id */ + ret = subsys_system_register(&chip_id_subsys, chip_id_attr_groups); + if (ret) + pr_err("Failed to register subsystem-%s\n", + chip_id_subsys.name); + + /* To find /sys/devices/svc/ */ + svc = kobject_create_and_add("svc", &devices_kset->kobj); + if (IS_ERR_OR_NULL(svc)) { + svc_sd = sysfs_get_dirent(devices_kset->kobj.sd, "svc"); + if (!svc_sd) { + pr_err("Failed to create sys/devices/svc\n"); + return; + } + svc = (struct kobject *)svc_sd->priv; + } + + /* create /sys/devices/svc/AP */ + AP = kobject_create_and_add("AP", svc); + if (IS_ERR_OR_NULL(AP)) { + pr_err("Failed to create sys/devices/svc/AP\n"); + goto error_create_AP; + } + + /* create /sys/devices/svc/AP/SVC_AP */ + ret = sysfs_create_file(AP, &sysfs_SVC_AP_attr.attr); + if (ret) { + pr_err("sysfs create fail-%s\n", sysfs_SVC_AP_attr.attr.name); + goto error_create_sysfs; + } + + pr_info("Completed\n"); + return; + +error_create_sysfs: + kobject_put(AP); +error_create_AP: + kobject_put(svc); +} + +int __init sec_debug_init(void) +{ + int ret; +// [ SEC_SELINUX_PORTING_QUALCOMM +#ifdef CONFIG_PROC_AVC + sec_avc_log_init(); +#endif +// ] SEC_SELINUX_PORTING_QUALCOMM + + ret = __sec_debug_dt_addr_init(); + if (unlikely(ret < 0)) + return ret; + + register_reboot_notifier(&nb_reboot_block); + atomic_notifier_chain_register(&panic_notifier_list, &nb_panic_block); + + sec_debug_set_upload_magic(RESTART_REASON_SEC_DEBUG_MODE); + sec_debug_set_upload_cause(UPLOAD_CAUSE_INIT); + + create_ap_serial_node(); + + /* TODO: below code caused reboot fail when debug level LOW */ + switch (sec_dbg_level) { + case ANDROID_DEBUG_LEVEL_LOW: +#ifdef CONFIG_SEC_FACTORY + case ANDROID_DEBUG_LEVEL_MID: +#endif + if (!force_upload) + sec_do_bypass_sdi_execution_in_low(); + break; + } + + return 0; +} +arch_initcall_sync(sec_debug_init); + +bool sec_debug_is_enabled(void) +{ + switch (sec_dbg_level) { + case ANDROID_DEBUG_LEVEL_LOW: +#ifdef CONFIG_SEC_FACTORY + case ANDROID_DEBUG_LEVEL_MID: +#endif + return !!(force_upload); + } + + return !!(enable); +} +EXPORT_SYMBOL(sec_debug_is_enabled); + +unsigned int sec_debug_level(void) +{ + return sec_dbg_level; +} +EXPORT_SYMBOL(sec_debug_level); + +#ifdef CONFIG_SEC_SSR_DEBUG_LEVEL_CHK +int sec_debug_is_enabled_for_ssr(void) +{ + return enable_cp_debug; +} +#endif + +#ifdef CONFIG_SEC_FILE_LEAK_DEBUG +int sec_debug_print_file_list(void) +{ + size_t i; + unsigned int nCnt; + struct file *file; + struct files_struct *files = current->files; + const char *pRootName; + const char *pFileName; + int ret = 0; + + nCnt = files->fdt->max_fds; + + pr_err(" [Opened file list of process %s(PID:%d, TGID:%d) :: %d]\n", + current->group_leader->comm, current->pid, current->tgid, nCnt); + + for (i = 0; i < nCnt; i++) { + rcu_read_lock(); + file = fcheck_files(files, i); + + pRootName = NULL; + pFileName = NULL; + + if (file) { + if (file->f_path.mnt && + file->f_path.mnt->mnt_root && + file->f_path.mnt->mnt_root->d_name.name) + pRootName = + file->f_path.mnt->mnt_root->d_name.name; + + if (file->f_path.dentry && + file->f_path.dentry->d_name.name) + pFileName = file->f_path.dentry->d_name.name; + + pr_err("[%04zd]%s%s\n", i, + pRootName ? pRootName : "null", + pFileName ? pFileName : "null"); + ret++; + } + rcu_read_unlock(); + } + + return (ret == nCnt) ? 1 : 0; +} + +void sec_debug_EMFILE_error_proc(void) +{ + pr_err("Too many open files(%d:%s)\n", + current->tgid, current->group_leader->comm); + + if (!sec_debug_is_enabled()) + return; + + /* We check EMFILE error in only "system_server", + * "mediaserver" and "surfaceflinger" process. + */ + if (!strcmp(current->group_leader->comm, "system_server") || + !strcmp(current->group_leader->comm, "mediaserver") || + !strcmp(current->group_leader->comm, "surfaceflinger")) { + if (sec_debug_print_file_list() == 1) + panic("Too many open files"); + } +} +#endif /* CONFIG_SEC_FILE_LEAK_DEBUG */ + +#ifdef CONFIG_SEC_DEBUG_PWDT +void sec_debug_check_pwdt(void) +{ + //struct task_struct *tmp_tsk = NULL; + struct task_struct *wdt_tsk = NULL; + + static unsigned long pwdt_sync_delay = 0; + static unsigned long pwdt_restart_delay = 0; + static unsigned long pwdt_init_delay = 0; + static unsigned long last_sync_cnt = 0; + + if (is_boot_recovery() || is_boot_lpm()) { + return; + } + + if (is_verifiedboot_state()) { + return; + } + + pr_info("pid[%d], start_ms[%ld], sync_cnt[%ld], restart_delay[%ld], init_dealy[%ld], sync_delay[%ld]\n", + pwdt_pid, pwdt_start_ms, pwdt_sync_cnt, pwdt_restart_delay, pwdt_init_delay, pwdt_sync_delay); + + // when pwdt is not initialized + if (pwdt_pid == 0 && pwdt_start_ms == 0) + { + // more than 2000secs + if (pwdt_init_delay++ >= SEC_DEBUG_MAX_PWDT_INIT_CNT) { + panic("Platform Watchdog couldnot be initialized"); + } + } + // when pwdt is killed + else if (pwdt_pid != 0 && pwdt_start_ms == 0) + { + if (pwdt_restart_delay == 0) + pr_info("pwdt has been killed!!, start_ms[%ld], end_ms[%ld]\n", pwdt_start_ms, pwdt_end_ms); + + // if pwdt cannot be restarted after 200 seconds since pwdt has been killed, kernel watchdog will trigger Panic + if (pwdt_restart_delay++ >= SEC_DEBUG_MAX_PWDT_RESTART_CNT) { + panic("Platform Watchdog couldnot be restarted"); + } + } + // when pwdt is alive + else { + pwdt_init_delay = 0; + pwdt_restart_delay = 0; + rcu_read_lock(); + + wdt_tsk = find_task_by_vpid(pwdt_pid); + + /* if cannot find platform watchdog thread, + it might be killed by system_crash or zygote, We ignored this case. + */ + if (wdt_tsk == NULL) { + rcu_read_unlock(); + last_sync_cnt = pwdt_sync_cnt; + pwdt_sync_delay = 0; + pr_info("cannot find watchdog thread!!\n"); + return; + } + + get_task_struct(wdt_tsk); + rcu_read_unlock(); + + if (unlikely(frozen(wdt_tsk) || freezing(wdt_tsk))) { + // clear delay if watchdog thread is frozen or freezing + pr_info("wdt_task is frozen : [%d],[%d]\n", frozen(wdt_tsk), freezing(wdt_tsk)); + last_sync_cnt = pwdt_sync_cnt; + pwdt_sync_delay = 0; + } + else { + if (last_sync_cnt == pwdt_sync_cnt) { + /* pwdt_sync_cnt is updated in every 30s, but sec_debug_check_pwdt is invoked in every 10s + kernel watchdog will trigger Panic if platform watchdog couldnot update sync_cnt for 400secs + */ + if (pwdt_sync_delay++ >= SEC_DEBUG_MAX_PWDT_SYNC_CNT) { + put_task_struct(wdt_tsk); + panic("Platform Watchdog can't update sync_cnt"); + return; + } + } + else { + last_sync_cnt = pwdt_sync_cnt; + pwdt_sync_delay = 0; + } + } + put_task_struct(wdt_tsk); + } + + return; +} +#endif + +static void sec_user_fault_dump(void) +{ + if (sec_debug_is_enabled() && enable_user) + panic(UPLOAD_MSG_USER_FAULT); +} + +static ssize_t sec_user_fault_write(struct file *file, + const char __user *buffer, size_t count, loff_t *offs) +{ + char buf[100]; + + if (count > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + + buf[count] = '\0'; + if (!strncmp(buf, "dump_user_fault", 15)) + sec_user_fault_dump(); + + return count; +} + +static const struct file_operations sec_user_fault_proc_fops = { + .write = sec_user_fault_write, +}; + +static int __init sec_debug_user_fault_init(void) +{ + struct proc_dir_entry *entry; + + entry = proc_create("user_fault", 0220, NULL, + &sec_user_fault_proc_fops); + if (!entry) + return -ENOMEM; + + return 0; +} +device_initcall(sec_debug_user_fault_init); + +static inline void __dump_one_task_info(struct task_struct *tsk, + bool is_process) +{ + char stat_array[] = { 'R', 'S', 'D' }; + char stat_ch; + + stat_ch = tsk->state <= TASK_UNINTERRUPTIBLE ? + stat_array[tsk->state] : '?'; + __pr_info("%8d %8llu %8llu %16llu %c (%ld) %p %c %s\n", + tsk->pid, (unsigned long long)tsk->utime, + (unsigned long long)tsk->stime, + tsk->se.exec_start, stat_ch, tsk->state, + tsk, is_process ? '*' : ' ', tsk->comm); + + if (tsk->state == TASK_RUNNING || tsk->state == TASK_UNINTERRUPTIBLE) + show_stack(tsk, NULL); +} + +#define __HLINE_LEFT " -------------------------------------------" +#define __HLINE_RIGHT "--------------------------------------------\n" + +void dump_all_task_info(void) +{ + struct task_struct *p; + struct task_struct *t; + + show_mem(0); + dump_tasks(NULL, NULL); + __pr_info("\n"); + __pr_info(" current proc : %d %s\n", + current->pid, current->comm); + __pr_info(__HLINE_LEFT __HLINE_RIGHT); + __pr_info("%8s %8s %8s %16s %5s %s\n", + "pid", "uTime", "sTime", "exec(ns)", + "stat", "task_struct"); + __pr_info(__HLINE_LEFT __HLINE_RIGHT); + + for_each_process_thread(p, t) { + __dump_one_task_info(t, p == t); + } + + __pr_info(__HLINE_LEFT __HLINE_RIGHT); +} + +/* TODO: this is a modified version of 'show_stat' in 'fs/proc/stat.c' + * this function should be adapted for each kernel version + */ +#ifndef arch_irq_stat_cpu +#define arch_irq_stat_cpu(cpu) 0 +#endif +#ifndef arch_irq_stat +#define arch_irq_stat() 0 +#endif + +void dump_cpu_stat(void) +{ + int i, j; + u64 user, nice, system, idle, iowait, irq, softirq, steal; + u64 guest, guest_nice; + u64 sum = 0; + u64 sum_softirq = 0; + unsigned int per_softirq_sums[NR_SOFTIRQS] = {0}; + struct timespec64 boottime; + + user = nice = system = idle = iowait = + irq = softirq = steal = 0; + guest = guest_nice = 0; + getboottime64(&boottime); + + for_each_possible_cpu(i) { + user += kcpustat_cpu(i).cpustat[CPUTIME_USER]; + nice += kcpustat_cpu(i).cpustat[CPUTIME_NICE]; + system += kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + idle += kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; + iowait += kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; + irq += kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; + softirq += kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]; + steal += kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; + guest += kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; + guest_nice += kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; + sum += kstat_cpu_irqs_sum(i); + sum += arch_irq_stat_cpu(i); + + for (j = 0; j < NR_SOFTIRQS; j++) { + unsigned int softirq_stat = kstat_softirqs_cpu(j, i); + + per_softirq_sums[j] += softirq_stat; + sum_softirq += softirq_stat; + } + } + sum += arch_irq_stat(); + + __pr_info("\n"); + __pr_info(__HLINE_LEFT __HLINE_RIGHT); + __pr_info(" %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s\n", + "user", "nice", "system", "idle", "iowait", "irq", + "softirq", "steal", "guest", "guest_nice"); + __pr_info("cpu %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu\n", + cputime64_to_clock_t(user), + cputime64_to_clock_t(nice), + cputime64_to_clock_t(system), + cputime64_to_clock_t(idle), + cputime64_to_clock_t(iowait), + cputime64_to_clock_t(irq), + cputime64_to_clock_t(softirq), + cputime64_to_clock_t(steal), + cputime64_to_clock_t(guest), + cputime64_to_clock_t(guest_nice)); + + for_each_online_cpu(i) { + /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ + user = kcpustat_cpu(i).cpustat[CPUTIME_USER]; + nice = kcpustat_cpu(i).cpustat[CPUTIME_NICE]; + system = kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + idle = kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; + iowait = kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; + irq = kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; + softirq = kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]; + steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; + guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; + guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; + __pr_info("cpu%-2d %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu\n", + i, + cputime64_to_clock_t(user), + cputime64_to_clock_t(nice), + cputime64_to_clock_t(system), + cputime64_to_clock_t(idle), + cputime64_to_clock_t(iowait), + cputime64_to_clock_t(irq), + cputime64_to_clock_t(softirq), + cputime64_to_clock_t(steal), + cputime64_to_clock_t(guest), + cputime64_to_clock_t(guest_nice)); + } + + __pr_info(__HLINE_LEFT __HLINE_RIGHT); + __pr_info("intr %llu\n", (unsigned long long)sum); + + /* sum again ? it could be updated? */ + for_each_irq_nr(j) + if (kstat_irqs(j)) + __pr_info(" irq-%d : %u\n", j, kstat_irqs(j)); + + __pr_info(__HLINE_LEFT __HLINE_RIGHT); + __pr_info("\nctxt %llu\n" + "btime %llu\n" + "processes %lu\n" + "procs_running %lu\n" + "procs_blocked %lu\n", + nr_context_switches(), + (unsigned long long)boottime.tv_sec, + total_forks, + nr_running(), + nr_iowait()); + + __pr_info(__HLINE_LEFT __HLINE_RIGHT); + __pr_info("softirq %llu\n", (unsigned long long)sum_softirq); + + for (i = 0; i < NR_SOFTIRQS; i++) + __pr_info(" softirq-%d : %u\n", i, per_softirq_sums[i]); + __pr_info("\n"); + + __pr_info(__HLINE_LEFT __HLINE_RIGHT); +} diff --git a/drivers/debug/sec_debug_force_err.c b/drivers/debug/sec_debug_force_err.c new file mode 100644 index 000000000000..0cf99e78a8e4 --- /dev/null +++ b/drivers/debug/sec_debug_force_err.c @@ -0,0 +1,363 @@ +/* + * drivers/debug/sec_debug_force_err.c + * + * COPYRIGHT(C) 2017 Samsung Electronics Co., Ltd. All Right Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +/* timeout for dog bark/bite */ +#define DELAY_TIME 20000 + +static int force_error(const char *val, const struct kernel_param *kp); +module_param_call(force_error, force_error, NULL, NULL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +static void __simulate_apps_wdog_bark(void) +{ + pr_emerg("Simulating apps watch dog bark\n"); + preempt_disable(); + mdelay(DELAY_TIME); + preempt_enable(); + /* if we reach here, simulation failed */ + pr_emerg("Simulation of apps watch dog bark failed\n"); +} + +static void __simulate_apps_wdog_bite(void) +{ +#ifdef CONFIG_HOTPLUG_CPU + int cpu; + for_each_online_cpu(cpu) { + if (0 == cpu) + continue; + cpu_down(cpu); + } +#endif + pr_emerg("Simulating apps watch dog bite\n"); + local_irq_disable(); + mdelay(DELAY_TIME); + local_irq_enable(); + /* if we reach here, simulation had failed */ + pr_emerg("Simualtion of apps watch dog bite failed\n"); +} + +#ifdef CONFIG_SEC_DEBUG_SEC_WDOG_BITE +static void __simulate_secure_wdog_bite(void) +{ +#define SCM_SVC_SEC_WDOG_TRIG 0x8 + struct scm_desc desc = { + .args[0] = 0, + .arginfo = SCM_ARGS(1), + }; + + pr_emerg("simulating secure watch dog bite\n"); + if (!is_scm_armv8()) + scm_call_atomic2(SCM_SVC_BOOT, + SCM_SVC_SEC_WDOG_TRIG, 0, 0); + else + scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, + SCM_SVC_SEC_WDOG_TRIG), &desc); + /* if we hit, scm_call has failed */ + pr_emerg("simulation of secure watch dog bite failed\n"); +} +#endif + +#ifdef CONFIG_USER_RESET_DEBUG_TEST +extern void qpnp_pon_pmic_wd_trigger(void); +static void __simulate_pmic_wdog_bite(void) +{ + pr_emerg("simulating pmic watch dog bite\n"); + + qpnp_pon_pmic_wd_trigger(); + + while(1); +} +#endif + +#if defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8974) +/* + * Misc data structures needed for simulating bus timeout in + * camera + */ + +#define HANG_ADDRESS 0xfda10000 + +struct clk_pair { + const char *dev; + const char *clk; +}; + +static struct clk_pair bus_timeout_camera_clocks_on[] = { + /* + * gcc_mmss_noc_cfg_ahb_clk should be on but right + * now this clock is on by default and not accessable. + * Update this table if gcc_mmss_noc_cfg_ahb_clk is + * ever not enabled by default! + */ + { + .dev = "fda0c000.qcom,cci", + .clk = "camss_top_ahb_clk", + }, + { + .dev = "fda10000.qcom,vfe", + .clk = "iface_clk", + }, +}; + +static struct clk_pair bus_timeout_camera_clocks_off[] = { + { + .dev = "fda10000.qcom,vfe", + .clk = "camss_vfe_vfe_clk", + } +}; + +static void bus_timeout_clk_access(struct clk_pair bus_timeout_clocks_off[], + struct clk_pair bus_timeout_clocks_on[], + int off_size, int on_size) +{ + size_t i; + + /* + * Yes, none of this cleans up properly but the goal here + * is to trigger a hang which is going to kill the rest of + * the system anyway + */ + + for (i = 0; i < on_size; i++) { + struct clk *this_clock; + + this_clock = clk_get_sys(bus_timeout_clocks_on[i].dev, + bus_timeout_clocks_on[i].clk); + if (!IS_ERR(this_clock)) + if (clk_prepare_enable(this_clock)) + pr_warn("Device %s: Clock %s not enabled", + bus_timeout_clocks_on[i].clk, + bus_timeout_clocks_on[i].dev); + } + + for (i = 0; i < off_size; i++) { + struct clk *this_clock; + + this_clock = clk_get_sys(bus_timeout_clocks_off[i].dev, + bus_timeout_clocks_off[i].clk); + if (!IS_ERR(this_clock)) + clk_disable_unprepare(this_clock); + } +} + +static void simulate_bus_hang(void) +{ + /* This simulates bus timeout on camera */ + int ret; + uint32_t dummy_value; + uint32_t address = HANG_ADDRESS; + void *hang_address; + struct regulator *r; + + /* simulate */ + hang_address = ioremap(address, SZ_4K); + r = regulator_get(NULL, "gdsc_vfe"); + ret = IS_ERR(r); + if (!ret) + regulator_enable(r); + else + pr_emerg("Unable to get regulator reference\n"); + + bus_timeout_clk_access(bus_timeout_camera_clocks_off, + bus_timeout_camera_clocks_on, + ARRAY_SIZE(bus_timeout_camera_clocks_off), + ARRAY_SIZE(bus_timeout_camera_clocks_on)); + + dummy_value = readl_relaxed(hang_address); + mdelay(DELAY_TIME); + /* if we hit here, test had failed */ + pr_emerg("Bus timeout test failed...0x%x\n", dummy_value); + iounmap(hang_address); +} +#else /* defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8974) */ +static void simulate_bus_hang(void) +{ + void __iomem *p; + + pr_emerg("Generating Bus Hang!\n"); + p = ioremap_nocache(0xFC4B8000, 32); + *(unsigned int *)p = *(unsigned int *)p; + mb(); /* memory barriar to generate bus hang */ + pr_info("*p = %x\n", *(unsigned int *)p); + pr_emerg("Clk may be enabled.Try again if it reaches here!\n"); +} +#endif /* defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8974) */ + +static void __simulate_dabort(void) +{ + char *buf = NULL; + *buf = 0x0; +} + + +static void __simulate_pabort(void) +{ + ((void (*)(void))NULL)(); +} + +static void __simulate_undef(void) +{ + BUG(); +} + +static void __simulate_dblfree(void) +{ + unsigned int *ptr = kmalloc(sizeof(unsigned int), GFP_KERNEL); + kfree(ptr); + msleep(1000); + kfree(ptr); +} + +static void __simulate_danglingref(void) +{ + unsigned int *ptr = kmalloc(sizeof(unsigned int), GFP_KERNEL); + kfree(ptr); + *ptr = 0x1234; +} + +static void __simulate_lowmem(void) +{ + size_t i; + + for (i = 0; kmalloc(128 * 1024, GFP_KERNEL); i++) + ; + pr_emerg("Allocated %zu KB!\n", i * 128); +} + +static void __simulate_memcorrupt(void) +{ + unsigned int *ptr = kmalloc(sizeof(unsigned int), GFP_KERNEL); + *ptr++ = 4; + *ptr = 2; + panic("MEMORY CORRUPTION"); +} + +#ifdef CONFIG_FREE_PAGES_RDONLY +static void __check_page_read(void) +{ + struct page *page = alloc_pages(GFP_ATOMIC, 0); + unsigned int *ptr = (unsigned int *)page_address(page); + pr_emerg("Test with RD page config."); + __free_pages(page, 0); + *ptr = 0x12345678; +} +#endif + +static int force_error(const char *val, const struct kernel_param *kp) +{ + size_t i; + struct __magic { + const char *val; + const char *msg; + void (*func)(void); + } magic[] = { + { "apppdogbark", + "Generating an apps wdog bark!", + &__simulate_apps_wdog_bark }, + { "appdogbite", + "Generating an apps wdog bite!", + &__simulate_apps_wdog_bite }, + { "dabort", + "Generating a data abort exception!", + &__simulate_dabort }, + { "pabort", + "Generating a data abort exception!", + &__simulate_pabort }, + { "undef", + "Generating a undefined instruction exception!", + &__simulate_undef }, + { "bushang", + "Generating a Bus Hang!", + &simulate_bus_hang }, + { "dblfree", + NULL, + &__simulate_dblfree }, + { "danglingref", + NULL, + &__simulate_danglingref }, + { "lowmem", + "Allocating memory until failure!", + &__simulate_lowmem }, + { "memcorrupt", + NULL, + &__simulate_memcorrupt }, +#ifdef CONFIG_SEC_DEBUG_SEC_WDOG_BITE + { "secdogbite", + NULL, + &__simulate_secure_wdog_bite }, +#endif +#ifdef CONFIG_USER_RESET_DEBUG_TEST + { "KP", + "Generating a data abort exception!", + &__simulate_dabort }, + { "DP", + NULL, + &force_watchdog_bark }, + { "WP", + NULL, + &__simulate_secure_wdog_bite }, + { "PMWD", + NULL, + &__simulate_pmic_wdog_bite }, +#endif +#ifdef CONFIG_FREE_PAGES_RDONLY + { "pageRDcheck", + NULL, + &__check_page_read }, +#endif + }; + + pr_emerg("!!!WARN forced error : %s\n", val); + + for (i = 0; i < ARRAY_SIZE(magic); i++) { + size_t len = strlen(magic[i].val); + + if (strncmp(val, magic[i].val, len)) + continue; + + if (NULL != magic[i].msg) + pr_emerg("%s\n", magic[i].msg); + + magic[i].func(); + return 0; + } + + pr_emerg("No such error defined for now!\n"); + return 0; +} diff --git a/drivers/debug/sec_debug_internal.h b/drivers/debug/sec_debug_internal.h new file mode 100644 index 000000000000..5b67a9f1ba14 --- /dev/null +++ b/drivers/debug/sec_debug_internal.h @@ -0,0 +1,52 @@ +/* + * drivers/debug/sec_debug_internal.h + * + * COPYRIGHT(C) 2017-2018 Samsung Electronics Co., Ltd. All Right 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_DEBUG_INTERNAL_H__ +#define __SEC_DEBUG_INTERNAL_H__ + +/* [[BEGIN>> sec_debug.c */ +extern void dump_all_task_info(void); +extern void dump_cpu_stat(void); +/* <> sec_debug_sched_log.c */ +extern struct sec_debug_log *secdbg_log; +extern phys_addr_t secdbg_paddr; +extern size_t secdbg_size; +/* < +extern struct uts_namespace init_uts_ns; + +/* drivers/base/core.c */ +extern struct kset *devices_kset; + +/* kernel/printk/printk.c */ +extern unsigned int get_sec_log_idx(void); + +/* fake pr_xxx macros to prevent checkpatch fails */ +#define __printx printk +#define __pr_info(fmt, ...) \ + __printx(KERN_INFO fmt, ##__VA_ARGS__) +#define __pr_err(fmt, ...) \ + __printx(KERN_ERR fmt, ##__VA_ARGS__) + +#endif /* __SEC_DEBUG_INTERNAL_H__ */ diff --git a/drivers/debug/sec_debug_partition.c b/drivers/debug/sec_debug_partition.c new file mode 100644 index 000000000000..e4354a203670 --- /dev/null +++ b/drivers/debug/sec_debug_partition.c @@ -0,0 +1,534 @@ +/* + * drivers/debug/sec_debug_partition.c + * + * COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PRINT_MSG_CYCLE 20 + +/* single global instance */ +struct debug_partition_data_s sched_debug_data; + +static int in_panic; +static int driver_initialized; +static int ap_health_initialized; +static struct delayed_work dbg_partition_notify_work; +static struct workqueue_struct *dbg_part_wq; +static struct delayed_work ap_health_work; +static ap_health_t ap_health_data; + +static DEFINE_MUTEX(ap_health_work_lock); +static DEFINE_MUTEX(debug_partition_mutex); +static BLOCKING_NOTIFIER_HEAD(dbg_partition_notifier_list); + +static void debug_partition_operation(struct work_struct *work) +{ + int ret; + struct file *filp; + mm_segment_t fs; + struct debug_partition_data_s *sched_data = + container_of(work, struct debug_partition_data_s, + debug_partition_work); + int flag = (sched_data->direction == PARTITION_WR) ? + (O_RDWR | O_SYNC) : O_RDONLY; + static unsigned int err_cnt; + + if (!sched_data->value) { + pr_err("%p %x %d %d - value is NULL!!\n", + sched_data->value, sched_data->offset, + sched_data->size, sched_data->direction); + sched_data->error = -ENODATA; + goto sched_data_err; + } + + fs = get_fs(); + set_fs(get_ds()); + + sched_data->error = 0; + + filp = filp_open(DEBUG_PARTITION_NAME, flag, 0); + if (IS_ERR(filp)) { + if (!(++err_cnt % PRINT_MSG_CYCLE)) + pr_err("filp_open failed: %ld[%u]\n", + PTR_ERR(filp), err_cnt); + sched_data->error = PTR_ERR(filp); + goto filp_err; + } + + ret = vfs_llseek(filp, sched_data->offset, SEEK_SET); + if (ret < 0) { + pr_err("FAIL LLSEEK\n"); + sched_data->error = ret; + goto llseek_err; + } + + if (sched_data->direction == PARTITION_RD) + vfs_read(filp, (char __user *)sched_data->value, + sched_data->size, &filp->f_pos); + else if (sched_data->direction == PARTITION_WR) + vfs_write(filp, (char __user *)sched_data->value, + sched_data->size, &filp->f_pos); + +llseek_err: + filp_close(filp, NULL); +filp_err: + set_fs(fs); +sched_data_err: + complete(&sched_data->work); +} + +static void ap_health_work_write_fn(struct work_struct *work) +{ + int ret; + struct file *filp; + mm_segment_t fs; + unsigned long delay = 5 * HZ; + static unsigned int err_cnt; + + pr_info("start.\n"); + + if (!mutex_trylock(&ap_health_work_lock)) { + pr_err("already locked.\n"); + delay = 2 * HZ; + goto occupied_retry; + } + + fs = get_fs(); + set_fs(get_ds()); + + filp = filp_open(DEBUG_PARTITION_NAME, (O_RDWR | O_SYNC), 0); + if (IS_ERR(filp)) { + if (!(++err_cnt % PRINT_MSG_CYCLE)) + pr_err("filp_open failed: %ld[%u]\n", + PTR_ERR(filp), err_cnt); + goto openfail_retry; + } + + ret = vfs_llseek(filp, SEC_DEBUG_AP_HEALTH_OFFSET, SEEK_SET); + if (ret < 0) { + pr_err("FAIL LLSEEK\n"); + ret = false; + goto seekfail_retry; + } + + vfs_write(filp, (char __user *)&ap_health_data, + sizeof(ap_health_t), &filp->f_pos); + + if (--ap_health_data.header.need_write) + goto remained; + + filp_close(filp, NULL); + set_fs(fs); + + mutex_unlock(&ap_health_work_lock); + pr_info("end.\n"); + return; + +remained: +seekfail_retry: + filp_close(filp, NULL); +openfail_retry: + set_fs(fs); + mutex_unlock(&ap_health_work_lock); +occupied_retry: + queue_delayed_work(dbg_part_wq, &ap_health_work, delay); + pr_info("end, will retry, wr(%u).\n", + ap_health_data.header.need_write); +} + +static bool init_lcd_debug_data(void) +{ + int ret = true, retry = 0; + struct lcd_debug_t lcd_debug; + + pr_info("%s start\n", __func__); + + memset((void *)&lcd_debug, 0, sizeof(struct lcd_debug_t)); + + pr_info("%s lcd_debug size[%ld]\n", __func__, sizeof(struct lcd_debug_t)); + + do { + if (retry++) { + pr_err("%s : will retry...\n", __func__); + msleep(1000); + } + + mutex_lock(&debug_partition_mutex); + + sched_debug_data.value = &lcd_debug; + sched_debug_data.offset = SEC_DEBUG_LCD_DEBUG_OFFSET; + sched_debug_data.size = sizeof(struct lcd_debug_t); + sched_debug_data.direction = PARTITION_WR; + + schedule_work(&sched_debug_data.debug_partition_work); + wait_for_completion(&sched_debug_data.work); + + mutex_unlock(&debug_partition_mutex); + } while (sched_debug_data.error); + + pr_info("%s end\n",__func__); + + return ret; +} + +static void init_ap_health_data(void) +{ + pr_info("start\n"); + + memset((void *)&ap_health_data, 0, sizeof(ap_health_t)); + + ap_health_data.header.magic = AP_HEALTH_MAGIC; + ap_health_data.header.version = AP_HEALTH_VER; + ap_health_data.header.size = sizeof(ap_health_t); + ap_health_data.spare_magic1 = AP_HEALTH_MAGIC; + ap_health_data.spare_magic2 = AP_HEALTH_MAGIC; + ap_health_data.spare_magic3 = AP_HEALTH_MAGIC; + + while (1) { + mutex_lock(&debug_partition_mutex); + + sched_debug_data.value = &ap_health_data; + sched_debug_data.offset = SEC_DEBUG_AP_HEALTH_OFFSET; + sched_debug_data.size = sizeof(ap_health_t); + sched_debug_data.direction = PARTITION_WR; + + schedule_work(&sched_debug_data.debug_partition_work); + wait_for_completion(&sched_debug_data.work); + + mutex_unlock(&debug_partition_mutex); + if (!sched_debug_data.error) + break; + + msleep(1000); + } + + pr_info("end\n"); +} + +static void init_debug_partition(void) +{ + struct debug_reset_header init_reset_header; + + pr_info("start\n"); + + /*++ add here need init data ++*/ + init_ap_health_data(); + /*-- add here need init data --*/ + + while (1) { + mutex_lock(&debug_partition_mutex); + + memset(&init_reset_header, 0, + sizeof(struct debug_reset_header)); + init_reset_header.magic = DEBUG_PARTITION_MAGIC; + + sched_debug_data.value = &init_reset_header; + sched_debug_data.offset = SEC_DEBUG_RESET_HEADER_OFFSET; + sched_debug_data.size = sizeof(struct debug_reset_header); + sched_debug_data.direction = PARTITION_WR; + + schedule_work(&sched_debug_data.debug_partition_work); + wait_for_completion(&sched_debug_data.work); + + mutex_unlock(&debug_partition_mutex); + + if (!sched_debug_data.error) + break; + + msleep(1000); + } + + pr_info("end\n"); +} + +static void check_magic_data(void) +{ + static int checked_magic; + struct debug_reset_header partition_header = {0,}; + + if (checked_magic) + return; + + pr_info("start\n"); + + while (1) { + mutex_lock(&debug_partition_mutex); + + sched_debug_data.value = &partition_header; + sched_debug_data.offset = SEC_DEBUG_RESET_HEADER_OFFSET; + sched_debug_data.size = sizeof(struct debug_reset_header); + sched_debug_data.direction = PARTITION_RD; + + schedule_work(&sched_debug_data.debug_partition_work); + wait_for_completion(&sched_debug_data.work); + + mutex_unlock(&debug_partition_mutex); + + if (!sched_debug_data.error) + break; + + msleep(1000); + } + + if (partition_header.magic != DEBUG_PARTITION_MAGIC) + init_debug_partition(); + + checked_magic = 1; + + pr_info("end\n"); +} + +#define READ_DEBUG_PARTITION(_value, _offset, _size) \ +{ \ + mutex_lock(&debug_partition_mutex); \ + sched_debug_data.value = _value; \ + sched_debug_data.offset = _offset; \ + sched_debug_data.size = _size; \ + sched_debug_data.direction = PARTITION_RD; \ + schedule_work(&sched_debug_data.debug_partition_work); \ + wait_for_completion(&sched_debug_data.work); \ + mutex_unlock(&debug_partition_mutex); \ +} + +bool read_debug_partition(enum debug_partition_index index, void *value) +{ + check_magic_data(); + + switch (index) { + case debug_index_reset_ex_info: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_EXTRA_INFO_OFFSET, + SEC_DEBUG_EX_INFO_SIZE); + break; + case debug_index_reset_klog_info: + case debug_index_reset_summary_info: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_RESET_HEADER_OFFSET, + sizeof(struct debug_reset_header)); + break; + case debug_index_reset_summary: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_RESET_SUMMARY_OFFSET, + SEC_DEBUG_RESET_SUMMARY_SIZE); + break; + case debug_index_reset_klog: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_RESET_KLOG_OFFSET, + SEC_DEBUG_RESET_KLOG_SIZE); + break; + case debug_index_reset_tzlog: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_RESET_TZLOG_OFFSET, + SEC_DEBUG_RESET_TZLOG_SIZE); + break; + case debug_index_ap_health: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_AP_HEALTH_OFFSET, + SEC_DEBUG_AP_HEALTH_SIZE); + break; + case debug_index_reset_extrc_info: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_RESET_EXTRC_OFFSET, + SEC_DEBUG_RESET_EXTRC_SIZE); + break; + case debug_index_lcd_debug_info: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_LCD_DEBUG_OFFSET, + sizeof(struct lcd_debug_t)); + break; + case debug_index_modem_info: + READ_DEBUG_PARTITION(value, + SEC_DEBUG_RESET_MODEM_OFFSET, + sizeof(struct sec_debug_summary_data_modem)); + break; + default: + return false; + } + + return true; +} +EXPORT_SYMBOL(read_debug_partition); + +bool write_debug_partition(enum debug_partition_index index, void *value) +{ + check_magic_data(); + + switch (index) { + case debug_index_reset_klog_info: + case debug_index_reset_summary_info: + mutex_lock(&debug_partition_mutex); + sched_debug_data.value = + (struct debug_reset_header *)value; + sched_debug_data.offset = SEC_DEBUG_RESET_HEADER_OFFSET; + sched_debug_data.size = + sizeof(struct debug_reset_header); + sched_debug_data.direction = PARTITION_WR; + schedule_work(&sched_debug_data.debug_partition_work); + wait_for_completion(&sched_debug_data.work); + mutex_unlock(&debug_partition_mutex); + break; + case debug_index_reset_ex_info: + case debug_index_reset_summary: + // do nothing. + break; + case debug_index_lcd_debug_info: + mutex_lock(&debug_partition_mutex); + sched_debug_data.value = (struct lcd_debug_t *)value; + sched_debug_data.offset = SEC_DEBUG_LCD_DEBUG_OFFSET; + sched_debug_data.size = sizeof(struct lcd_debug_t); + sched_debug_data.direction = PARTITION_WR; + schedule_work(&sched_debug_data.debug_partition_work); + wait_for_completion(&sched_debug_data.work); + mutex_unlock(&debug_partition_mutex); + break; + default: + return false; + } + + return true; +} +EXPORT_SYMBOL(write_debug_partition); + +ap_health_t *ap_health_data_read(void) +{ + if (!driver_initialized) + return NULL; + + if (ap_health_initialized) + goto out; + + if (in_interrupt()) { + pr_info("skip read opt.\n"); + return NULL; + } + + read_debug_partition(debug_index_ap_health, (void *)&ap_health_data); + + if (ap_health_data.header.magic != AP_HEALTH_MAGIC || + ap_health_data.header.version != AP_HEALTH_VER || + ap_health_data.header.size != sizeof(ap_health_t) || + ap_health_data.spare_magic1 != AP_HEALTH_MAGIC || + ap_health_data.spare_magic2 != AP_HEALTH_MAGIC || + ap_health_data.spare_magic3 != AP_HEALTH_MAGIC || + is_boot_recovery()) { + init_ap_health_data(); + init_lcd_debug_data(); + } + + ap_health_initialized = 1; +out: + return &ap_health_data; +} +EXPORT_SYMBOL(ap_health_data_read); + +int ap_health_data_write(ap_health_t *data) +{ + if (!driver_initialized || !data || !ap_health_initialized) + return -ENODATA; + + data->header.need_write++; + + if (!in_panic) + queue_delayed_work(dbg_part_wq, &ap_health_work, 0); + + return 0; +} +EXPORT_SYMBOL(ap_health_data_write); + +int dbg_partition_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &dbg_partition_notifier_list, nb); +} +EXPORT_SYMBOL(dbg_partition_notifier_register); + +static void debug_partition_do_notify(struct work_struct *work) +{ + blocking_notifier_call_chain(&dbg_partition_notifier_list, + DBG_PART_DRV_INIT_DONE, NULL); +} + +static int dbg_partition_panic_prepare(struct notifier_block *nb, + unsigned long event, void *data) +{ + in_panic = 1; + return NOTIFY_DONE; +} + +static struct notifier_block dbg_partition_panic_notifier_block = { + .notifier_call = dbg_partition_panic_prepare, +}; + +static int __init sec_debug_partition_init(void) +{ + pr_info("start\n"); + + sched_debug_data.offset = 0; + sched_debug_data.direction = 0; + sched_debug_data.size = 0; + sched_debug_data.value = NULL; + + init_completion(&sched_debug_data.work); + INIT_WORK(&sched_debug_data.debug_partition_work, + debug_partition_operation); + INIT_DELAYED_WORK(&dbg_partition_notify_work, + debug_partition_do_notify); + INIT_DELAYED_WORK(&ap_health_work, ap_health_work_write_fn); + + dbg_part_wq = create_singlethread_workqueue("glink_lbsrv"); + if (!dbg_part_wq) { + pr_err("fail to create dbg_part_wq!\n"); + return -EFAULT; + } + atomic_notifier_chain_register(&panic_notifier_list, + &dbg_partition_panic_notifier_block); + + driver_initialized = DRV_INITIALIZED; + schedule_delayed_work(&dbg_partition_notify_work, 2 * HZ); + pr_info("end\n"); + + return 0; +} + +static void __exit sec_debug_partition_exit(void) +{ + driver_initialized = DRV_UNINITIALIZED; + cancel_work_sync(&sched_debug_data.debug_partition_work); + cancel_delayed_work_sync(&dbg_partition_notify_work); + pr_info("exit\n"); +} + +module_init(sec_debug_partition_init); +module_exit(sec_debug_partition_exit); diff --git a/drivers/debug/sec_debug_sched_log.c b/drivers/debug/sec_debug_sched_log.c new file mode 100644 index 000000000000..c9b5a02ba216 --- /dev/null +++ b/drivers/debug/sec_debug_sched_log.c @@ -0,0 +1,461 @@ +/* + * drivers/debug/sec_debug_sched_log.c + * + * COPYRIGHT(C) 2017-2018 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include +#include +#include +#include + +#include + +#include "sec_debug_internal.h" + +/* TODO: set 'NO_ATOMIC_IDX' only for internal testing purpose */ +#ifdef NO_ATOMIC_IDX +#define sec_debug_idx_inc_return(__idx) (++(*(__idx))) +#define sec_debug_idx_set(__idx, __v) { (*(__idx)) = __v; } +#else +#define sec_debug_idx_inc_return(__idx) atomic_inc_return((__idx)) +#define sec_debug_idx_set(__idx, __v) atomic_sec((__idx), (__v)) +#endif + +struct sec_debug_log *secdbg_log; + +phys_addr_t secdbg_paddr; +size_t secdbg_size; + +static int __init sec_dbg_setup(char *str) +{ + size_t size = (size_t)memparse(str, &str); + + pr_info("str=%s\n", str); + + if (size /*&& (size == roundup_pow_of_two(size))*/ && (*str == '@')) { + secdbg_paddr = (phys_addr_t)memparse(++str, NULL); + secdbg_size = size; + } + + pr_info("secdbg_paddr = 0x%llx\n", (unsigned long long)secdbg_paddr); + pr_info("secdbg_size = 0x%zx\n", secdbg_size); + + return 0; +} +__setup("sec_dbg=", sec_dbg_setup); + +/* save last_pet and last_ns with these nice functions */ +void sec_debug_save_last_pet(unsigned long long last_pet) +{ + if (likely(secdbg_log)) + secdbg_log->last_pet = last_pet; +} +EXPORT_SYMBOL(sec_debug_save_last_pet); + +void sec_debug_save_last_ns(unsigned long long last_ns) +{ + if (likely(secdbg_log)) + atomic64_set(&(secdbg_log->last_ns), last_ns); +} +EXPORT_SYMBOL(sec_debug_save_last_ns); + +static inline long get_switch_state(bool preempt, struct task_struct *p) +{ + return preempt ? TASK_RUNNING | TASK_STATE_MAX : p->state; +} + +static __always_inline void __sec_debug_task_sched_log(int cpu, bool preempt, + struct task_struct *task, struct task_struct *prev, + char *msg) +{ + struct sched_log *sched_log; + int i; + + if (unlikely(!secdbg_log)) + return; + + if (unlikely(!task && !msg)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_sched[cpu])) + & (SCHED_LOG_MAX - 1); + sched_log = &secdbg_log->sched[cpu][i]; + + sched_log->time = cpu_clock(cpu); + if (task) { + strlcpy(sched_log->comm, task->comm, sizeof(sched_log->comm)); + sched_log->pid = task->pid; + sched_log->pTask = task; + sched_log->prio = task->prio; + strlcpy(sched_log->prev_comm, prev->comm, + sizeof(sched_log->prev_comm)); + + sched_log->prev_pid = prev->pid; + sched_log->prev_state = get_switch_state(preempt, prev); + sched_log->prev_prio = prev->prio; + } else { + strlcpy(sched_log->comm, msg, sizeof(sched_log->comm)); + sched_log->pid = current->pid; + sched_log->pTask = NULL; + } +} + +void sec_debug_irq_enterexit_log(unsigned int irq, u64 start_time) +{ + struct irq_exit_log *irq_exit_log; + int cpu = smp_processor_id(); + int i; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_irq_exit[cpu])) + & (SCHED_LOG_MAX - 1); + irq_exit_log = &secdbg_log->irq_exit[cpu][i]; + + irq_exit_log->time = start_time; + irq_exit_log->end_time = cpu_clock(cpu); + irq_exit_log->irq = irq; + irq_exit_log->elapsed_time = irq_exit_log->end_time - start_time; + irq_exit_log->pid = current->pid; +} + +void sec_debug_task_sched_log_short_msg(char *msg) +{ + __sec_debug_task_sched_log(raw_smp_processor_id(), + false, NULL, NULL, msg); +} + +void sec_debug_task_sched_log(int cpu, bool preempt, + struct task_struct *task, struct task_struct *prev) +{ + __sec_debug_task_sched_log(cpu, false, task, prev, NULL); +} + +void sec_debug_timer_log(unsigned int type, int int_lock, void *fn) +{ + struct timer_log *timer_log; + int cpu = smp_processor_id(); + int i; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_timer[cpu])) + & (SCHED_LOG_MAX - 1); + timer_log = &secdbg_log->timer_log[cpu][i]; + + timer_log->time = cpu_clock(cpu); + timer_log->type = type; + timer_log->int_lock = int_lock; + timer_log->fn = (void *)fn; + timer_log->pid = current->pid; +} + +void sec_debug_secure_log(u32 svc_id, u32 cmd_id) +{ + struct secure_log *secure_log; + static DEFINE_SPINLOCK(secdbg_securelock); + unsigned long flags; + int cpu; + int i; + + if (unlikely(!secdbg_log)) + return; + + spin_lock_irqsave(&secdbg_securelock, flags); + + cpu = smp_processor_id(); + i = sec_debug_idx_inc_return(&(secdbg_log->idx_secure[cpu])) + & (TZ_LOG_MAX - 1); + secure_log = &secdbg_log->secure[cpu][i]; + + secure_log->time = cpu_clock(cpu); + secure_log->svc_id = svc_id; + secure_log->cmd_id = cmd_id; + secure_log->pid = current->pid; + + spin_unlock_irqrestore(&secdbg_securelock, flags); +} + +void sec_debug_irq_sched_log(unsigned int irq, void *fn, + char *name, unsigned int en) +{ + struct irq_log *irq_log; + int cpu = smp_processor_id(); + int i; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_irq[cpu])) + & (SCHED_LOG_MAX - 1); + irq_log = &secdbg_log->irq[cpu][i]; + + irq_log->time = cpu_clock(cpu); + irq_log->irq = irq; + irq_log->fn = (void *)fn; + irq_log->name = name; + irq_log->en = irqs_disabled(); + irq_log->preempt_count = preempt_count(); + irq_log->context = &cpu; + irq_log->pid = current->pid; + irq_log->entry_exit = en; +} + +#ifdef CONFIG_SEC_DEBUG_MSG_LOG +int sec_debug_msg_log(void *caller, const char *fmt, ...) +{ + struct secmsg_log *secmsg_log; + int cpu = smp_processor_id(); + int r; + int i; + va_list args; + + if (unlikely(!secdbg_log)) + return 0; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_secmsg[cpu])) + & (MSG_LOG_MAX - 1); + secmsg_log = &secdbg_log->secmsg[cpu][i]; + + secmsg_log->time = cpu_clock(cpu); + va_start(args, fmt); + r = vsnprintf(secmsg_log->msg, sizeof(secmsg_log->msg), fmt, args); + va_end(args); + + secmsg_log->caller0 = __builtin_return_address(0); + secmsg_log->caller1 = caller; + secmsg_log->task = current->comm; + + return r; +} +#endif /* CONFIG_SEC_DEBUG_MSG_LOG */ + +#ifdef CONFIG_SEC_DEBUG_AVC_LOG +int sec_debug_avc_log(const char *fmt, ...) +{ + struct secavc_log *secavc_log; + int cpu = smp_processor_id(); + int r; + int i; + va_list args; + + if (unlikely(!secdbg_log)) + return 0; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_secavc[cpu])) + & (AVC_LOG_MAX - 1); + secavc_log = &secdbg_log->secavc[cpu][i]; + + va_start(args, fmt); + r = vsnprintf(secavc_log->msg, sizeof(secavc_log->msg), fmt, args); + va_end(args); + + return r; +} +#endif /* CONFIG_SEC_DEBUG_AVC_LOG */ + +#ifdef CONFIG_SEC_DEBUG_DCVS_LOG +void sec_debug_dcvs_log(int cpu_no, unsigned int prev_freq, + unsigned int new_freq) +{ + struct dcvs_debug *dcvs_debug; + int i; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->dcvs_log_idx[cpu_no])) + & (DCVS_LOG_MAX - 1); + dcvs_debug = &secdbg_log->dcvs_log[cpu_no][i]; + + dcvs_debug->cpu_no = cpu_no; + dcvs_debug->prev_freq = prev_freq; + dcvs_debug->new_freq = new_freq; + dcvs_debug->time = cpu_clock(cpu_no); +} +#endif + +#ifdef CONFIG_SEC_DEBUG_FUELGAUGE_LOG +void sec_debug_fuelgauge_log(unsigned int voltage, unsigned short soc, + unsigned short charging_status) +{ + struct fuelgauge_debug *fuelgauge_debug; + int cpu = smp_processor_id(); + int i; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->fg_log_idx)) + & (FG_LOG_MAX - 1); + fuelgauge_debug = &secdbg_log->fg_log[i]; + + fuelgauge_debug->time = cpu_clock(cpu); + fuelgauge_debug->voltage = voltage; + fuelgauge_debug->soc = soc; + fuelgauge_debug->charging_status = charging_status; +} +#endif + +#ifdef CONFIG_SEC_DEBUG_POWER_LOG +void sec_debug_cpu_lpm_log(int cpu, unsigned int index, + bool success, int entry_exit) +{ + struct power_log *power_log; + int i; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_power[cpu])) + & (POWER_LOG_MAX - 1); + power_log = &secdbg_log->pwr_log[cpu][i]; + + power_log->time = cpu_clock(cpu); + power_log->pid = current->pid; + power_log->type = CPU_POWER_TYPE; + + power_log->cpu.index = index; + power_log->cpu.success = success; + power_log->cpu.entry_exit = entry_exit; +} + +void sec_debug_cluster_lpm_log(const char *name, int index, + unsigned long sync_cpus, unsigned long child_cpus, + bool from_idle, int entry_exit) +{ + struct power_log *power_log; + int i; + int cpu = smp_processor_id(); + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_power[cpu])) + & (POWER_LOG_MAX - 1); + power_log = &secdbg_log->pwr_log[cpu][i]; + + power_log->time = cpu_clock(cpu); + power_log->pid = current->pid; + power_log->type = CLUSTER_POWER_TYPE; + + power_log->cluster.name = (char *) name; + power_log->cluster.index = index; + power_log->cluster.sync_cpus = sync_cpus; + power_log->cluster.child_cpus = child_cpus; + power_log->cluster.from_idle = from_idle; + power_log->cluster.entry_exit = entry_exit; +} + +void sec_debug_clock_log(const char *name, unsigned int state, + unsigned int cpu_id, int complete) +{ + struct power_log *power_log; + int i; + int cpu = cpu_id; + + if (unlikely(!secdbg_log)) + return; + + i = sec_debug_idx_inc_return(&(secdbg_log->idx_power[cpu_id])) + & (POWER_LOG_MAX - 1); + power_log = &secdbg_log->pwr_log[cpu][i]; + + power_log->time = cpu_clock(cpu_id); + power_log->pid = current->pid; + power_log->type = CLOCK_RATE_TYPE; + + power_log->clk_rate.name = (char *)name; + power_log->clk_rate.state = state; + power_log->clk_rate.cpu_id = cpu_id; + power_log->clk_rate.complete = complete; +} +#endif /* CONFIG_SEC_DEBUG_POWER_LOG */ + +static int __init sec_debug_sched_log_init(void) +{ + size_t i; + struct sec_debug_log *vaddr; + size_t size; + + if (secdbg_paddr == 0 || secdbg_size == 0) { + pr_info("sec debug buffer not provided. Using kmalloc..\n"); + size = sizeof(struct sec_debug_log); + vaddr = kzalloc(size, GFP_KERNEL); + } else { + size = secdbg_size; + vaddr = ioremap_wc(secdbg_paddr, secdbg_size); + } + + pr_info("vaddr=0x%p paddr=0x%llx size=0x%zx sizeof(struct sec_debug_log)=0x%zx\n", + vaddr, (uint64_t)secdbg_paddr, + secdbg_size, sizeof(struct sec_debug_log)); + + if ((!vaddr) || (sizeof(struct sec_debug_log) > size)) { + pr_err("ERROR! init failed!\n"); + return -EFAULT; + } + + memset_io(vaddr->sched, 0x0, sizeof(vaddr->sched)); + memset_io(vaddr->irq, 0x0, sizeof(vaddr->irq)); + memset_io(vaddr->irq_exit, 0x0, sizeof(vaddr->irq_exit)); + memset_io(vaddr->timer_log, 0x0, sizeof(vaddr->timer_log)); + memset_io(vaddr->secure, 0x0, sizeof(vaddr->secure)); + +#ifdef CONFIG_SEC_DEBUG_MSG_LOG + memset_io(vaddr->secmsg, 0x0, sizeof(vaddr->secmsg)); +#endif + +#ifdef CONFIG_SEC_DEBUG_AVC_LOG + memset_io(vaddr->secavc, 0x0, sizeof(vaddr->secavc)); +#endif + + for (i = 0; i < num_possible_cpus(); i++) { + sec_debug_idx_set(&(vaddr->idx_sched[i]), -1); + sec_debug_idx_set(&(vaddr->idx_irq[i]), -1); + sec_debug_idx_set(&(vaddr->idx_secure[i]), -1); + sec_debug_idx_set(&(vaddr->idx_irq_exit[i]), -1); + sec_debug_idx_set(&(vaddr->idx_timer[i]), -1); + +#ifdef CONFIG_SEC_DEBUG_MSG_LOG + sec_debug_idx_set(&(vaddr->idx_secmsg[i]), -1); +#endif + +#ifdef CONFIG_SEC_DEBUG_AVC_LOG + sec_debug_idx_set(&(vaddr->idx_secavc[i]), -1); +#endif + +#ifdef CONFIG_SEC_DEBUG_DCVS_LOG + sec_debug_idx_set(&(vaddr->dcvs_log_idx[i]), -1); +#endif + +#ifdef CONFIG_SEC_DEBUG_POWER_LOG + sec_debug_idx_set(&(vaddr->idx_power[i]), -1); +#endif + } + +#ifdef CONFIG_SEC_DEBUG_FUELGAUGE_LOG + sec_debug_idx_set(&(vaddr->fg_log_idx), -1); +#endif + + secdbg_log = vaddr; + + pr_info("init done\n"); + + return 0; +} +arch_initcall_sync(sec_debug_sched_log_init); diff --git a/drivers/debug/sec_debug_summary.c b/drivers/debug/sec_debug_summary.c new file mode 100644 index 000000000000..0b1050d29e72 --- /dev/null +++ b/drivers/debug/sec_debug_summary.c @@ -0,0 +1,629 @@ +/* + * drivers/debug/sec_debug_summary.c + * + * driver supporting debug functions for Samsung device + * + * COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sec_debug_internal.h" + +struct sec_debug_summary *secdbg_summary; +struct sec_debug_summary_data_apss *secdbg_apss; +static char build_root[] = __FILE__; + +static uint32_t tzapps_start_addr; +static uint32_t tzapps_size; + +char *sec_debug_arch_desc; + +static unsigned long cpu_buf_vaddr; +static unsigned long cpu_buf_paddr; +static unsigned long cpu_data_vaddr; +static unsigned long cpu_data_paddr; +static unsigned long max_cpu_ctx_size; + +#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML +unsigned int cpu_frequency[CONFIG_NR_CPUS]; +unsigned int cpu_volt[CONFIG_NR_CPUS]; +char cpu_state[CONFIG_NR_CPUS][VAR_NAME_MAX]; +EXPORT_SYMBOL(cpu_frequency); +EXPORT_SYMBOL(cpu_volt); +EXPORT_SYMBOL(cpu_state); +#endif + +#ifdef CONFIG_ARM64 +#define ARM_PT_REG_PC pc +#define ARM_PT_REG_LR regs[30] +#else +#define ARM_PT_REG_PC ARM_pc +#define ARM_PT_REG_LR ARM_lr +#endif + +int sec_debug_save_die_info(const char *str, struct pt_regs *regs) +{ +#ifdef CONFIG_USER_RESET_DEBUG + _kern_ex_info_t *p_ex_info; +#endif + if (!secdbg_apss) + return -ENOMEM; + snprintf(secdbg_apss->excp.pc_sym, sizeof(secdbg_apss->excp.pc_sym), + "%pS", (void *)regs->ARM_PT_REG_PC); + snprintf(secdbg_apss->excp.lr_sym, sizeof(secdbg_apss->excp.lr_sym), + "%pS", (void *)regs->ARM_PT_REG_LR); + +#ifdef CONFIG_USER_RESET_DEBUG + sec_debug_store_extc_idx(false); + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + if (p_ex_info->cpu == -1) { + int slen; + char *msg; + + p_ex_info->cpu = smp_processor_id(); + snprintf(p_ex_info->task_name, + sizeof(p_ex_info->task_name), "%s", current->comm); + p_ex_info->ktime = local_clock(); + snprintf(p_ex_info->pc, + sizeof(p_ex_info->pc), "%pS", (void *)regs->ARM_PT_REG_PC); + snprintf(p_ex_info->lr, + sizeof(p_ex_info->lr), "%pS", (void *)regs->ARM_PT_REG_LR); + slen = snprintf(p_ex_info->panic_buf, + sizeof(p_ex_info->panic_buf), "%s", str); + + msg = p_ex_info->panic_buf; + + if ((slen >= 1) && (msg[slen-1] == '\n')) + msg[slen-1] = 0; + } + } +#endif + + return 0; +} + +int sec_debug_save_panic_info(const char *str, unsigned long caller) +{ +#ifdef CONFIG_USER_RESET_DEBUG + _kern_ex_info_t *p_ex_info; +#endif + if (!secdbg_apss) + return -ENOMEM; + snprintf(secdbg_apss->excp.panic_caller, + sizeof(secdbg_apss->excp.panic_caller), "%pS", (void *)caller); + snprintf(secdbg_apss->excp.panic_msg, + sizeof(secdbg_apss->excp.panic_msg), "%s", str); + snprintf(secdbg_apss->excp.thread, + sizeof(secdbg_apss->excp.thread), "%s:%d", current->comm, + task_pid_nr(current)); + +#ifdef CONFIG_USER_RESET_DEBUG + sec_debug_store_extc_idx(false); + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + if (p_ex_info->cpu == -1) { + int slen; + char *msg; + + p_ex_info->cpu = smp_processor_id(); + snprintf(p_ex_info->task_name, + sizeof(p_ex_info->task_name), "%s", current->comm); + p_ex_info->ktime = local_clock(); + snprintf(p_ex_info->pc, + sizeof(p_ex_info->pc), "%pS", (void *)(caller-0x4)); + snprintf(p_ex_info->lr, + sizeof(p_ex_info->lr), "%pS", (void *)caller); + slen = snprintf(p_ex_info->panic_buf, + sizeof(p_ex_info->panic_buf), "%s", str); + + msg = p_ex_info->panic_buf; + + if ((slen >= 1) && (msg[slen-1] == '\n')) + msg[slen-1] = 0; + } + } +#endif + + return 0; +} + +int sec_debug_summary_add_infomon(char *name, unsigned int size, phys_addr_t pa) +{ + if (!secdbg_apss) + return -ENOMEM; + + if (secdbg_apss->info_mon.idx >= ARRAY_SIZE(secdbg_apss->info_mon.var)) + return -ENOMEM; + + strlcpy(secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].name, + name, sizeof(secdbg_apss->info_mon.var[0].name)); + secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].sizeof_type + = size; + secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].var_paddr = pa; + + secdbg_apss->info_mon.idx++; + + return 0; +} + +int sec_debug_summary_add_varmon(char *name, unsigned int size, phys_addr_t pa) +{ + if (!secdbg_apss) + return -ENOMEM; + + if (secdbg_apss->var_mon.idx >= ARRAY_SIZE(secdbg_apss->var_mon.var)) + return -ENOMEM; + + strlcpy(secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].name, name, + sizeof(secdbg_apss->var_mon.var[0].name)); + secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].sizeof_type = size; + secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].var_paddr = pa; + + secdbg_apss->var_mon.idx++; + + return 0; +} + +#ifdef CONFIG_SEC_DEBUG_MDM_FILE_INFO +void sec_set_mdm_summary_info(char *str_buf) +{ + snprintf(secdbg_apss->mdmerr_info, + sizeof(secdbg_apss->mdmerr_info), "%s", str_buf); +} +#endif + +static int ___build_root_init(char *str) +{ + char *st, *ed; + int len; + ed = strstr(str, "/android/kernel"); + if (!ed || ed == str) + return -1; + *ed = '\0'; + st = strrchr(str, '/'); + if (!st) + return -1; + st++; + len = (unsigned long)ed - (unsigned long)st + 1; + memmove(str, st, len); + return 0; +} + +#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML +void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value) +{ + if (SAVE_FREQ == flag) + cpu_frequency[cpu] = value; + else if (SAVE_VOLT == flag) + cpu_volt[cpu] = (unsigned int)value; +} +#else +void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value) +{ +} +#endif + +void sec_debug_secure_app_addr_size(uint32_t addr, uint32_t size) +{ + tzapps_start_addr = addr; + tzapps_size = size; +} + +static int __init _set_kconst(struct sec_debug_summary_data_apss *p) +{ + p->kconst.nr_cpus = NR_CPUS; + p->kconst.per_cpu_offset.pa = virt_to_phys(__per_cpu_offset); + p->kconst.per_cpu_offset.size = sizeof(__per_cpu_offset[0]); + p->kconst.per_cpu_offset.count = sizeof(__per_cpu_offset) / + sizeof(__per_cpu_offset[0]); + p->kconst.phys_offset = PHYS_OFFSET; + p->kconst.page_offset = PAGE_OFFSET; + p->kconst.va_bits = VA_BITS; + p->kconst.kimage_vaddr = kimage_vaddr; + p->kconst.kimage_voffset = kimage_voffset; + + return 0; +} + +static int __init summary_init_infomon(void) +{ + if (___build_root_init(build_root) == 0) + ADD_STR_TO_INFOMON(build_root); + ADD_STR_TO_INFOMON(linux_banner); +#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP + sec_debug_summary_add_infomon("Kernel cmdline", -1, + __pa(erased_command_line)); +#else + sec_debug_summary_add_infomon("Kernel cmdline", -1, + __pa(saved_command_line)); +#endif + sec_debug_summary_add_infomon("Hardware name", -1, + __pa(sec_debug_arch_desc)); + + return 0; +} + +static int __init summary_init_varmon(void) +{ + uint64_t last_pet_paddr = 0; + uint64_t last_ns_paddr = 0; + + /* save paddrs of last_pet und last_ns */ + if (secdbg_paddr && secdbg_log) { + last_pet_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, last_pet); + last_ns_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, last_ns); + sec_debug_summary_add_varmon("last_pet", + sizeof((secdbg_log->last_pet)), last_pet_paddr); + sec_debug_summary_add_varmon("last_ns", + sizeof((secdbg_log->last_ns.counter)), + last_ns_paddr); + } else + pr_emerg("**** secdbg_log or secdbg_paddr is not initialized ****\n"); + +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + ADD_VAR_TO_VARMON(boot_reason); + ADD_VAR_TO_VARMON(cold_boot); +#endif + +#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML + for (i = 0; i < CONFIG_NR_CPUS; i++) { + ADD_STR_ARRAY_TO_VARMON(cpu_state[i], i, CPU_STAT_CORE); + ADD_ARRAY_TO_VARMON(cpu_frequency[i], i, CPU_FREQ_CORE); + ADD_ARRAY_TO_VARMON(cpu_volt[i], i, CPU_VOLT_CORE); + } +#endif + + return 0; +} + +static int __init summary_init_sched_log(struct sec_debug_summary_data_apss *p) +{ + if (!secdbg_paddr) + return -ENOMEM; + + p->sched_log.sched_idx_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, idx_sched); + p->sched_log.sched_buf_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, sched); + p->sched_log.sched_struct_sz = + sizeof(struct sched_log); + p->sched_log.sched_array_cnt = SCHED_LOG_MAX; + + p->sched_log.irq_idx_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, idx_irq); + p->sched_log.irq_buf_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, irq); + p->sched_log.irq_struct_sz = + sizeof(struct irq_log); + p->sched_log.irq_array_cnt = SCHED_LOG_MAX; + + p->sched_log.secure_idx_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, idx_secure); + p->sched_log.secure_buf_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, secure); + p->sched_log.secure_struct_sz = + sizeof(struct secure_log); + p->sched_log.secure_array_cnt = TZ_LOG_MAX; + + p->sched_log.irq_exit_idx_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, idx_irq_exit); + p->sched_log.irq_exit_buf_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, irq_exit); + p->sched_log.irq_exit_struct_sz = + sizeof(struct irq_exit_log); + p->sched_log.irq_exit_array_cnt = SCHED_LOG_MAX; + +#ifdef CONFIG_SEC_DEBUG_MSG_LOG + p->sched_log.msglog_idx_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, idx_secmsg); + p->sched_log.msglog_buf_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, secmsg); + p->sched_log.msglog_struct_sz = + sizeof(struct secmsg_log); + p->sched_log.msglog_array_cnt = MSG_LOG_MAX; +#else + p->sched_log.msglog_idx_paddr = 0; + p->sched_log.msglog_buf_paddr = 0; + p->sched_log.msglog_struct_sz = 0; + p->sched_log.msglog_array_cnt = 0; +#endif + + return 0; +} + +static unsigned long get_wdog_regsave_paddr(void) +{ + return __pa(&cpu_buf_paddr); +} +unsigned int get_last_pet_paddr(void) +{ +#if 0 // MUST BE CHECK + return virt_to_phys(&wdog_dd->last_pet); +#else + return 0; +#endif +} + +void sec_debug_summary_bark_dump(unsigned long cpu_data, + unsigned long pcpu_data, unsigned long cpu_buf, + unsigned long pcpu_buf, unsigned long cpu_ctx_size) +{ + cpu_data_vaddr = cpu_data; + cpu_data_paddr = pcpu_data; + cpu_buf_vaddr = cpu_buf; + cpu_buf_paddr = pcpu_buf; + max_cpu_ctx_size = cpu_ctx_size; +} + +static int sec_debug_summary_set_msm_dump_info( + struct sec_debug_summary_data_apss *apss) +{ + apss->cpu_reg.msm_dump_info.cpu_data_paddr = cpu_data_paddr; + apss->cpu_reg.msm_dump_info.cpu_buf_paddr = cpu_buf_paddr; + apss->cpu_reg.msm_dump_info.cpu_ctx_size = max_cpu_ctx_size; + apss->cpu_reg.msm_dump_info.offset = 0x10; + + pr_info("cpu_data_paddr = 0x%llx\n", + apss->cpu_reg.msm_dump_info.cpu_data_paddr); + pr_info("cpu_buf_paddr = 0x%llx\n", + apss->cpu_reg.msm_dump_info.cpu_buf_paddr); + + return 0; +} + +static int __init summary_init_core_reg(struct sec_debug_summary_data_apss *p) +{ + /* setup sec debug core reg info */ + p->cpu_reg.sec_debug_core_reg_paddr = virt_to_phys(&sec_debug_core_reg); + + pr_info("sec_debug_core_reg_paddr = 0x%llx\n", + p->cpu_reg.sec_debug_core_reg_paddr); + +#ifdef CONFIG_QCOM_MEMORY_DUMP_V2 + /* setup qc core reg info */ + sec_debug_summary_set_msm_dump_info(p); +#endif + + return 0; +} + +static int __init summary_init_avc_log(struct sec_debug_summary_data_apss *p) +{ + if (!secdbg_paddr) + return -EINVAL; + +#ifdef CONFIG_SEC_DEBUG_AVC_LOG + p->avc_log.secavc_idx_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, idx_secavc); + p->avc_log.secavc_buf_paddr = secdbg_paddr + + offsetof(struct sec_debug_log, secavc); + p->avc_log.secavc_struct_sz = + sizeof(struct secavc_log); + p->avc_log.secavc_array_cnt = AVC_LOG_MAX; +#else + p->avc_log.secavc_idx_paddr = 0; + p->avc_log.secavc_buf_paddr = 0; + p->avc_log.secavc_struct_sz = 0; + p->avc_log.secavc_array_cnt = 0; +#endif + + return 0; +} + +int sec_debug_is_modem_separate_debug_ssr(void) +{ + return secdbg_summary->priv.modem.seperate_debug; +} + +#define SET_MEMBER_TYPE_INFO(PTR, TYPE, MEMBER) \ + { \ + (PTR)->size = sizeof(((TYPE *)0)->MEMBER); \ + (PTR)->offset = offsetof(TYPE, MEMBER); \ + } + +int summary_set_task_info(struct sec_debug_summary_data_apss *apss) +{ + extern struct task_struct init_task; + + apss->task.stack_size = THREAD_SIZE; + apss->task.start_sp = THREAD_START_SP; + apss->task.irq_stack.pcpu_stack = (uint64_t)&irq_stack; + apss->task.irq_stack.size = IRQ_STACK_SIZE; + apss->task.irq_stack.start_sp = IRQ_STACK_START_SP; + + apss->task.ti.struct_size = sizeof(struct thread_info); + SET_MEMBER_TYPE_INFO(&apss->task.ti.flags, struct thread_info, flags); + SET_MEMBER_TYPE_INFO(&apss->task.ts.cpu, struct task_struct, cpu); + + apss->task.ts.struct_size = sizeof(struct task_struct); + SET_MEMBER_TYPE_INFO(&apss->task.ts.state, struct task_struct, state); + SET_MEMBER_TYPE_INFO(&apss->task.ts.exit_state, struct task_struct, + exit_state); + SET_MEMBER_TYPE_INFO(&apss->task.ts.stack, struct task_struct, stack); + SET_MEMBER_TYPE_INFO(&apss->task.ts.flags, struct task_struct, flags); + SET_MEMBER_TYPE_INFO(&apss->task.ts.on_cpu, struct task_struct, on_cpu); + SET_MEMBER_TYPE_INFO(&apss->task.ts.pid, struct task_struct, pid); + SET_MEMBER_TYPE_INFO(&apss->task.ts.comm, struct task_struct, comm); + SET_MEMBER_TYPE_INFO(&apss->task.ts.tasks_next, struct task_struct, + tasks.next); + SET_MEMBER_TYPE_INFO(&apss->task.ts.thread_group_next, + struct task_struct, thread_group.next); + SET_MEMBER_TYPE_INFO(&apss->task.ts.fp, struct task_struct, + thread.cpu_context.fp); + SET_MEMBER_TYPE_INFO(&apss->task.ts.sp, struct task_struct, + thread.cpu_context.sp); + SET_MEMBER_TYPE_INFO(&apss->task.ts.pc, struct task_struct, + thread.cpu_context.pc); + +#ifdef CONFIG_SCHED_INFO + /* sched_info */ + SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__pcount, + struct task_struct, sched_info.pcount); + SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__run_delay, + struct task_struct, + sched_info.run_delay); + SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__last_arrival, + struct task_struct, + sched_info.last_arrival); + SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__last_queued, + struct task_struct, + sched_info.last_queued); +#endif + + apss->task.init_task = (uint64_t)&init_task; + apss->task.ropp.magic = 0x0; + + return 0; +} + +#ifdef CONFIG_MSM_PM +void summary_set_lpm_info_cci(uint64_t paddr) +{ + if (secdbg_apss) { + pr_info("%s : 0x%llx\n", __func__, paddr); + secdbg_apss->aplpm.p_cci = paddr; + } +} +#else +void summary_set_lpm_info_cci(uint64_t phy_addr) +{ +} +#endif + +void * sec_debug_summary_get_modem(void) +{ + if (secdbg_summary) { + return (void *)&secdbg_summary->priv.modem; + } else { + pr_info("%s : secdbg_summary is null.\n", __func__); + return NULL; + } +} + +int __init sec_debug_summary_init(void) +{ +#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML + short i; +#endif + + pr_info("SMEM_ID_VENDOR2=0x%x size=0x%lx\n", + (unsigned int)SMEM_ID_VENDOR2, + sizeof(struct sec_debug_summary)); + + /* set summary address in smem for other subsystems to see */ + secdbg_summary = (struct sec_debug_summary *)smem_alloc( + SMEM_ID_VENDOR2, + sizeof(struct sec_debug_summary), + 0, + SMEM_ANY_HOST_FLAG); + + if (secdbg_summary == NULL) { + pr_info("smem alloc failed!\n"); + return -ENOMEM; + } + + memset(secdbg_summary, 0, (unsigned long)sizeof(secdbg_summary)); + + secdbg_summary->secure_app_start_addr = tzapps_start_addr; + secdbg_summary->secure_app_size = tzapps_size; + + secdbg_apss = &secdbg_summary->priv.apss; + + secdbg_summary->apss = (struct sec_debug_summary_data_apss *) + (smem_virt_to_phys(&secdbg_summary->priv.apss)&0xFFFFFFFF); + secdbg_summary->rpm = (struct sec_debug_summary_data *) + (smem_virt_to_phys(&secdbg_summary->priv.rpm)&0xFFFFFFFF); + secdbg_summary->modem = (struct sec_debug_summary_data_modem *) + (smem_virt_to_phys(&secdbg_summary->priv.modem)&0xFFFFFFFF); + secdbg_summary->dsps = (struct sec_debug_summary_data *) + (smem_virt_to_phys(&secdbg_summary->priv.dsps)&0xFFFFFFFF); + + pr_info("apss(%lx) rpm(%lx) modem(%lx) dsps(%lx)\n", + (unsigned long)secdbg_summary->apss, + (unsigned long)secdbg_summary->rpm, + (unsigned long)secdbg_summary->modem, + (unsigned long)secdbg_summary->dsps); + + + strlcpy(secdbg_apss->name, "APSS", sizeof(secdbg_apss->name) + 1); + strlcpy(secdbg_apss->state, "Init", sizeof(secdbg_apss->state) + 1); + secdbg_apss->nr_cpus = CONFIG_NR_CPUS; + + sec_debug_summary_set_kloginfo(&secdbg_apss->log.first_idx_paddr, + &secdbg_apss->log.next_idx_paddr, + &secdbg_apss->log.log_paddr, &secdbg_apss->log.size_paddr); + + secdbg_apss->tz_core_dump = + (struct msm_dump_data **)get_wdog_regsave_paddr(); + + summary_init_infomon(); + + summary_init_varmon(); + + summary_init_sched_log(secdbg_apss); + + summary_init_core_reg(secdbg_apss); + + summary_init_avc_log(secdbg_apss); + + sec_debug_summary_set_kallsyms_info(secdbg_apss); + + _set_kconst(secdbg_apss); + +#ifdef CONFIG_QCOM_RTB + sec_debug_summary_set_rtb_info(secdbg_apss); +#endif + + summary_set_task_info(secdbg_apss); + + summary_set_lpm_info_cluster(secdbg_apss); + summary_set_lpm_info_runqueues(secdbg_apss); + + summary_set_msm_memdump_info(secdbg_apss); + + /* fill magic nubmer last to ensure data integrity when the magic + * numbers are written + */ + secdbg_summary->magic[0] = SEC_DEBUG_SUMMARY_MAGIC0; + secdbg_summary->magic[1] = SEC_DEBUG_SUMMARY_MAGIC1; + secdbg_summary->magic[2] = SEC_DEBUG_SUMMARY_MAGIC2; + secdbg_summary->magic[3] = SEC_DEBUG_SUMMARY_MAGIC3; + + return 0; +} + +subsys_initcall_sync(sec_debug_summary_init); diff --git a/drivers/debug/sec_debug_user_reset.c b/drivers/debug/sec_debug_user_reset.c new file mode 100644 index 000000000000..bf889c40f1aa --- /dev/null +++ b/drivers/debug/sec_debug_user_reset.c @@ -0,0 +1,923 @@ +/* + * drivers/debug/sec_debug_user_reset.c + * + * COPYRIGHT(C) 2006-2018 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "sec_debug_internal.h" + +static char rr_str[][3] = { + [USER_UPLOAD_CAUSE_SMPL] = "SP", + [USER_UPLOAD_CAUSE_WTSR] = "WP", + [USER_UPLOAD_CAUSE_WATCHDOG] = "DP", + [USER_UPLOAD_CAUSE_PANIC] = "KP", + [USER_UPLOAD_CAUSE_MANUAL_RESET] = "MP", + [USER_UPLOAD_CAUSE_POWER_RESET] = "PP", + [USER_UPLOAD_CAUSE_REBOOT] = "RP", + [USER_UPLOAD_CAUSE_BOOTLOADER_REBOOT] = "BP", + [USER_UPLOAD_CAUSE_POWER_ON] = "NP", + [USER_UPLOAD_CAUSE_THERMAL] = "TP", + [USER_UPLOAD_CAUSE_UNKNOWN] = "NP", +}; + +char *klog_buf; +uint32_t klog_size; +char *klog_read_buf; +struct debug_reset_header *klog_info; +static DEFINE_MUTEX(klog_mutex); + +char *summary_buf; +struct debug_reset_header *summary_info; +static DEFINE_MUTEX(summary_mutex); +static unsigned reset_reason = 0xFFEEFFEE; + +char *tzlog_buf; +struct debug_reset_header *tzlog_info; +static DEFINE_MUTEX(tzlog_mutex); + +static int reset_write_cnt = -1; + +uint32_t sec_debug_get_reset_reason(void) +{ + return reset_reason; +} +EXPORT_SYMBOL(sec_debug_get_reset_reason); + +int sec_debug_get_reset_write_cnt(void) +{ + return reset_write_cnt; +} +EXPORT_SYMBOL(sec_debug_get_reset_write_cnt); + +char *sec_debug_get_reset_reason_str(unsigned int reason) +{ + if (reason < USER_UPLOAD_CAUSE_MIN || reason > USER_UPLOAD_CAUSE_MAX) + reason = USER_UPLOAD_CAUSE_UNKNOWN; + + return rr_str[reason]; +} +EXPORT_SYMBOL(sec_debug_get_reset_reason_str); + +static void sec_debug_update_reset_reason(uint32_t debug_partition_rr) +{ + static char updated = 0; + + if (!updated) { + reset_reason = debug_partition_rr; + updated = 1; + pr_info("partition[%d] result[%s]\n", + debug_partition_rr, + sec_debug_get_reset_reason_str(reset_reason)); + } +} + +static void reset_reason_update_and_clear(void) +{ + ap_health_t *p_health; + uint32_t rr_data; + + p_health = ap_health_data_read(); + if (p_health == NULL) { + pr_err("p_health is NULL\n"); + return; + } + + pr_info("done\n"); + rr_data = sec_debug_get_reset_reason(); + switch (rr_data) { + case USER_UPLOAD_CAUSE_SMPL: + p_health->daily_rr.sp++; + p_health->rr.sp++; + break; + case USER_UPLOAD_CAUSE_WTSR: + p_health->daily_rr.wp++; + p_health->rr.wp++; + break; + case USER_UPLOAD_CAUSE_WATCHDOG: + p_health->daily_rr.dp++; + p_health->rr.dp++; + break; + case USER_UPLOAD_CAUSE_PANIC: + p_health->daily_rr.kp++; + p_health->rr.kp++; + break; + case USER_UPLOAD_CAUSE_MANUAL_RESET: + p_health->daily_rr.mp++; + p_health->rr.mp++; + break; + case USER_UPLOAD_CAUSE_POWER_RESET: + p_health->daily_rr.pp++; + p_health->rr.pp++; + break; + case USER_UPLOAD_CAUSE_REBOOT: + p_health->daily_rr.rp++; + p_health->rr.rp++; + break; + case USER_UPLOAD_CAUSE_THERMAL: + p_health->daily_rr.tp++; + p_health->rr.tp++; + break; + default: + p_health->daily_rr.np++; + p_health->rr.np++; + } + + p_health->last_rst_reason = 0; + ap_health_data_write(p_health); +} + +static int set_reset_reason_proc_show(struct seq_file *m, void *v) +{ + uint32_t rr_data = sec_debug_get_reset_reason(); + static uint32_t rr_cnt_update = 1; + + seq_printf(m, "%sON\n", sec_debug_get_reset_reason_str(rr_data)); + if (rr_cnt_update) { + reset_reason_update_and_clear(); + rr_cnt_update = 0; + } + + return 0; +} + +static int sec_reset_reason_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, set_reset_reason_proc_show, NULL); +} + +static const struct file_operations sec_reset_reason_proc_fops = { + .open = sec_reset_reason_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static phys_addr_t sec_debug_reset_ex_info_paddr; +static unsigned sec_debug_reset_ex_info_size; +rst_exinfo_t *sec_debug_reset_ex_info; +ex_info_fault_t ex_info_fault[NR_CPUS]; + +void sec_debug_store_extc_idx(bool prefix) +{ + _kern_ex_info_t *p_ex_info; + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + if (p_ex_info->extc_idx == 0) { + p_ex_info->extc_idx = get_sec_log_idx(); + if (prefix) + p_ex_info->extc_idx += SEC_DEBUG_RESET_EXTRC_SIZE; + } + } +} +EXPORT_SYMBOL(sec_debug_store_extc_idx); + +void sec_debug_store_bug_string(const char *fmt, ...) +{ + va_list args; + _kern_ex_info_t *p_ex_info; + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + va_start(args, fmt); + vsnprintf(p_ex_info->bug_buf, + sizeof(p_ex_info->bug_buf), fmt, args); + va_end(args); + } +} +EXPORT_SYMBOL(sec_debug_store_bug_string); + +void sec_debug_store_additional_dbg(enum extra_info_dbg_type type, + unsigned int value, const char *fmt, ...) +{ + va_list args; + _kern_ex_info_t *p_ex_info; + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + switch (type) { + case DBG_0_GLAD_ERR: + va_start(args, fmt); + vsnprintf(p_ex_info->dbg0, + sizeof(p_ex_info->dbg0), fmt, args); + va_end(args); + break; + case DBG_1_UFS_ERR: + va_start(args, fmt); + vsnprintf(p_ex_info->ufs_err, + sizeof(p_ex_info->ufs_err), fmt, args); + va_end(args); + break; + case DBG_2_DISPLAY_ERR: + va_start(args, fmt); + vsnprintf(p_ex_info->display_err, + sizeof(p_ex_info->display_err), fmt, args); + va_end(args); + break; + case DBG_3_RESERVED ... DBG_5_RESERVED: + break; + default: + break; + } + } +} +EXPORT_SYMBOL(sec_debug_store_additional_dbg); + +static void sec_debug_init_panic_extra_info(void) +{ + _kern_ex_info_t *p_ex_info; + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + memset((void *)&sec_debug_reset_ex_info->kern_ex_info, 0, + sizeof(sec_debug_reset_ex_info->kern_ex_info)); + p_ex_info->cpu = -1; + pr_info("%s: ex_info memory initialized size[%ld]\n", + __func__, sizeof(kern_exinfo_t)); + } +} + +static int __init sec_debug_ex_info_setup(char *str) +{ + unsigned size = memparse(str, &str); + int ret; + + if (size && (*str == '@')) { + unsigned long long base = 0; + + ret = kstrtoull(++str, 0, &base); + if (ret) { + pr_err("failed to parse sec_dbg_ex_info\n"); + return ret; + } + + sec_debug_reset_ex_info_paddr = base; + sec_debug_reset_ex_info_size = + (size + 0x1000 - 1) & ~(0x1000 - 1); + + pr_info("ex info phy=0x%llx, size=0x%x\n", + (uint64_t)sec_debug_reset_ex_info_paddr, + sec_debug_reset_ex_info_size); + } + return 1; +} +__setup("sec_dbg_ex_info=", sec_debug_ex_info_setup); + +static int __init sec_debug_get_extra_info_region(void) +{ + if (!sec_debug_reset_ex_info_paddr || !sec_debug_reset_ex_info_size) + return -1; + + sec_debug_reset_ex_info = ioremap_cache(sec_debug_reset_ex_info_paddr, + sec_debug_reset_ex_info_size); + + if (!sec_debug_reset_ex_info) { + pr_err("Failed to remap nocache ex info region\n"); + return -1; + } + + sec_debug_init_panic_extra_info(); + + return 0; +} +arch_initcall_sync(sec_debug_get_extra_info_region); + +struct debug_reset_header *get_debug_reset_header(void) +{ + struct debug_reset_header *header = NULL; + static int get_state = DRH_STATE_INIT; + + if (get_state == DRH_STATE_INVALID) + return NULL; + + header = kmalloc(sizeof(struct debug_reset_header), GFP_KERNEL); + if (!header) { + pr_err("%s : fail - kmalloc for debug_reset_header\n", __func__); + return NULL; + } + if (!read_debug_partition(debug_index_reset_summary_info, header)) { + pr_err("%s : fail - get param!! debug_reset_header\n", __func__); + kfree(header); + header = NULL; + return NULL; + } + + if (get_state != DRH_STATE_VALID) { + if (header->write_times == header->read_times) { + pr_err("%s : untrustworthy debug_reset_header\n", __func__); + get_state = DRH_STATE_INVALID; + kfree(header); + header = NULL; + return NULL; + } + reset_write_cnt = header->write_times; + get_state = DRH_STATE_VALID; + } + + return header; +} + +static int set_debug_reset_header(struct debug_reset_header *header) +{ + int ret = 0; + static int set_state = DRH_STATE_INIT; + + if (set_state == DRH_STATE_VALID) { + pr_info("%s : debug_reset_header working well\n", __func__); + return ret; + } + + if ((header->write_times - 1) == header->read_times) { + pr_info("%s : debug_reset_header working well\n", __func__); + header->read_times++; + } else { + pr_info("%s : debug_reset_header read[%d] and write[%d] work sync error.\n", + __func__, header->read_times, header->write_times); + header->read_times = header->write_times; + } + + if (!write_debug_partition(debug_index_reset_summary_info, header)) { + pr_err("%s : fail - set param!! debug_reset_header\n", __func__); + ret = -ENOENT; + } else { + set_state = DRH_STATE_VALID; + } + + return ret; +} + +static int sec_reset_summary_info_init(void) +{ + int ret = 0; + + if (summary_buf != NULL) + return true; + + if (summary_info != NULL) { + pr_err("%s : already memory alloc for summary_info\n", __func__); + return -EINVAL; + } + + summary_info = get_debug_reset_header(); + if (summary_info == NULL) + return -EINVAL; + + if (summary_info->summary_size > SEC_DEBUG_RESET_SUMMARY_SIZE) { + pr_err("%s : summary_size has problem.\n", __func__); + ret = -EINVAL; + goto error_summary_info; + } + + summary_buf = vmalloc(SEC_DEBUG_RESET_SUMMARY_SIZE); + if (!summary_buf) { + pr_err("%s : fail - kmalloc for summary_buf\n", __func__); + ret = -ENOMEM; + goto error_summary_info; + } + if (!read_debug_partition(debug_index_reset_summary, summary_buf)) { + pr_err("%s : fail - get param!! summary data\n", __func__); + ret = -ENOENT; + goto error_summary_buf; + } + + pr_info("%s : w[%d] r[%d] idx[%d] size[%d]\n", + __func__, summary_info->write_times, summary_info->read_times, + summary_info->ap_klog_idx, summary_info->summary_size); + + return ret; + +error_summary_buf: + vfree(summary_buf); +error_summary_info: + kfree(summary_info); + + return ret; +} + +static int sec_reset_summary_completed(void) +{ + int ret = 0; + + ret = set_debug_reset_header(summary_info); + + vfree(summary_buf); + kfree(summary_info); + + summary_info = NULL; + summary_buf = NULL; + pr_info("%s finish\n", __func__); + + return ret; +} + +static ssize_t sec_reset_summary_info_proc_read(struct file *file, + char __user *buf, size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + mutex_lock(&summary_mutex); + if (sec_reset_summary_info_init() < 0) { + mutex_unlock(&summary_mutex); + return -ENOENT; + } + + if ((pos >= summary_info->summary_size) || (pos >= SEC_DEBUG_RESET_SUMMARY_SIZE)) { + pr_info("%s : pos %lld, size %d\n", __func__, pos, summary_info->summary_size); + sec_reset_summary_completed(); + mutex_unlock(&summary_mutex); + return 0; + } + + count = min(len, (size_t)(summary_info->summary_size - pos)); + if (copy_to_user(buf, summary_buf + pos, count)) { + mutex_unlock(&summary_mutex); + return -EFAULT; + } + + *offset += count; + mutex_unlock(&summary_mutex); + return count; +} + +static const struct file_operations sec_reset_summary_info_proc_fops = { + .owner = THIS_MODULE, + .read = sec_reset_summary_info_proc_read, +}; + +static int sec_reset_klog_init(void) +{ + int ret = 0; + + if ((klog_read_buf != NULL) && (klog_buf != NULL)) + return true; + + if (klog_info != NULL) { + pr_err("%s : already memory alloc for klog_info\n", __func__); + return -EINVAL; + } + + klog_info = get_debug_reset_header(); + if (klog_info == NULL) + return -EINVAL; + + klog_read_buf = vmalloc(SEC_DEBUG_RESET_KLOG_SIZE); + if (!klog_read_buf) { + pr_err("%s : fail - vmalloc for klog_read_buf\n", __func__); + ret = -ENOMEM; + goto error_klog_info; + } + if (!read_debug_partition(debug_index_reset_klog, klog_read_buf)) { + pr_err("%s : fail - get param!! summary data\n", __func__); + ret = -ENOENT; + goto error_klog_read_buf; + } + + pr_info("%s : idx[%d]\n", __func__, klog_info->ap_klog_idx); + + klog_size = min((uint32_t)SEC_DEBUG_RESET_KLOG_SIZE, (uint32_t)klog_info->ap_klog_idx); + + klog_buf = vmalloc(klog_size); + if (!klog_buf) { + pr_err("%s : fail - vmalloc for klog_buf\n", __func__); + ret = -ENOMEM; + goto error_klog_read_buf; + } + + if (klog_size && klog_buf && klog_read_buf) { + unsigned int i; + for (i = 0; i < klog_size; i++) + klog_buf[i] = klog_read_buf[(klog_info->ap_klog_idx - klog_size + i) % SEC_DEBUG_RESET_KLOG_SIZE]; + } + + return ret; + +error_klog_read_buf: + vfree(klog_read_buf); +error_klog_info: + kfree(klog_info); + + return ret; +} + +static void sec_reset_klog_completed(void) +{ + set_debug_reset_header(klog_info); + + vfree(klog_buf); + vfree(klog_read_buf); + kfree(klog_info); + + klog_info = NULL; + klog_buf = NULL; + klog_read_buf = NULL; + klog_size = 0; + + pr_info("%s finish\n", __func__); +} + +static ssize_t sec_reset_klog_proc_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + mutex_lock(&klog_mutex); + if (sec_reset_klog_init() < 0) { + mutex_unlock(&klog_mutex); + return -ENOENT; + } + + if (pos >= klog_size) { + pr_info("%s : pos %lld, size %d\n", __func__, pos, klog_size); + sec_reset_klog_completed(); + mutex_unlock(&klog_mutex); + return 0; + } + + count = min(len, (size_t)(klog_size - pos)); + if (copy_to_user(buf, klog_buf + pos, count)) { + mutex_unlock(&klog_mutex); + return -EFAULT; + } + + *offset += count; + mutex_unlock(&klog_mutex); + return count; +} + +static const struct file_operations sec_reset_klog_proc_fops = { + .owner = THIS_MODULE, + .read = sec_reset_klog_proc_read, +}; + +static int sec_reset_tzlog_init(void) +{ + int ret = 0; + + if (tzlog_buf != NULL) + return true; + + if (tzlog_info != NULL) { + pr_err("%s : already memory alloc for tzlog_info\n", __func__); + return -EINVAL; + } + + tzlog_info = get_debug_reset_header(); + if (tzlog_info == NULL) + return -EINVAL; + + if (tzlog_info->stored_tzlog == 0) { + pr_err("%s : The target didn't run SDI operation\n", __func__); + ret = -EINVAL; + goto error_tzlog_info; + } + + tzlog_buf = vmalloc(SEC_DEBUG_RESET_TZLOG_SIZE); + if (!tzlog_buf) { + pr_err("%s : fail - vmalloc for tzlog_read_buf\n", __func__); + ret = -ENOMEM; + goto error_tzlog_info; + } + if (!read_debug_partition(debug_index_reset_tzlog, tzlog_buf)) { + pr_err("%s : fail - get param!! tzlog data\n", __func__); + ret = -ENOENT; + goto error_tzlog_buf; + } + + return ret; + +error_tzlog_buf: + vfree(tzlog_buf); +error_tzlog_info: + kfree(tzlog_info); + + return ret; +} + +static void sec_reset_tzlog_completed(void) +{ + set_debug_reset_header(tzlog_info); + + vfree(tzlog_buf); + kfree(tzlog_info); + + tzlog_info = NULL; + tzlog_buf = NULL; + + pr_info("%s finish\n", __func__); +} + +static ssize_t sec_reset_tzlog_proc_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + mutex_lock(&tzlog_mutex); + if (sec_reset_tzlog_init() < 0) { + mutex_unlock(&tzlog_mutex); + return -ENOENT; + } + + if (pos >= SEC_DEBUG_RESET_TZLOG_SIZE) { + pr_info("%s : pos %lld, size %d\n", __func__, pos, SEC_DEBUG_RESET_TZLOG_SIZE); + sec_reset_tzlog_completed(); + mutex_unlock(&tzlog_mutex); + return 0; + } + + count = min(len, (size_t)(SEC_DEBUG_RESET_TZLOG_SIZE - pos)); + if (copy_to_user(buf, tzlog_buf + pos, count)) { + mutex_unlock(&tzlog_mutex); + return -EFAULT; + } + + *offset += count; + mutex_unlock(&tzlog_mutex); + return count; +} + +static const struct file_operations sec_reset_tzlog_proc_fops = { + .owner = THIS_MODULE, + .read = sec_reset_tzlog_proc_read, +}; + +static int set_store_lastkmsg_proc_show(struct seq_file *m, void *v) +{ + struct debug_reset_header *check_store = NULL; + + if (check_store != NULL) { + pr_err("%s : already memory alloc for check_store\n", __func__); + return -EINVAL; + } + + check_store = get_debug_reset_header(); + if (check_store == NULL) { + seq_printf(m, "0\n"); + } else { + seq_printf(m, "1\n"); + } + + if (check_store != NULL) { + kfree(check_store); + check_store = NULL; + } + + return 0; +} + +static int sec_store_lastkmsg_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, set_store_lastkmsg_proc_show, NULL); +} + +static const struct file_operations sec_store_lastkmsg_proc_fops = { + .open = sec_store_lastkmsg_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void sec_restore_modem_reset_data(void) +{ + void *p_modem = sec_debug_summary_get_modem(); + struct debug_reset_header *header = get_debug_reset_header(); + + if (!header) { + pr_info("%s : updated nothing.\n", __func__); + return; + } + + if (sec_debug_get_reset_reason() != USER_UPLOAD_CAUSE_PANIC) { + pr_info("%s : it was not kernel panic.\n", __func__); + return; + } + + if (p_modem) { + read_debug_partition(debug_index_modem_info, p_modem); + pr_info("%s : complete.\n", __func__); + } else { + pr_info("%s : skip.\n", __func__); + } +} + +void sec_debug_summary_modem_print(void) +{ + if (sec_debug_get_reset_reason() != USER_UPLOAD_CAUSE_PANIC) { + pr_info("%s : it was not kernel panic.\n", __func__); + return; + } + + pr_info("0x%016lx\n", + (unsigned long)sec_debug_summary_get_modem()); + + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, + sec_debug_summary_get_modem(), + 0x190, 1); +} +EXPORT_SYMBOL(sec_debug_summary_modem_print); + +static int sec_reset_reason_dbg_part_notifier_callback( + struct notifier_block *nfb, unsigned long action, void *data) +{ + ap_health_t *p_health; + uint32_t rr_data; + + switch (action) { + case DBG_PART_DRV_INIT_DONE: + p_health = ap_health_data_read(); + if (!p_health) + return NOTIFY_DONE; + + sec_debug_update_reset_reason( + p_health->last_rst_reason); + rr_data = sec_debug_get_reset_reason(); + sec_restore_modem_reset_data(); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + + +static struct notifier_block sec_reset_reason_dbg_part_notifier = { + .notifier_call = sec_reset_reason_dbg_part_notifier_callback, +}; + +static int __init sec_debug_reset_reason_init(void) +{ + struct proc_dir_entry *entry; + + entry = proc_create("reset_reason", S_IWUGO, NULL, + &sec_reset_reason_proc_fops); + if (unlikely(!entry)) + return -ENOMEM; + + entry = proc_create("reset_summary", S_IWUGO, NULL, + &sec_reset_summary_info_proc_fops); + if (unlikely(!entry)) + return -ENOMEM; + + entry = proc_create("reset_klog", S_IWUGO, NULL, + &sec_reset_klog_proc_fops); + if (unlikely(!entry)) + return -ENOMEM; + + entry = proc_create("reset_tzlog", S_IWUGO, NULL, + &sec_reset_tzlog_proc_fops); + if (unlikely(!entry)) + return -ENOMEM; + + entry = proc_create("store_lastkmsg", S_IWUGO, NULL, + &sec_store_lastkmsg_proc_fops); + if (unlikely(!entry)) + return -ENOMEM; + + dbg_partition_notifier_register(&sec_reset_reason_dbg_part_notifier); + + return 0; +} +device_initcall(sec_debug_reset_reason_init); + +static ssize_t show_recovery_cause(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char recovery_cause[256]; + + sec_get_param(param_index_reboot_recovery_cause, recovery_cause); + pr_info("%s\n", recovery_cause); + + return scnprintf(buf, sizeof(recovery_cause), "%s", recovery_cause); +} + +static ssize_t store_recovery_cause(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char recovery_cause[256]; + + if (count > sizeof(recovery_cause)) { + pr_err("input buffer length is out of range.\n"); + return -EINVAL; + } + snprintf(recovery_cause, sizeof(recovery_cause), "%s:%d ", current->comm, task_pid_nr(current)); + if (strlen(recovery_cause) + strlen(buf) >= sizeof(recovery_cause)) { + pr_err("input buffer length is out of range.\n"); + return -EINVAL; + } + strncat(recovery_cause, buf, strlen(buf)); + sec_set_param(param_index_reboot_recovery_cause, recovery_cause); + pr_info("%s\n", recovery_cause); + + return count; +} + +static DEVICE_ATTR(recovery_cause, 0660, show_recovery_cause, store_recovery_cause); + +static struct device *sec_debug_dev; + +static int __init sec_debug_recovery_reason_init(void) +{ + int ret; + + /* create sysfs for reboot_recovery_cause */ + sec_debug_dev = sec_device_create(0, NULL, "sec_debug"); + if (IS_ERR(sec_debug_dev)) { + pr_err("Failed to create device for sec_debug\n"); + return PTR_ERR(sec_debug_dev); + } + + ret = sysfs_create_file(&sec_debug_dev->kobj, &dev_attr_recovery_cause.attr); + if (ret) { + pr_err("Failed to create sysfs group for sec_debug\n"); + sec_device_destroy(sec_debug_dev->devt); + sec_debug_dev = NULL; + return ret; + } + + return 0; +} +device_initcall(sec_debug_recovery_reason_init); + +void _sec_debug_store_backtrace(unsigned long where) +{ + static int offset; + unsigned int max_size = 0; + _kern_ex_info_t *p_ex_info; + + if (sec_debug_reset_ex_info) { + p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info; + max_size = (unsigned long long int)&sec_debug_reset_ex_info->rpm_ex_info.info + - (unsigned long long int)p_ex_info->backtrace; + + if (max_size <= offset) + return; + + if (offset) + offset += snprintf(p_ex_info->backtrace+offset, + max_size-offset, " > "); + + offset += snprintf(p_ex_info->backtrace+offset, max_size-offset, + "%pS", (void *)where); + } +} + +void sec_debug_backtrace(void) +{ + static int once = 0; + struct stackframe frame; + int skip_callstack = 0; + + if (!once++) { + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_stack_pointer; + frame.pc = (unsigned long)sec_debug_backtrace; + + while (1) { + int ret; + + ret = unwind_frame(current, &frame); + if (ret < 0) + break; + + if (skip_callstack++ > 3) { + _sec_debug_store_backtrace(frame.pc); + } + } + } +} +EXPORT_SYMBOL(sec_debug_backtrace); + diff --git a/drivers/debug/sec_kcompat.c b/drivers/debug/sec_kcompat.c new file mode 100644 index 000000000000..c4d1fd58e5db --- /dev/null +++ b/drivers/debug/sec_kcompat.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/samsung/sec_kcompat.c + * + * COPYRIGHT(C) 2019 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include +#include +#include + +#if defined(CONFIG_MSM_SMEM) +#include +#endif + +#include "sec_kcompat.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +#if defined(CONFIG_MSM_SMEM) +void * __weak qcom_smem_get(unsigned host, unsigned item, size_t *size) +{ + void * ret; + unsigned int size_tmp = 0; + + ret = smem_get_entry(item, &size_tmp, SMEM_APPS, host); + *size = (size_t)size_tmp; + + return ret; +} + +phys_addr_t __weak qcom_smem_virt_to_phys(void *p) +{ + return smem_virt_to_phys(p) & 0xFFFFFFFFULL; +} +#endif /* CONFIG_MSM_SMEM */ +#endif /* KERNEL_VERSION(4,14,0) */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0) +unsigned long * __weak bitmap_alloc(unsigned int nbits, gfp_t flags) +{ + return kmalloc_array(BITS_TO_LONGS(nbits), sizeof(unsigned long), + flags); +} + +unsigned long * __weak bitmap_zalloc(unsigned int nbits, gfp_t flags) +{ + return bitmap_alloc(nbits, flags | __GFP_ZERO); +} +#endif /* KERNEL_VERSION(4,19,0) */ diff --git a/drivers/debug/sec_kcompat.h b/drivers/debug/sec_kcompat.h new file mode 100644 index 000000000000..fd86e973d06f --- /dev/null +++ b/drivers/debug/sec_kcompat.h @@ -0,0 +1,34 @@ +#ifndef __SEC_KCOMPAT_H__ +#define __SEC_KCOMPAT_H__ + +#include + +#if defined(CONFIG_MSM_SMEM) && defined(CONFIG_QCOM_SMEM) +#error "CONFIG_MSM_SMEM and CONFIG_QCOM_SMEM can not be enabled at the same time" +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) + +#define timer_setup(__timer, __fn, __data) setup_timer(__timer, __fn, __data) + +#if defined(CONFIG_MSM_SMEM) + +#if defined(QCOM_SMEM_HOST_ANY) +#undef QCOM_SMEM_HOST_ANY +#endif +#define QCOM_SMEM_HOST_ANY SMEM_ANY_HOST_FLAG + +void *qcom_smem_get(unsigned host, unsigned item, size_t *size); +extern phys_addr_t qcom_smem_virt_to_phys(void *p); +#endif /* CONFIG_MSM_SMEM */ + +#endif /* KERNEL_VERSION(4,14,0) */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0) + +extern unsigned long *bitmap_alloc(unsigned int nbits, gfp_t flags); +extern unsigned long *bitmap_zalloc(unsigned int nbits, gfp_t flags); + +#endif /* KERNEL_VERSION(4,19,0) */ + +#endif /* __SEC_KCOMPAT_H__ */ diff --git a/drivers/debug/sec_key_notifier.c b/drivers/debug/sec_key_notifier.c new file mode 100644 index 000000000000..e9a09dca279d --- /dev/null +++ b/drivers/debug/sec_key_notifier.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/samsung/debug/sec_key_notifier.c + * + * COPYRIGHT(C) 2016-2019 Samsung Electronics Co., Ltd. All Right 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ + +#include +#include +#include +#include + +#include + +#include "sec_key_notifier.h" + +static DEFINE_SPINLOCK(sec_kn_event_lock); + +static ATOMIC_NOTIFIER_HEAD(sec_kn_notifier_list); + +static atomic_t sec_kn_acceptable_event[KEY_MAX] __read_mostly; + +static void inline update_acceptable_event(unsigned int event_code, bool is_add) +{ + if (is_add) + atomic_inc(&(sec_kn_acceptable_event[event_code])); + else + atomic_dec(&(sec_kn_acceptable_event[event_code])); +} + +int sec_kn_register_notifier(struct notifier_block *nb, + const unsigned int *events, const size_t nr_events) +{ + size_t i; + + for (i = 0; i < nr_events; i++) + update_acceptable_event(events[i], true); + + return atomic_notifier_chain_register(&sec_kn_notifier_list, nb); +} + +int sec_kn_unregister_notifier(struct notifier_block *nb, + const unsigned int *events, const size_t nr_events) +{ + size_t i; + + for (i = 0; i < nr_events; i++) + update_acceptable_event(events[i], false); + + return atomic_notifier_chain_unregister(&sec_kn_notifier_list, nb); +} + +static inline bool is_event_supported(unsigned int event_type, + unsigned int event_code) +{ + bool ret; + + if (event_type != EV_KEY || event_code >= KEY_MAX) + return false; + + ret = !!atomic_read(&(sec_kn_acceptable_event[event_code])); + + return ret; +} + +static void sec_kn_event(struct input_handle *handle, unsigned int event_type, + unsigned int event_code, int value) +{ + struct sec_key_notifier_param param = { + .keycode = event_code, + .down = value, + }; + + if (!is_event_supported(event_type, event_code)) + return; + + spin_lock(&sec_kn_event_lock); + + atomic_notifier_call_chain(&sec_kn_notifier_list, 0, ¶m); + + spin_unlock(&sec_kn_event_lock); +} + +static int sec_kn_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "sec_key_notifier"; + + error = input_register_handle(handle); + if (error) + goto err_free_handle; + + error = input_open_device(handle); + if (error) + goto err_unregister_handle; + + return 0; + +err_unregister_handle: + input_unregister_handle(handle); +err_free_handle: + kfree(handle); + return error; +} + +static void sec_kn_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id sec_kn_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + {}, +}; + +static struct input_handler sec_kn_handler = { + .event = sec_kn_event, + .connect = sec_kn_connect, + .disconnect = sec_kn_disconnect, + .name = "sec_key_notifier", + .id_table = sec_kn_ids, +}; + +static int __init sec_kn_init(void) +{ + int err; + size_t i; + + for (i = 0; i < KEY_MAX; i++) + atomic_set(&(sec_kn_acceptable_event[i]), 0); + + spin_lock_init(&sec_kn_event_lock); + + err = input_register_handler(&sec_kn_handler); + + return err; +} + +static void __exit sec_kn_exit(void) +{ + input_unregister_handler(&sec_kn_handler); +} + +arch_initcall(sec_kn_init); +module_exit(sec_kn_exit); diff --git a/drivers/debug/sec_key_notifier.h b/drivers/debug/sec_key_notifier.h new file mode 100644 index 000000000000..58c773dec494 --- /dev/null +++ b/drivers/debug/sec_key_notifier.h @@ -0,0 +1,35 @@ +/* + * drivers/debug/sec_key_notifier.h + * + * COPYRIGHT(C) 2016 Samsung Electronics Co., Ltd. All Right Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SEC_KEY_NOTIFIER_H__ +#define __SEC_KEY_NOTIFIER_H__ + +struct sec_key_notifier_param { + unsigned int keycode; + int down; +}; + +int sec_kn_register_notifier(struct notifier_block *nb, + const unsigned int *events, const size_t nr_events); + +int sec_kn_unregister_notifier(struct notifier_block *nb, + const unsigned int *events, const size_t nr_events); + +#endif /* __SEC_KEY_NOTIFIER_H__ */ diff --git a/drivers/debug/sec_nad.c b/drivers/debug/sec_nad.c new file mode 100644 index 000000000000..8b79cdca27d6 --- /dev/null +++ b/drivers/debug/sec_nad.c @@ -0,0 +1,1719 @@ +/* + * drivers/debug/sec_nad.c + * + * COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right 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 +#include +#include +#include +#include + +#define NAD_PRINT(format, ...) printk(KERN_ERR "[NAD] " format, ##__VA_ARGS__) +#define NAD_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define BUFF_SZ 256 +/* flag for nad test mode : SMD_NAD or ETC_NAD or MAIN_NAD*/ +static int nad_test_mode = -1; + +#define SMD_NAD_PROG "/system/bin/qnad/qnad.sh" +#define MAIN_NAD_PROG "/system/bin/qnad/qnad_main.sh" +#define ETC_NAD_PROG "/system/bin/qnad/qnad.sh" +#define ERASE_NAD_PRG "/system/bin/qnad/remove_files.sh" + +#define SMD_NAD_RESULT "/nad_refer/NAD_SMD/test_result.csv" +#define MAIN_NAD_RESULT "/nad_refer/NAD_MAIN/test_result.csv" +#define ETC_NAD_RESULT "/nad_refer/NAD/test_result.csv" + +#define SMD_NAD_LOGPATH "logPath:/nad_refer/NAD_SMD" +#define MAIN_NAD_LOGPATH "logPath:/nad_refer/NAD_MAIN" +#define ETC_NAD_LOGPATH "logPath:/nad_refer/NAD" + +struct param_qnad param_qnad_data; +static int erase_pass; +extern unsigned int lpcharge; +unsigned int clk_limit_flag; +int curr_smd = ETC_NAD; +int main_reboot; + +char *STR_TEST_ITEM[ITEM_CNT + 1] = { + "UFS", + "QMESACACHE", + "QMESADDR", + "VDDMIN", + "SUSPEND", + "CCOHERENCY", + "ICACHE", + "CRYPTO", + "DDR", + "SENSOR", + // + "FULL" +}; + +struct kobj_uevent_env nad_uevent; + +int get_nad_result(char *buf, int piece) +{ + int iCnt; + unsigned int smd_result; + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + smd_result = param_qnad_data.total_test_result; + + NAD_PRINT("%s : param_qnad_data.total_test_result=%u,", __func__, + param_qnad_data.total_test_result); + + if (piece == ITEM_CNT) { + for (iCnt = 0; iCnt < ITEM_CNT; iCnt++) { + switch (smd_result & 0x3) { + case 0: + strcat(buf, "[X]"); + break; + case 1: + strcat(buf, "[F]"); + break; + case 2: + strcat(buf, "[P]"); + break; + case 3: + strcat(buf, "[N]"); + break; + } + + smd_result >>= 2; + } + } else { + smd_result >>= 2 * piece; + switch (smd_result & 0x3) { + case 1: + strlcpy(buf, "FAIL", sizeof(buf)); + break; + case 2: + strlcpy(buf, "PASS", sizeof(buf)); + break; + case 3: + strlcpy(buf, "NA", sizeof(buf)); + break; + } + } + + return ITEM_CNT; +err_out: + return 0; +} +EXPORT_SYMBOL(get_nad_result); + +static int NadResult(int smd_nad) +{ + int fd = 0; + int ret = TOTALTEST_UNKNOWN; + int ddr_result = 0; + char buf[512] = { '\0', }; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + + switch (smd_nad) { + case SMD_NAD: + fd = sys_open(SMD_NAD_RESULT, O_RDONLY, 0); + break; + case MAIN_NAD: + fd = sys_open(MAIN_NAD_RESULT, O_RDONLY, 0); + break; + case ETC_NAD: + fd = sys_open(ETC_NAD_RESULT, O_RDONLY, 0); + break; + default: + break; + } + + if (fd >= 0) { + int found = 0; + + printk(KERN_DEBUG); + + while (sys_read(fd, buf, 512)) { + char *ptr; + char *div = "\n"; + char *tok = NULL; + + ptr = buf; + while ((tok = strsep(&ptr, div)) != NULL) { + + if ((strstr(tok, "FAIL"))) { + ret = TOTALTEST_FAIL; + found = 1; + break; + } + + if ((strstr(tok, "AllTestDone"))) { + ret = TOTALTEST_PASS; + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) + ret = TOTALTEST_NO_RESULT_STRING; + + sys_close(fd); + } else { + NAD_PRINT("The result file is not existed. %s\n", __func__); + ret = TOTALTEST_NO_RESULT_FILE; + } + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + if ((smd_nad == SMD_NAD) || (smd_nad == MAIN_NAD)) { + ddr_result = GET_DDR_TEST_RESULT(smd_nad, param_qnad_data.ddrtest_result); + if (ddr_result == DDRTEST_PASS) { + NAD_PRINT("ddr test was succeeded\n"); + } else if ((ret != TOTALTEST_UNKNOWN) && (ddr_result == DDRTEST_FAIL)) { + NAD_PRINT("ddr test was failed\n"); + ret = TOTALTEST_FAIL; + } else + NAD_PRINT("ddr test was not executed\n"); + } + +err_out: + set_fs(old_fs); + + return ret; +} + +static void do_nad(int smd_nad) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/qnad:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + int ret_userapp; + + switch (smd_nad) { + case SMD_NAD: + argv[0] = SMD_NAD_PROG; + argv[1] = SMD_NAD_LOGPATH; + NAD_PRINT("SMD_NAD, nad_test_mode : %d\n", nad_test_mode); + break; + case MAIN_NAD: + argv[0] = MAIN_NAD_PROG; + argv[1] = MAIN_NAD_LOGPATH; + if (main_reboot == 1) + argv[2] = "Reboot"; + NAD_PRINT("MAIN_NAD, nad_test_mode : %d", nad_test_mode); + NAD_PRINT("reboot option enabled \n"); + break; + case ETC_NAD: + argv[0] = ETC_NAD_PROG; + argv[1] = ETC_NAD_LOGPATH; + NAD_PRINT("ETC_NAD, nad_test_mode : %d\n", nad_test_mode); + argv[2] = "Reboot"; + NAD_PRINT + ("Setting an argument to reboot after NAD completes.\n"); + break; + default: + NAD_PRINT("Invalid smd_nad value, nad_test_mode : %d\n", + nad_test_mode); + break; + } + + ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + + if (!ret_userapp) { + NAD_PRINT("%s is executed. ret_userapp = %d\n", argv[0], + ret_userapp); + + if (erase_pass) + erase_pass = 0; + } else { + NAD_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], + ret_userapp); + nad_test_mode = -1; + } +} + +static ssize_t show_nad_acat(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nad_result; + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + NAD_PRINT("%s : magic %x, nad cnt %d, ddr cnt %d, ddr result 0x%2X\n", + __func__, param_qnad_data.magic, + param_qnad_data.nad_remain_count, + param_qnad_data.ddrtest_remain_count, + GET_DDR_TEST_RESULT(ETC_NAD, param_qnad_data.ddrtest_result)); + + nad_result = NadResult(ETC_NAD); + switch (nad_result) { + case TOTALTEST_PASS: { + NAD_PRINT("NAD Passed\n"); + return snprintf(buf, BUFF_SZ, "OK_ACAT_NONE\n"); + } break; + + case TOTALTEST_FAIL: { + NAD_PRINT("NAD fail\n"); + return snprintf(buf, BUFF_SZ, "NG_ACAT_ASV\n"); + } break; + + default: { + NAD_PRINT("NAD No Run\n"); + return snprintf(buf, BUFF_SZ, "OK\n"); + } + } +err_out: + return snprintf(buf, BUFF_SZ, "UNKNOWN\n"); +} + +static ssize_t store_nad_acat(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -1; + int idx = 0; + int nad_loop_count, dram_loop_count; + char temp[NAD_BUFF_SIZE * 3]; + char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE]; + char *nad_ptr, *string; + NAD_PRINT("buf : %s count : %d\n", buf, (int)count); + if ((int)count < NAD_BUFF_SIZE) + return -EINVAL; + + /* Copy buf to nad temp */ + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + + while (idx < NAD_CMD_LIST) { + nad_ptr = strsep(&string, ","); + strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE); + } + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + if (!strncmp(buf, "nad_acat", 8)) { + // checking 1st boot and setting test count + if (param_qnad_data.magic != NAD_SMD_MAGIC) { // <======================== 1st boot at right after SMD D/L done + nad_test_mode = SMD_NAD; + NAD_PRINT("1st boot at SMD\n"); + param_qnad_data.magic = NAD_SMD_MAGIC; + param_qnad_data.nad_remain_count = 0x0; + param_qnad_data.ddrtest_remain_count = 0x0; + + // flushing to param partition + if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) { + pr_err + ("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + curr_smd = nad_test_mode; + do_nad(nad_test_mode); + } else { // <========================not SMD, it can be LCIA, CAL, FINAL and 15 ACAT. + nad_test_mode = ETC_NAD; + ret = sscanf(nad_cmd[1], "%d\n", &nad_loop_count); + if (ret != 1) + return -EINVAL; + + ret = sscanf(nad_cmd[2], "%d\n", &dram_loop_count); + if (ret != 1) + return -EINVAL; + + NAD_PRINT("ETC NAD, nad_acat%d,%d\n", nad_loop_count, + dram_loop_count); + if (!nad_loop_count && !dram_loop_count) { // <+++++++++++++++++ both counts are 0, it means 1. testing refers to current remain_count + + // stop retrying when failure occur during retry test at ACAT/15test + if (param_qnad_data.magic == NAD_SMD_MAGIC + && NadResult(ETC_NAD) == TOTALTEST_FAIL) { + pr_err + ("%s : nad test fail - set the remain counts to 0\n", + __func__); + param_qnad_data.nad_remain_count = 0; + param_qnad_data.ddrtest_remain_count = + 0; + + // flushing to param partition + if (!sec_set_param + (param_index_qnad, + ¶m_qnad_data)) { + pr_err + ("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + } + + if (param_qnad_data.nad_remain_count > 0) { // NAD count still remain + NAD_PRINT + ("nad : nad_remain_count = %d, ddrtest_remain_count = %d\n", + param_qnad_data.nad_remain_count, + param_qnad_data. + ddrtest_remain_count); + param_qnad_data.nad_remain_count--; + param_qnad_data.total_test_result &= 0xffffffff; + +/* if(param_qnad_data.ddrtest_remain_count && param_qnad_data.nad_remain_count == 0) { // last NAD count, and next is ddr test. rebooting will be done by NAD + NAD_PRINT("switching : nad_remain_count = %d, ddrtest_remain_count = %d\n", param_qnad_data.nad_remain_count, param_qnad_data.ddrtest_remain_count); + param_qnad_data.ddrtest_remain_count--; + + do_ddrtest(); + }*/ + + // flushing to param partition + if (!sec_set_param + (param_index_qnad, + ¶m_qnad_data)) { + pr_err + ("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + + curr_smd = nad_test_mode; + do_nad(nad_test_mode); + } else if (param_qnad_data.ddrtest_remain_count) { // NAD already done before, only ddr test remains. then it needs selfrebooting. + NAD_PRINT + ("ddrtest : nad_remain_count = %d, ddrtest_remain_count = %d\n", + param_qnad_data.nad_remain_count, + param_qnad_data. + ddrtest_remain_count); + + //do_msm_restart(REBOOT_HARD, "sec_debug_hw_reset"); + while (1) + ; + } + } else { // <+++++++++++++++++ not (0,0) means 1. new test count came, 2. so overwrite the remain_count, 3. and not reboot by itsself, 4. reboot cmd will come from outside like factory PGM + + param_qnad_data.nad_remain_count = + nad_loop_count; + param_qnad_data.ddrtest_remain_count = + dram_loop_count; + param_qnad_data.ddrtest_mode = UPLOAD_CAUSE_DDR_TEST; + // flushing to param partition + if (!sec_set_param + (param_index_qnad, ¶m_qnad_data)) { + pr_err + ("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + + NAD_PRINT + ("new cmd : nad_remain_count = %d, ddrtest_remain_count = %d\n", + param_qnad_data.nad_remain_count, + param_qnad_data.ddrtest_remain_count); + } + } + return count; + } else + return count; +err_out: + return count; +} + +static DEVICE_ATTR(nad_acat, S_IRUGO | S_IWUSR, show_nad_acat, store_nad_acat); + +static ssize_t show_nad_stat(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nad_result; + + // refer to qpnp_pon_reason (index=boot_reason-1) + NAD_PRINT("%s : boot_reason was %d\n", __func__, boot_reason); + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + NAD_PRINT("%s : magic %x, nad cnt %d, ddr cnt %d, ddr result 0x%2X\n", + __func__, param_qnad_data.magic, + param_qnad_data.nad_remain_count, + param_qnad_data.ddrtest_remain_count, + GET_DDR_TEST_RESULT(SMD_NAD, param_qnad_data.ddrtest_result)); + + if (param_qnad_data.magic != NAD_SMD_MAGIC) { + NAD_PRINT("SMD NAD NOT_TESTED\n"); + return snprintf(buf, BUFF_SZ, "NOT_TESTED\n"); + } else { + nad_result = NadResult(SMD_NAD); + switch (nad_result) { + case TOTALTEST_PASS: { + // there is "AllTestDone" at SMD/test_result.csv + NAD_PRINT("SMD NAD PASS\n"); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } break; + + case TOTALTEST_FAIL: { + // there is "FAIL" at SMD/test_result.csv + char strResult[BUFF_SZ-14] = { '\0', }; + + get_nad_result(strResult, ITEM_CNT); + NAD_PRINT("SMD NAD FAIL, %s", buf); + return snprintf(buf, BUFF_SZ, "NG_2.0_FAIL_%s\n", strResult); + } break; + + case TOTALTEST_NO_RESULT_FILE: { + // there is no SMD/test_result.csv + if (nad_test_mode == SMD_NAD) { + // will be executed soon + NAD_PRINT("SMD NAD TESTING\n"); + return snprintf(buf, BUFF_SZ, "TESTING\n"); + } else { + // not exeuted by unknown reasons + // ex1) magic exists but nad_refer was removed + // ex2) fail to execute qnad.sh + // ex3) fail to make test_result.csv + // ex4) etc... + NAD_PRINT("SMD NAD NO_RESULT_FILE && not started\n"); + return snprintf(buf, BUFF_SZ, "RE_WORK\n"); + } + } break; + + case TOTALTEST_NO_RESULT_STRING: { + if (nad_test_mode == SMD_NAD) { + // will be completed + NAD_PRINT("SMD NAD TESTING\n"); + return snprintf(buf, BUFF_SZ, "TESTING\n"); + } else { + // need to rework + NAD_PRINT("SMD NAD NO_RESULT_STRING && not started\n"); + return snprintf(buf, BUFF_SZ, "RE_WORK\n"); + } + } break; + } + } + +err_out: + NAD_PRINT("SMD NAD UNKNOWN\n"); + return snprintf(buf, BUFF_SZ, "RE_WORK\n"); +} + +static DEVICE_ATTR(nad_stat, S_IRUGO, show_nad_stat, NULL); + +static ssize_t show_ddrtest_remain_count(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + return snprintf(buf, BUFF_SZ, "%d\n", + param_qnad_data.ddrtest_remain_count); +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_ddrtest_remain_count, S_IRUGO, show_ddrtest_remain_count, + NULL); + +static ssize_t show_nad_remain_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + return snprintf(buf, BUFF_SZ, "%d\n", param_qnad_data.nad_remain_count); +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_qmvs_remain_count, 0444, show_nad_remain_count, NULL); + +static ssize_t store_nad_erase(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!strncmp(buf, "erase", 5)) { + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[3] = { NULL, NULL, NULL }; + int ret_userapp; + int api_gpio_test = 0; + char api_gpio_test_result[256] = { 0, }; + + argv[0] = ERASE_NAD_PRG; + + envp[0] = "HOME=/"; + envp[1] = + "PATH=/system/bin/qnad/:/system/bin:/system/xbin"; + ret_userapp = + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + + if (!ret_userapp) { + NAD_PRINT + ("remove_files.sh is executed. ret_userapp = %d\n", + ret_userapp); + erase_pass = 1; + } else { + NAD_PRINT + ("remove_files.sh is NOT executed. ret_userapp = %d\n", + ret_userapp); + erase_pass = 0; + } + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", + __func__); + goto err_out; + } + + param_qnad_data.magic = 0x0; + param_qnad_data.nad_remain_count = 0x0; + param_qnad_data.ddrtest_remain_count = 0x0; + param_qnad_data.ddrtest_result = 0x0; + param_qnad_data.ddrtest_mode = 0x0; + param_qnad_data.total_test_result = 0x0; + + // flushing to param partition + if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + + NAD_PRINT("clearing MAGIC code done = %d\n", + param_qnad_data.magic); + NAD_PRINT("nad_remain_count = %d\n", + param_qnad_data.nad_remain_count); + NAD_PRINT("ddrtest_remain_count = %d\n", + param_qnad_data.ddrtest_remain_count); + NAD_PRINT("ddrtest_result = 0x%8X\n", + param_qnad_data.ddrtest_result); + NAD_PRINT("clearing smd ddr test MAGIC code done = %d\n", + param_qnad_data.magic); + + // clearing API test result + if (!sec_set_param(param_index_api_gpio_test, &api_gpio_test)) { + pr_err("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + + if (!sec_set_param + (param_index_api_gpio_test_result, api_gpio_test_result)) { + pr_err("%s : fail - set param!! param_qnad_data\n", + __func__); + goto err_out; + } + return count; + } else + return count; +err_out: + return count; +} + +static ssize_t show_nad_erase(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (erase_pass) + return snprintf(buf, BUFF_SZ, "OK\n"); + else + return snprintf(buf, BUFF_SZ, "NG\n"); +} + +static DEVICE_ATTR(nad_erase, S_IRUGO | S_IWUSR, show_nad_erase, + store_nad_erase); + +static ssize_t show_nad_dram(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + // The factory app needs only the ddrtest result of ACAT now. + // If the ddrtest result of SMD and MAIN are also needed, + // implement an additional sysfs node or a modification of app. + ret = GET_DDR_TEST_RESULT(ETC_NAD, param_qnad_data.ddrtest_result); + + if (ret == DDRTEST_PASS) + return snprintf(buf, BUFF_SZ, "OK_DRAM\n"); + else if (ret == DDRTEST_FAIL) + return snprintf(buf, BUFF_SZ, "NG_DRAM_DATA\n"); + else + return snprintf(buf, BUFF_SZ, "NO_DRAMTEST\n"); +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_dram, S_IRUGO, show_nad_dram, NULL); + +static ssize_t show_nad_dram_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ +// int ret=0; + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + return snprintf(buf, BUFF_SZ, "0x%x\n", param_qnad_data.ddrtest_result); +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_dram_debug, S_IRUGO, show_nad_dram_debug, NULL); + +static ssize_t show_nad_dram_err_addr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + int i = 0; + struct param_qnad_ddr_result param_qnad_ddr_result_data; + + if (!sec_get_param + (param_index_qnad_ddr_result, ¶m_qnad_ddr_result_data)) { + pr_err("%s : fail - get param!! param_qnad_ddr_result_data\n", + __func__); + goto err_out; + } + + ret = + snprintf(buf, BUFF_SZ, "Total : %d\n\n", + param_qnad_ddr_result_data.ddr_err_addr_total); + for (i = 0; i < param_qnad_ddr_result_data.ddr_err_addr_total; i++) { + ret += + snprintf(buf + ret - 1, BUFF_SZ, "[%d] 0x%llx\n", i, + param_qnad_ddr_result_data.ddr_err_addr[i]); + } + + return ret; +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_dram_err_addr, S_IRUGO, show_nad_dram_err_addr, NULL); + +static ssize_t show_nad_support(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(CONFIG_ARCH_MSM8998) || defined(CONFIG_ARCH_MSM8996) || defined(CONFIG_ARCH_SDM845) + return snprintf(buf, BUFF_SZ, "SUPPORT\n"); +#else + return snprintf(buf, BUFF_SZ, "NOT_SUPPORT\n"); +#endif +} + +static DEVICE_ATTR(nad_support, S_IRUGO, show_nad_support, NULL); + +static ssize_t store_nad_logs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int fd = 0, i = 0; + char logbuf[500] = { '\0' }; + char path[100] = { '\0' }; + char ptr; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + + NAD_PRINT("%s\n", buf); + + sscanf(buf, "%s", path); + fd = sys_open(path, O_RDONLY, 0); + + if (fd >= 0) { + while (sys_read(fd, &ptr, 1) && ptr != -1) { + //NAD_PRINT("%c\n", ptr); + logbuf[i] = ptr; + i++; + if (ptr == '\n') { + NAD_PRINT("%s", logbuf); + i = 0; + } + } + + sys_close(fd); + } else { + NAD_PRINT("The File is not existed. %s\n", __func__); + } + + set_fs(old_fs); + return count; +} + +static DEVICE_ATTR(nad_logs, 0200, NULL, store_nad_logs); + +static ssize_t store_nad_end(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char result[20] = { '\0' }; + + NAD_PRINT("result : %s\n", buf); + + sscanf(buf, "%s", result); + + if (!strcmp(result, "nad_pass")) { + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, nad_uevent.envp); + NAD_PRINT + ("NAD result : %s, nad_pass, Send to Process App for Nad test end : %s\n", + result, __func__); + } else { + if (nad_test_mode == MAIN_NAD || nad_test_mode == ETC_NAD) { + NAD_PRINT + ("NAD result : %s, Device enter the upload mode because it is ETC_NAD : %s\n", + result, __func__); + panic(result); + } else { + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, + nad_uevent.envp); + NAD_PRINT + ("NAD result : %s, Send to Process App for Nad test end : %s\n", + result, __func__); + } + } + + nad_test_mode = -1; + return count; +} + +static DEVICE_ATTR(nad_end, 0200, NULL, store_nad_end); + +static ssize_t show_nad_api(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int api_gpio_test; + char api_gpio_test_result[256]; + + if (!sec_get_param(param_index_api_gpio_test, &api_gpio_test)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + if (api_gpio_test) { + if (!sec_get_param + (param_index_api_gpio_test_result, api_gpio_test_result)) { + pr_err("%s : fail - get param!! param_qnad_data\n", + __func__); + goto err_out; + } + return snprintf(buf, BUFF_SZ, "%s", api_gpio_test_result); + } else + return snprintf(buf, BUFF_SZ, "NONE\n"); + +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_api, 0444, show_nad_api, NULL); + +static ssize_t store_nad_result(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int _result = -1; + char test_name[NAD_BUFF_SIZE * 2] = { '\0', }; + char temp[NAD_BUFF_SIZE * 3] = { '\0', }; + char nad_test[2][NAD_BUFF_SIZE * 2]; // 2: "test_name", "result" + char result_string[NAD_BUFF_SIZE] = { '\0', }; + char *nad_ptr, *string; + int smd = curr_smd; + int item = -1; + + if (curr_smd != SMD_NAD) { + NAD_PRINT("store nad_result only at smd_nad\n"); + return -EIO; + } + + NAD_PRINT("buf : %s count : %d\n", buf, (int)count); + + if (NAD_BUFF_SIZE * 3 < (int)count || (int)count < 4) { + NAD_PRINT("result cmd size too long : NAD_BUFF_SIZE<%d\n", + (int)count); + return -EINVAL; + } + + /* Copy buf to nad temp */ + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + + nad_ptr = strsep(&string, ","); + strlcpy(nad_test[0], nad_ptr, NAD_BUFF_SIZE * 2); + nad_ptr = strsep(&string, ","); + strlcpy(nad_test[1], nad_ptr, NAD_BUFF_SIZE * 2); + + sscanf(nad_test[0], "%s", test_name); + sscanf(nad_test[1], "%s", result_string); + + NAD_PRINT("test_name : %s, test result=%s\n", test_name, result_string); + + if (TEST_PASS(result_string)) + _result = 2; + else if (TEST_FAIL(result_string)) + _result = 1; + else + _result = 0; + + // _results = 0(N/A), 1(FAIL), 2(PASS) from QNAD + + if (TEST_CRYPTO(test_name)) + item = CRYPTO; + else if (TEST_ICACHE(test_name)) + item = ICACHE; + else if (TEST_CCOHERENCY(test_name)) + item = CCOHERENCY; + else if (TEST_SUSPEND(test_name)) + item = SUSPEND; + else if (TEST_VDDMIN(test_name)) + item = VDDMIN; + else if (TEST_QMESADDR(test_name)) + item = QMESADDR; + else if (TEST_QMESACACHE(test_name)) + item = QMESACACHE; + else if (TEST_UFS(test_name)) + item = UFS; + else if (TEST_DDR(test_name)) + item = DDR; + else if (TEST_SENSOR(test_name)) + item = SENSOR; + else + item = NOT_ASSIGN; + + if (item == NOT_ASSIGN) { + pr_err("%s : fail - get test item in QNAD!! \n", __func__); + return count; + } + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + return -EINVAL; + } + + param_qnad_data.total_test_result |= + TEST_ITEM_RESULT(smd, item, _result); + + NAD_PRINT("total_test_result=%u, smd=%d, item=%d, _result=%d\n", + param_qnad_data.total_test_result, smd, item, _result); + + if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - set param!! param_qnad_data\n", __func__); + return -EINVAL; + } + + return count; +} + +static ssize_t show_nad_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t info_size = 0; + int iCnt; + + for (iCnt = 0; iCnt < ITEM_CNT; iCnt++) { + char strResult[NAD_BUFF_SIZE] = { '\0', }; + + if (!get_nad_result(strResult, iCnt)) + goto err_out; + + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"%s\":\"%s\",", STR_TEST_ITEM[iCnt], strResult); + } + + pr_info("%s, result=%s\n", __func__, buf); + + return info_size; +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_result, S_IRUGO | S_IWUSR, show_nad_result, + store_nad_result); + +static ssize_t store_nad_info(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char info_name[NAD_BUFF_SIZE * 2] = { '\0', }; + char temp[NAD_BUFF_SIZE * 3] = { '\0', }; + char nad_test[2][NAD_BUFF_SIZE * 2]; // 2: "info_name", "result" + int resultValue; + char *nad_ptr, *string; + + NAD_PRINT("buf : %s count : %d\n", buf, (int)count); + + if (NAD_BUFF_SIZE * 3 < (int)count || (int)count < 4) { + NAD_PRINT("result cmd size too long : NAD_BUFF_SIZE<%d\n", + (int)count); + return -EINVAL; + } + + /* Copy buf to nad temp */ + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + + nad_ptr = strsep(&string, ","); + strlcpy(nad_test[0], nad_ptr, NAD_BUFF_SIZE * 2); + nad_ptr = strsep(&string, ","); + strlcpy(nad_test[1], nad_ptr, NAD_BUFF_SIZE * 2); + + sscanf(nad_test[0], "%s", info_name); + sscanf(nad_test[1], "%d", &resultValue); + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + return -EINVAL; + } + + if (!strcmp("thermal", info_name)) + param_qnad_data.thermal = resultValue; + else if (!strcmp("clock", info_name)) + param_qnad_data.tested_clock = resultValue; + + NAD_PRINT("info_name : %s, result=%d\n", info_name, resultValue); + + if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - set param!! param_qnad_data\n", __func__); + return -EINVAL; + } + + return count; +} + +static ssize_t show_nad_info(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t info_size = 0; + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + goto err_out; + } + + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"REMAIN_CNT\":\"%d\",", + param_qnad_data.nad_remain_count); + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"THERMAL\":\"%d\",", param_qnad_data.thermal); + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"CLOCK\":\"%d\",", param_qnad_data.tested_clock); + + return info_size; +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_info, S_IRUGO | S_IWUSR, show_nad_info, store_nad_info); + +static ssize_t show_nad_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + #if 0 + //QNAD_2.0.0_SS_09012017 - Trial version + return snprintf(buf, BUFF_SZ, "S845.0201.01.TR\n"); + + //QNAD_2.0.0_SS_10152017 - Offical version + return snprintf(buf, BUFF_SZ, "S845.0202.01.OF\n"); + + //QNAD_2.0.0_SS_11152017 - S/R, Storage added + return snprintf(buf, BUFF_SZ, "S845.0203.01.SRSTO\n"); + + //QNAD_2.0.0_SS_12152017 - QMESA Version up + return snprintf(buf, BUFF_SZ, "S845.0204.01.QM\n"); + + //QNAD_2.0.0_SS_12152017 - HMAC, AES parallel Disable + return snprintf(buf, BUFF_SZ, "S845.0204.02.HAESd\n"); + + //QNAD_2.0.0_SS_12152017 - HOT PLUG Enable + return snprintf(buf, BUFF_SZ, "S845.0204.03.HOTe\n"); + + //QNAD_2.0.0_SS_12152017 - SLPI Enable + return snprintf(buf, BUFF_SZ, "S845.0204.04.SLPIe\n"); + + //QNAD_2.0.1_SS_SLT_06142018_NS_GCM - CRYPTO_AES_GCM_ENABLE + return snprintf(buf, BUFF_SZ, "S845.0205.04.AESGCM\n"); + #else + //QNAD_2.0.1_SS_SLT_06142018_NS_GCM - GFX_ENABLE_at_MAIN + return snprintf(buf, BUFF_SZ, "S845.0205.05.GFXMAIN\n"); + #endif +} + +static DEVICE_ATTR(nad_version, 0444, show_nad_version, NULL); + +static ssize_t store_nad_main(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0; + int ret = -1; + char temp[NAD_BUFF_SIZE * 3]; + char nad_cmd[NAD_MAIN_CMD_LIST][NAD_BUFF_SIZE]; + char *nad_ptr, *string; + int running_time; + + /* Copy buf to nad temp */ + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + + while (idx < NAD_MAIN_CMD_LIST) { + nad_ptr = strsep(&string, ","); + strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE); + } + + if (nad_test_mode == MAIN_NAD) { + NAD_PRINT("duplicated!\n"); + return count; + } + + if (!strncmp(buf, "start", 5)) { + ret = sscanf(nad_cmd[1], "%d\n", &running_time); + if (ret != 1) + return -EINVAL; + + main_reboot = 1; + + nad_test_mode = MAIN_NAD; + + if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - get param!! param_qnad_data\n", __func__); + return -1; + } + + param_qnad_data.ddrtest_mode = UPLOAD_CAUSE_DDR_TEST_FOR_MAIN; + param_qnad_data.ddrtest_remain_count = 1; + param_qnad_data.nad_remain_count = 0; + param_qnad_data.total_test_result &= 0xffffffff; + + if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) { + pr_err("%s : fail - set param!! param_qnad_data\n", __func__); + return -1; + } + + do_nad(MAIN_NAD); + } + + return count; +} + +static ssize_t show_nad_main(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nad_result; + + nad_result = NadResult(MAIN_NAD); + switch (nad_result) { + case TOTALTEST_PASS: { + NAD_PRINT("MAIN NAD Passed\n"); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } break; + + case TOTALTEST_FAIL: { + NAD_PRINT("MAIN NAD fail\n"); + return snprintf(buf, BUFF_SZ, "NG_2.0_FAIL\n"); + } break; + + default: { + NAD_PRINT("MAIN NAD No Run\n"); + return snprintf(buf, BUFF_SZ, "OK\n"); + } + } + + return snprintf(buf, BUFF_SZ, "MAIN NAD UNKNOWN\n"); +} + +static DEVICE_ATTR(balancer, S_IRUGO | S_IWUSR, show_nad_main, store_nad_main); + +static ssize_t show_nad_main_timeout(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, BUFF_SZ, "%d\n", NAD_MAIN_TIMEOUT); +} + +static DEVICE_ATTR(timeout, 0444, show_nad_main_timeout, NULL); + + +enum nad_qdaf_action_t { + NAD_QDAF_ACTION_EMPTY = 0, + + /*==== from AtNadCheck.java to qdaf.sh ====*/ + NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC = 1, + NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC = 2, + NAD_QDAF_ACTION_CONTROL_STOP = 3, + /*==== from qnad.sh to qdaf.sh ====*/ + NAD_QDAF_ACTION_CONTROL_STOP_WATING_FOR_QNAD = 4, + + /*==== from AtNadCheck.java to qdaf.sh ====*/ + NAD_QDAF_ACTION_RESULT_ERASE = 5, + NAD_QDAF_ACTION_RESULT_GET = 6, + + /*==== from qdaf.sh ====*/ + NAD_QDAF_ACTION_DEBUG_SAVE_LOGS = 7, + NAD_QDAF_ACTION_DEBUG_TRIGGER_PANIC = 8, +}; + +enum nad_qdaf_result_string_t { + NAD_QDAF_RESULT_OK = 0, + NAD_QDAF_RESULT_NG = 1, + NAD_QDAF_RESULT_NONE = 2, +}; + +#define QDAF_PROG "/system/bin/qnad/qdaf.sh" +#define QDAF_QMESA_LOG "/data/log/qdaf_qmesa_log.txt" + +static int qdaf_cur_cmd_mode; + +static ssize_t show_nad_qdaf_control(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if( qdaf_cur_cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC || + qdaf_cur_cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC ) + return snprintf(buf, BUFF_SZ, "RUNNING\n"); + + return snprintf(buf, BUFF_SZ, "READY\n"); +} +static ssize_t store_nad_qdaf_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0, cmd_mode=0, wait_val=0; + char temp[NAD_BUFF_SIZE * 3]; + char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE]; + char *nad_ptr, *string; + int ret_userapp; + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/qnad:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + + NAD_PRINT("%s : is called\n",__func__); + + if ((int)count < NAD_BUFF_SIZE) { + NAD_PRINT("%s : return error (count=%d)\n", __func__, (int)count); + return -EINVAL;; + } + + if (strncmp(buf, "nad_qdaf_control", 16)) { + NAD_PRINT("%s : return error (buf=%s)\n", __func__, buf); + return -EINVAL;; + } + + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + while (idx < NAD_CMD_LIST) { + nad_ptr = strsep(&string, ","); + strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE); + } + sscanf(nad_cmd[1], "%d", &cmd_mode); + + // let's just return if receiving the same command as before one + if ( qdaf_cur_cmd_mode==cmd_mode ) { + if( cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC || + cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC) { + NAD_PRINT("%s : return because qdaf.sh already has been running.\n", __func__); + }else if( cmd_mode==NAD_QDAF_ACTION_CONTROL_STOP || + cmd_mode==NAD_QDAF_ACTION_CONTROL_STOP_WATING_FOR_QNAD) { + NAD_PRINT("%s : return because qdaf.sh has not been running yet.\n", __func__); + } + return count; + } + + // if receiving control command from AtNadCheck when qnad is running, let's return NG + // if receiving control command from qnad.sh, invoke qdaf.sh to stop qdaf with UMH_WAIT_PROC + if( nad_test_mode!=-1 && + (cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC || + cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC || + cmd_mode==NAD_QDAF_ACTION_CONTROL_STOP) ) { + NAD_PRINT("%s : return because qnad is running.\n", __func__); + return count; + } + + argv[0] = QDAF_PROG; + argv[1] = nad_cmd[1]; + switch (cmd_mode) { + case NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC : + NAD_PRINT("%s : qdaf will be started (without panic)\n",__func__); + wait_val = UMH_WAIT_EXEC; + break; + case NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC: + NAD_PRINT("%s : qdaf will be started (with panic)\n",__func__); + wait_val = UMH_WAIT_EXEC; + break; + case NAD_QDAF_ACTION_CONTROL_STOP : + NAD_PRINT("%s : qdaf will be stopped\n",__func__); + wait_val = UMH_WAIT_EXEC; + break; + case NAD_QDAF_ACTION_CONTROL_STOP_WATING_FOR_QNAD : + NAD_PRINT("%s : qdaf will be stopped (waiting)\n",__func__); + // should wait for completion to gurantee not working of qdaf + wait_val = UMH_WAIT_PROC; + break; + default : + NAD_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode); + return count; + } + + ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val); + if (!ret_userapp) { + qdaf_cur_cmd_mode = cmd_mode; + NAD_PRINT("%s : succeded to trigger qdaf.sh\n", __func__); + NAD_PRINT("%s : qdaf_cur_cmd_mode=%s\n", __func__, nad_cmd[1]); + }else { + qdaf_cur_cmd_mode = NAD_QDAF_ACTION_EMPTY; + // evaulate return value after ret_userapp>>8 + NAD_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp); + } + return count; +} +static DEVICE_ATTR(nad_qdaf_control, S_IRUGO | S_IWUSR, show_nad_qdaf_control, store_nad_qdaf_control); + +/* +static int qdaf_check_prev_result(void) +{ + int fd = 0; + int ret; + char buf[512] = { '\0', }; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + + fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0); + if (fd >= 0) { + int found = 0; + + printk(KERN_DEBUG); + + while (sys_read(fd, buf, 512)) { + char *ptr; + char *div = "\n"; + char *tok = NULL; + + ptr = buf; + while ((tok = strsep(&ptr, div)) != NULL) { + if ((strstr(tok, "failure"))) { + ret = NAD_QDAF_TEST_RESULT_NG; + found = 1; + break; + } + } + + if (found) break; + } + + if (!found) ret = NAD_QDAF_TEST_RESULT_OK; + sys_close(fd); + } else { + NAD_PRINT("%s : result file is not existed\n", __func__); + ret = NAD_QDAF_TEST_RESULT_NONE; + } + + set_fs(old_fs); + return ret; +} + +static ssize_t show_nad_qdaf_prev_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int qdaf_result = qdaf_check_prev_result(); + switch (qdaf_result) { + case NAD_QDAF_TEST_RESULT_OK: + NAD_PRINT("%s : previous test result : pass\n", __func__); + return snprintf(buf, BUFF_SZ, "OK\n"); + break; + case NAD_QDAF_TEST_RESULT_NG: + NAD_PRINT("%s : previous test result : fail\n", __func__); + return snprintf(buf, BUFF_SZ, "NG\n"); + break; + default: + NAD_PRINT("%s : previous test result : unknown\n", __func__); + return snprintf(buf, BUFF_SZ, "NONE\n"); + } + + return snprintf(buf, BUFF_SZ, "NONE\n"); + +} +*/ + +static ssize_t show_nad_qdaf_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret_userapp, wait_val=0, failed_cnt=0; + char act[NAD_BUFF_SIZE]={0,}; + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/qnad:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + + NAD_PRINT("%s : is called\n",__func__); + + // refer to qpnp_pon_reason (index=boot_reason-1) + NAD_PRINT("%s : boot_reason was %d\n", __func__, boot_reason); + + snprintf(act, 1, "%d", NAD_QDAF_ACTION_RESULT_GET); + + argv[0] = QDAF_PROG; + // TODO : conversion int->string + argv[1] = "6"; //NAD_QDAF_ACTION_RESULT_GET + wait_val = UMH_WAIT_PROC; + ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val); + if (!ret_userapp) { + NAD_PRINT("%s : succeded to trigger qdaf.sh and failed_cnt=0\n", __func__); + return snprintf(buf, BUFF_SZ, "OK\n"); + }else if (ret_userapp > 0 ) { + // evaulate return value after ret_userapp>>8 + // qdaf.sh exit with failed_cnt if persist.qdaf.failed_cnt>0, so let's use return value from call_usermodehelper + failed_cnt = ret_userapp>>8; + NAD_PRINT("%s : succeded to trigger qdaf.sh and return_value=%d(failed_cnt=%d)\n", + __func__, ret_userapp, failed_cnt); + return snprintf(buf, BUFF_SZ, "NG,%d\n", failed_cnt); + }else { + // evaulate return value after ret_userapp>>8 + NAD_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp); + return snprintf(buf, BUFF_SZ, "NONE\n"); + } + + return snprintf(buf, BUFF_SZ, "NONE\n"); +} + +static ssize_t store_nad_qdaf_result(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0, cmd_mode=0, wait_val=0; + int ret_userapp; + char temp[NAD_BUFF_SIZE * 3]; + char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE]; + char *nad_ptr, *string; + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/qnad:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + + NAD_PRINT("%s : is called\n",__func__); + + if ((int)count < NAD_BUFF_SIZE) { + NAD_PRINT("%s : return error (count=%d)\n", __func__, (int)count); + return -EINVAL;; + } + + if (strncmp(buf, "nad_qdaf_result", 15)) { + NAD_PRINT("%s : return error (buf=%s)\n", __func__, buf); + return -EINVAL;; + } + + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + while (idx < NAD_CMD_LIST) { + nad_ptr = strsep(&string, ","); + strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE); + } + sscanf(nad_cmd[1], "%d", &cmd_mode); + + argv[0] = QDAF_PROG; + argv[1] = nad_cmd[1]; + switch (cmd_mode) { + case NAD_QDAF_ACTION_RESULT_ERASE : + NAD_PRINT("%s : qdaf will erase failed count\n",__func__); + wait_val = UMH_WAIT_PROC; + break; + default : + NAD_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode); + return count; + } + + ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val); + if (!ret_userapp) + NAD_PRINT("%s : succeded to trigger qdaf.sh\n", __func__); + else { + // evaulate return value after ret_userapp>>8 + NAD_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp); + } + return count; +} + +static DEVICE_ATTR(nad_qdaf_result, S_IRUGO | S_IWUSR, show_nad_qdaf_result, store_nad_qdaf_result); + +static void qdaf_save_logs(void) +{ + int fd, idx=0; + char temp[1]={'\0'}, buf[BUFF_SZ]={'\0',}; + + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0); + if (fd >= 0) { + while (sys_read(fd, temp, 1) == 1) { + buf[idx++] = temp[0]; + if( temp[0]=='\n' ) { + buf[idx] = '\0'; + NAD_PRINT("%s", buf); + idx = 0; + } + } + sys_close(fd); + } + set_fs(old_fs); +} + +static ssize_t store_nad_qdaf_debug(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0, cmd_mode=0; + char temp[NAD_BUFF_SIZE * 3]; + char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE]; + char *nad_ptr, *string; + + NAD_PRINT("%s : is called\n",__func__); + + if ((int)count < NAD_BUFF_SIZE) { + NAD_PRINT("%s : return error (count=%d)\n", __func__, (int)count); + return -EINVAL;; + } + + if (strncmp(buf, "nad_qdaf_debug", 14)) { + NAD_PRINT("%s : return error (buf=%s)\n", __func__, buf); + return -EINVAL;; + } + + strlcpy(temp, buf, NAD_BUFF_SIZE * 3); + string = temp; + while (idx < NAD_CMD_LIST) { + nad_ptr = strsep(&string, ","); + strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE); + } + sscanf(nad_cmd[1], "%d", &cmd_mode); + + switch (cmd_mode) { + case NAD_QDAF_ACTION_DEBUG_SAVE_LOGS : + NAD_PRINT("%s : qdaf will save log into kmsg\n",__func__); + qdaf_save_logs(); + return count; + case NAD_QDAF_ACTION_DEBUG_TRIGGER_PANIC : + NAD_PRINT("%s : will trigger panic\n",__func__); + panic("qdaf_fail"); + return count; + } + + return count; +} +static DEVICE_ATTR(nad_qdaf_debug, S_IWUSR, NULL, store_nad_qdaf_debug); + + + +static int __init sec_nad_init(void) +{ + int ret = 0; + struct device *sec_nad; + struct device *sec_nad_balancer; + + NAD_PRINT("%s\n", __func__); + + /* Skip nad init when device goes to lp charging */ + if (lpcharge) + return ret; + + sec_nad = sec_device_create(0, NULL, "sec_nad"); + + if (IS_ERR(sec_nad)) { + pr_err("%s Failed to create device(sec_nad)!\n", __func__); + return PTR_ERR(sec_nad); + } + + ret = device_create_file(sec_nad, &dev_attr_nad_stat); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_ddrtest_remain_count); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qmvs_remain_count); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_erase); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_acat); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_dram); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_support); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_logs); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_end); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_dram_debug); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_dram_err_addr); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_result); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_api); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_info); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_version); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_control); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_debug); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_result); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + if (add_uevent_var(&nad_uevent, "NAD_TEST=%s", "DONE")) { + pr_err("%s : uevent NAD_TEST_AND_PASS is failed to add\n", + __func__); + goto err_create_nad_sysfs; + } + + sec_nad_balancer = sec_device_create(0, NULL, "sec_nad_balancer"); + + if (IS_ERR(sec_nad)) { + pr_err("%s Failed to create device(sec_nad)!\n", __func__); + return PTR_ERR(sec_nad); + } + + ret = device_create_file(sec_nad_balancer, &dev_attr_balancer); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad_balancer, &dev_attr_timeout); + if (ret) { + pr_err("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + return 0; +err_create_nad_sysfs: + return ret; +} + +module_init(sec_nad_init); diff --git a/drivers/debug/sec_quest.c b/drivers/debug/sec_quest.c new file mode 100644 index 000000000000..23be1e96d9a7 --- /dev/null +++ b/drivers/debug/sec_quest.c @@ -0,0 +1,2457 @@ +/* + * drivers/debug/sec_quest.c + * + * COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern unsigned int lpcharge; + +static struct param_quest_t param_quest_data; +static struct kobj_uevent_env quest_uevent; +static int erase_pass; +static int quest_step = -1; /* flag for quest test mode */ +struct mutex uefi_parse_lock; +struct mutex hlos_parse_lock; +struct mutex common_lock; +static int qdaf_cur_cmd_mode; /* this value need to be defined here for lower version kernel build_error*/ + + +static void print_param_quest_data(void) +{ +#if defined(CONFIG_SEC_QUEST_UEFI) + QUEST_PRINT( + "%s : magic %x, hlos cnt %d, uefi cnt %d, ddr cnt %d, smd_test_result 0x%2X, quest_uefi_result 0x%2X, ddr result 0x%2X, ddr_test_mode 0x%3X, quest_step %d\n", + __func__, param_quest_data.magic, + param_quest_data.quest_hlos_remain_count, + param_quest_data.quest_uefi_remain_count, + param_quest_data.ddrtest_remain_count, + param_quest_data.smd_test_result, + param_quest_data.quest_uefi_result, + param_quest_data.ddrtest_result, + param_quest_data.ddrtest_mode, + param_quest_data.quest_step); +#else + QUEST_PRINT( + "%s : magic %x, hlos cnt %d, ddr cnt %d, smd_test_result 0x%2X, ddr result 0x%2X, ddr_test_mode 0x%3X, quest_step %d\n", + __func__, param_quest_data.magic, + param_quest_data.quest_hlos_remain_count, + param_quest_data.ddrtest_remain_count, + param_quest_data.smd_test_result, + param_quest_data.ddrtest_result, + param_quest_data.ddrtest_mode, + param_quest_data.quest_step); +#endif +} + + +// get the result of ddr_scan at smd, cal or main +static int get_ddr_scan_result(int which_quest) +{ + int ddr_result = DDRTEST_INCOMPLETED; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + return ddr_result; + } + + ddr_result = GET_DDR_TEST_RESULT(which_quest, param_quest_data.ddrtest_result); + QUEST_PRINT("%s : (step=%d) (ddr_result=%d)\n", __func__, which_quest, ddr_result); + + return ddr_result; +} + + +// update bitmap_item_result +void set_ddr_scan_result_for_smd(void) +{ + int ddr_result = DDRTEST_INCOMPLETED; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + return; + } + + ddr_result = get_ddr_scan_result(SMD_QUEST); + + if (ddr_result == DDRTEST_PASS) { + param_quest_data.bitmap_item_result |= + TEST_ITEM_RESULT(SMD_QUEST, DDR_SCAN, QUEST_ITEM_TEST_PASS); + } else if ( ddr_result == DDRTEST_FAIL ) { + param_quest_data.bitmap_item_result |= + TEST_ITEM_RESULT(SMD_QUEST, DDR_SCAN, QUEST_ITEM_TEST_FAIL); + } + + if (!sec_set_param(param_index_quest,¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + } +} + + +// check test_result.csv +static int get_quest_hlos_result(int which_quest) +{ + int fd = 0; + int hlos_result = TOTALTEST_UNKNOWN; + char buf[512] = { '\0', }; + mm_segment_t old_fs = get_fs(); + + mutex_lock(&hlos_parse_lock); + + set_fs(KERNEL_DS); + + switch (which_quest) { + case SMD_QUEST: + fd = sys_open(SMD_QUEST_RESULT, O_RDONLY, 0); + break; + case CAL_QUEST: + fd = sys_open(CAL_QUEST_RESULT, O_RDONLY, 0); + break; + case MAIN_QUEST: + fd = sys_open(MAIN_QUEST_RESULT, O_RDONLY, 0); + break; + default: + break; + } + + if (fd >= 0) { + int found = 0; + + printk(KERN_DEBUG); + + while (sys_read(fd, buf, 512)) { + char *ptr; + char *div = "\n"; + char *tok = NULL; + + ptr = buf; + while ((tok = strsep(&ptr, div)) != NULL) { + + if ((strstr(tok, "FAIL"))) { + QUEST_PRINT("%s : (step=%d) The result is FAIL\n",__func__, which_quest); + hlos_result = TOTALTEST_FAIL; + found = 1; + break; + } + + if ((strstr(tok, "AllTestDone"))) { + QUEST_PRINT("%s : (step=%d) The result is PASS\n",__func__, which_quest); + hlos_result = TOTALTEST_PASS; + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) { + QUEST_PRINT("%s : (step=%d) no result string\n",__func__, which_quest); + hlos_result = TOTALTEST_NO_RESULT_STRING; + } + + sys_close(fd); + } else { + QUEST_PRINT("%s : (step=%d) The result file is not existed (fd=%d)\n",__func__, which_quest, fd); + hlos_result = TOTALTEST_NO_RESULT_FILE; + } + + set_fs(old_fs); + + mutex_unlock(&hlos_parse_lock); + + return hlos_result; +} + + +// check bitmap_item_result +static int get_smd_item_result(char *buf, int piece) +{ + int iCnt; + unsigned int smd_quest_result; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + if( param_quest_data.bitmap_item_result == 0 ) { + QUEST_PRINT("%s : param_quest_data.bitmap_item_result = 0\n", __func__); + goto err_out; + } + + smd_quest_result = param_quest_data.bitmap_item_result; + + QUEST_PRINT("%s : param_quest_data.bitmap_item_result=%u,", __func__, + smd_quest_result); + + if (piece == ITEM_CNT) { + for (iCnt = 0; iCnt < ITEM_CNT; iCnt++) { + switch (smd_quest_result & 0x3) { + case 0: + strcat(buf, "[X]"); + break; + case 1: + strcat(buf, "[F]"); + break; + case 2: + strcat(buf, "[P]"); + break; + } + + smd_quest_result >>= 2; + } + } else { + smd_quest_result >>= 2 * piece; + switch (smd_quest_result & 0x3) { + case 0: + strlcpy(buf, "NA", sizeof(buf)); + break; + case 1: + strlcpy(buf, "FAIL", sizeof(buf)); + break; + case 2: + strlcpy(buf, "PASS", sizeof(buf)); + break; + } + } + + return ITEM_CNT; +err_out: + return 0; +} + + +static void do_quest(void) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + int ret_userapp; + char log_path[50] = { '\0', }; + + switch (quest_step) { + case SMD_QUEST: + argv[0] = SMD_QUEST_PROG; + snprintf(log_path, 50, "logPath:%s%c", SMD_QUEST_LOGPATH, '\0'); + argv[1] = log_path; + QUEST_PRINT("SMD_QUEST, quest_step : %d", quest_step); + break; + case CAL_QUEST: + argv[0] = CAL_QUEST_PROG; + snprintf(log_path, 50, "logPath:%s%c", CAL_QUEST_LOGPATH, '\0'); + argv[1] = log_path; + argv[2] = "Reboot"; + QUEST_PRINT("CAL_QUEST_HLOS, quest_step : %d", quest_step); + QUEST_PRINT("reboot option enabled \n"); + break; + case MAIN_QUEST: + argv[0] = MAIN_QUEST_PROG; + snprintf(log_path, 50, "logPath:%s%c", MAIN_QUEST_LOGPATH, '\0'); + argv[1] = log_path; + argv[2] = "Reboot"; + QUEST_PRINT("MAIN_QUEST, quest_step : %d\n", quest_step); + QUEST_PRINT("reboot option enabled \n"); + break; + default: + QUEST_PRINT("Invalid quest value, quest_step : %d\n", quest_step); + return; + } + + ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + + if (!ret_userapp) { + QUEST_PRINT("%s is executed. ret_userapp = %d\n", argv[0], + ret_userapp); + + if (erase_pass) erase_pass = 0; + } else { + QUEST_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], + ret_userapp); + quest_step = -1; + } +} + + +static void remove_specific_logs(char* remove_log_path) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + int ret_userapp; + + QUEST_PRINT("%s : will delete log files at %s\n", __func__, remove_log_path); + + argv[0] = ERASE_QUEST_PRG; + argv[1] = remove_log_path; + ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + if (!ret_userapp) { + QUEST_PRINT("%s is executed. ret_userapp = %d\n", argv[0], ret_userapp); + } else { + QUEST_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], ret_userapp); + } +} + + +static void make_debugging_logs(char* options) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + int ret_userapp; + + QUEST_PRINT("%s : will make debugging logs with options %s\n", __func__, options); + + argv[0] = QUEST_DEBUGGING_PRG; + argv[1] = options; + ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + if (!ret_userapp) { + QUEST_PRINT("%s is executed. ret_userapp = %d\n", argv[0], ret_userapp); + } else { + QUEST_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], ret_userapp); + } +} + + + +#if defined(CONFIG_SEC_QUEST_UEFI) + +static void arrange_quest_logs(char* log_path) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + int ret_userapp; + + QUEST_PRINT("%s : will arrange quest log files at (%s) before test\n", + __func__, log_path); + + argv[0] = ARRANGE_QUEST_LOGS_PRG; + argv[1] = log_path; + ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + if (!ret_userapp) { + QUEST_PRINT("%s is executed. ret_userapp = %d\n", argv[0], ret_userapp); + } else { + QUEST_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], ret_userapp); + } +} + + +static void move_questresult_to_sub_dir(int quest_step) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + int ret_userapp; + + argv[0] = MOVE_QUESTRESULT_PRG; + switch (quest_step) { + //case SMD_QUEST: { + // argv[1] = SMD_QUEST_LOGPATH; + //} break; + case CAL_QUEST: { + argv[1] = CAL_QUEST_LOGPATH; + } break; + case MAIN_QUEST: { + argv[1] = MAIN_QUEST_LOGPATH; + } break; + } + QUEST_PRINT("%s : will move questresult.txt to %s\n", __func__, argv[1]); + + ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + if (!ret_userapp) { + QUEST_PRINT("%s is executed. ret_userapp = %d\n", argv[0], ret_userapp); + } else { + QUEST_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], ret_userapp); + } +} + + +static int get_quest_uefi_result(char* uefi_log_path, int will_print) +{ + int i, fd = 0; + int uefi_result = TOTALTEST_UNKNOWN; + char buf[512] = { '\0', }; + mm_segment_t old_fs = get_fs(); + int test_done = 0; + int check_strings = sizeof(parsing_info)/sizeof(parsing_info[0]); + char questresult_file[50] = { '\0', }; + + mutex_lock(&uefi_parse_lock); + + set_fs(KERNEL_DS); + + snprintf(questresult_file, 50, "%s/%s%c", uefi_log_path, "questresult.txt", '\0'); + QUEST_PRINT("%s : will parse %s\n",__func__, questresult_file); + + fd = sys_open(questresult_file, O_RDONLY, 0); + + if (fd < 0) { + QUEST_PRINT("%s : The result file of quest_uefi is not existed (fd=%d)\n", __func__, fd); + uefi_result = TOTALTEST_NO_RESULT_FILE; + goto err_out; + } + + for(i=0; i %s\n", + // __func__, parsing_info[i].item_name, PASS_STR); + parsing_info[i].result = QUEST_ITEM_TEST_PASS; + } else if( strstr(tok, FAIL_STR) ) { + //QUEST_PRINT("%s : %s result => %s\n", + // __func__, parsing_info[i].item_name, FAIL_STR); + parsing_info[i].result = QUEST_ITEM_TEST_FAIL; + } else if( i == check_strings-1 ) { // for checking test done + //QUEST_PRINT("%s : test done\n", __func__); + test_done = 1; + } + + break; + } + + } + if( test_done ) break; + } + } + + if( !test_done ) { + QUEST_PRINT("%s : There is no \"QUEST Test Done!\"\n", __func__); + uefi_result = TOTALTEST_NO_RESULT_STRING; + } else { + for(i=0; i=0 ) { + + // if uefi logs which was not parsed exist, backup previous logs first and move current uefi logs to cal directory + QUEST_PRINT("%s : cal_step and uefi logs exists, so backup and move uefi logs\n", __func__); + + // if hlos has been already failed, do not backup cal logs because repeating hlos&uefi will be ended soon + if( get_quest_hlos_result(CAL_QUEST) != TOTALTEST_FAIL ) + arrange_quest_logs(CAL_QUEST_LOGPATH); + move_questresult_to_sub_dir(CAL_QUEST); + can_trigger_panic = 1; + + }else + QUEST_PRINT("%s : no questresult.txt", __func__); + //else { + // the reasons are : + // normal status + // 1. quest_uefi was not triggered + // 2. questresult has already been moved to /data/log/quest/CAL + // abnormal status + // 1. unknown exception happened during quest_uefi + // 2. mount fail or sys_open fail + // 3. unknown + //QUEST_PRINT("%s : questresult logs does not exist (fd=%d)\n", __func__, fd); + //} + }else + QUEST_PRINT("%s : now step is not cal quest", __func__); + set_fs(old_fs); +#endif + + + // 2. parsing the result of quest_uefi +#if defined(CONFIG_SEC_QUEST_UEFI) + uefi_result = get_quest_uefi_result(CAL_QUEST_LOGPATH, 1); + hlos_result = get_quest_hlos_result(CAL_QUEST); + + // In case of cal quest, we can run only quest_uefi or both quest_uefi and quest_hlos. + // At factory line, we run only quest_uefi using cal 1time button, + // so let's consider the result of quest_uefi only except for the case of quest_hlos failure. + if( uefi_result==TOTALTEST_FAIL || hlos_result==TOTALTEST_FAIL ) + total_result = TOTALTEST_FAIL; + else + total_result = uefi_result; + QUEST_PRINT("%s : uefi_result(%d) / hlos_result(%d) / total_result(%d)\n", + __func__, uefi_result, hlos_result, total_result ); +#else + total_result = get_quest_hlos_result(CAL_QUEST); // get quest_hlos result +#endif + + + // 2.1. update param + if( param_quest_data.quest_step == CAL_QUEST ) { + +#if defined(CONFIG_SEC_QUEST_UEFI) + param_quest_data.quest_uefi_result + = SET_UEFI_RESULT(CAL_QUEST, param_quest_data.quest_uefi_result, uefi_result); + + if( param_quest_data.quest_hlos_remain_count==0 && + param_quest_data.quest_uefi_remain_count==0 ) { + + QUEST_PRINT("%s : cal step finished\n", __func__); + param_quest_data.quest_step = -1; + } +#else + if( param_quest_data.quest_hlos_remain_count==0 ) { + QUEST_PRINT("%s : cal step finished\n", __func__); + param_quest_data.quest_step = -1; + } +#endif + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + goto err_out; + } + } + + + // 3. result processing + switch (total_result) { + case TOTALTEST_PASS: { + QUEST_PRINT("ACAT QUEST Passed\n"); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK_ACAT_NONE\n"); + } break; + + case TOTALTEST_FAIL: { +#if defined(CONFIG_SEC_QUEST_UEFI) + // in case of CAL, the device can enter to ramdump mode using nad_end as soon as hlos failed + // so, let's consider only the case of uefi failure + if( uefi_result==TOTALTEST_FAIL && can_trigger_panic ) { + + QUEST_PRINT("%s : quest_uefi result was failed, so trigger panic\n", __func__ ); + QUEST_PRINT("%s : current step is CAL and panic will occur, so initialize quest_step=-1\n",__func__); + + // if failed, let's do not run quest_uefi any more. + param_quest_data.quest_uefi_remain_count = 0; + param_quest_data.quest_hlos_remain_count = 0; + param_quest_data.quest_step = -1; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + goto err_out; + } + + // trigger panic + mutex_unlock(&common_lock); + panic("quest_uefi failed"); + } + QUEST_PRINT("%s : uefi_result(%d), current quest_step(%d), so will not trigger panic\n", + __func__, uefi_result, param_quest_data.quest_step ); +#else + param_quest_data.quest_step = -1; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + goto err_out; + } +#endif + + QUEST_PRINT("ACAT QUEST fail\n"); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "NG_ACAT_ASV\n"); + } break; + + default: { + if( param_quest_data.quest_step == CAL_QUEST ) { + QUEST_PRINT("%s : total_result = %d, so let's make lastkmsg\n", __func__, total_result ); + snprintf(options, 50, "action:lastkmsg output_log_path:%s%c", CAL_QUEST_LOGPATH, '\0'); + make_debugging_logs(options); + } + + QUEST_PRINT("ACAT QUEST No Run\n"); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK\n"); + } + } + +err_out: + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "UNKNOWN\n"); +} + +static ssize_t store_quest_acat(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -1; + int idx = 0; + int quest_loop_count, dram_loop_count; + char temp[QUEST_BUFF_SIZE * 3]; + char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE]; + char *quest_ptr, *string; + + if( erase_pass ) { + QUEST_PRINT("%s : store_quest_erase was called. so let's ignore this call\n", __func__); + return count; + } + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + print_param_quest_data(); + +#if !defined(CONFIG_SEC_SKP) + // check if smd quest_hlos should start + if (param_quest_data.magic != QUEST_SMD_MAGIC) { + // <======================== 1st boot at right after SMD D/L done + QUEST_PRINT("1st boot at SMD\n"); + param_quest_data.magic = QUEST_SMD_MAGIC; + param_quest_data.smd_test_result = 0; + param_quest_data.bitmap_item_result = 0; + param_quest_data.quest_hlos_remain_count = 0; + param_quest_data.ddrtest_remain_count = 0; + param_quest_data.quest_step = SMD_QUEST; + if (!sec_set_param(param_index_quest,¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + + quest_step = SMD_QUEST; + do_quest(); + + // check smd ddr test and update bitmap_item_result here!!! + set_ddr_scan_result_for_smd(); + + return count; + } +#endif + + QUEST_PRINT("%s: buf(%s) count(%d)\n", __func__, buf, (int)count); + if ((int)count < QUEST_BUFF_SIZE) + return -EINVAL; + + /* Copy buf to quest temp */ + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + + while (idx < QUEST_CMD_LIST) { + quest_ptr = strsep(&string, ","); + strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE); + } + + if (!strncmp(buf, "nad_acat", 8)) { + + // get quest_loop_count and dram_loop_count + ret = sscanf(quest_cmd[1], "%d\n", &quest_loop_count); + if (ret != 1) return -EINVAL; + ret = sscanf(quest_cmd[2], "%d\n", &dram_loop_count); + if (ret != 1) return -EINVAL; + QUEST_PRINT("%s : nad_acat%d,%d\n", + __func__, quest_loop_count, dram_loop_count); + + if (!quest_loop_count && !dram_loop_count) { + // both counts are 0, means + // 1. testing refers to current remain_count + + // stop retrying when failure occur during retry test at ACAT/15test +#if defined(CONFIG_SEC_QUEST_UEFI) + if ( get_quest_hlos_result(CAL_QUEST)== TOTALTEST_FAIL + || get_quest_uefi_result(CAL_QUEST_LOGPATH, 0)==TOTALTEST_FAIL ) +#else + if ( get_quest_hlos_result(CAL_QUEST)==TOTALTEST_FAIL ) +#endif + { + QUEST_PRINT("%s : we did not initialize params when quest_hlos failed\n", __func__); + QUEST_PRINT("%s : current step is CAL and panic occurred, so force to initialize params\n",__func__); + + param_quest_data.quest_hlos_remain_count = 0; +#if defined(CONFIG_SEC_QUEST_UEFI) + param_quest_data.quest_uefi_remain_count = 0; +#endif + param_quest_data.ddrtest_remain_count = 0; + param_quest_data.quest_step = -1; + if (!sec_set_param(param_index_quest,¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + } + + // QUEST count still remain +#if defined(CONFIG_SEC_QUEST_UEFI) + if( (param_quest_data.quest_hlos_remain_count > 0) && + (param_quest_data.quest_hlos_remain_count > param_quest_data.quest_uefi_remain_count)) +#else + if( (param_quest_data.quest_hlos_remain_count > 0) ) +#endif + { + QUEST_PRINT("%s : quest_hlos_remain_count = %d, ddrtest_remain_count = %d\n", + __func__, param_quest_data.quest_hlos_remain_count, + param_quest_data.ddrtest_remain_count); + + param_quest_data.quest_hlos_remain_count--; + + // ongoing quest_step (can skip) + param_quest_data.quest_step = CAL_QUEST; + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + + quest_step = CAL_QUEST; + do_quest(); + } + } + else { + // not (0,0) means + // 1. new test count came, + // 2. so overwrite the remain_count, + // 3. and not reboot by itsself, + // 4. reboot cmd will come from outside like factory PGM + + /*--- set quest_uefi and quest_hlos remain count ---*/ +#if defined(CONFIG_SEC_QUEST_UEFI) + param_quest_data.quest_uefi_remain_count = quest_loop_count; + + // run quest_hlos if quest_loop_count>1 + if( quest_loop_count > 1 ) + param_quest_data.quest_hlos_remain_count = quest_loop_count; +#else + param_quest_data.quest_hlos_remain_count = quest_loop_count; +#endif + + param_quest_data.quest_step = CAL_QUEST; + + /*--- set ddrtest_mode ---*/ + if( (param_quest_data.ddrtest_remain_count = dram_loop_count) > 0 ) + param_quest_data.ddrtest_mode = UPLOAD_CAUSE_QUEST_DDR_TEST_CAL; + + /*--- set additional things for skp scenario ---*/ +#if defined(CONFIG_SEC_SKP) + param_quest_data.quest_uefi_remain_count = quest_loop_count+5; + param_quest_data.quest_hlos_remain_count = 1; + param_quest_data.magic = QUEST_SMD_MAGIC; + param_quest_data.quest_step = SKP_QUEST; +#endif + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + + // remove cal logs before new cal test + remove_specific_logs("cal"); + + QUEST_PRINT("%s : new cmd : quest_hlos_remain_count = %d, ddrtest_remain_count = %d\n", + __func__, + param_quest_data.quest_hlos_remain_count, + param_quest_data.ddrtest_remain_count); + } + }else + QUEST_PRINT("%s : wrong arguments\n", __func__); + +err_out: + return count; +} + +static DEVICE_ATTR(nad_acat, S_IRUGO | S_IWUSR, show_quest_acat, store_quest_acat); + +#if defined(CONFIG_SEC_SKP) +static void param_quest_init() +{ + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + } + print_param_quest_data(); + + QUEST_PRINT("%s : param_quest_init\n",__func__); + + param_quest_data.magic = 0; + param_quest_data.quest_hlos_remain_count = 0; + param_quest_data.quest_uefi_remain_count = 0; + param_quest_data.quest_step = -1; + + if (!sec_set_param(param_index_quest,¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + } +} +#endif + +// parsing result of quest_smd +static ssize_t show_quest_stat(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int hlos_result = TOTALTEST_UNKNOWN; + int first_check = 0; + char options[50] = { '\0', }; + + mutex_lock(&common_lock); + + QUEST_PRINT("%s : at boot time, third call from factory app (read sec_nad/nad_stat)\n", __func__); + + // refer to qpnp_pon_reason (index=boot_reason-1) + QUEST_PRINT("%s : boot_reason was %d\n", __func__, boot_reason); + + // 0. print param informations + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + print_param_quest_data(); + if( param_quest_data.smd_test_result == TOTALTEST_UNKNOWN ) + first_check = 1; + +#if defined(CONFIG_SEC_SKP) + { + int uefi_result = TOTALTEST_UNKNOWN; + static int checked = 0; + + if( checked++ > 0 ) { + QUEST_PRINT("%s : already checked for skp, so just return\n", __func__ ); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } + + QUEST_PRINT("%s : if questresult logs exist, let's move them to cal directory\n", __func__); + move_questresult_to_sub_dir(CAL_QUEST); + uefi_result = get_quest_uefi_result(CAL_QUEST_LOGPATH, 1); + param_quest_data.quest_uefi_result = + SET_UEFI_RESULT(CAL_QUEST, param_quest_data.quest_uefi_result, uefi_result); + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + + if ( uefi_result == TOTALTEST_NO_RESULT_FILE ) { + param_quest_init(); + QUEST_PRINT("%s : maybe first boot, so dont' run quest_uefi\n", __func__ ); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + }else if ( (uefi_result == TOTALTEST_FAIL) || + (uefi_result == TOTALTEST_NO_RESULT_STRING) ) { + if( param_quest_data.quest_step == SKP_QUEST ) { + QUEST_PRINT("%s : quest_uefi result was abnormal, so trigger panic\n", __func__ ); + param_quest_init(); + mutex_unlock(&common_lock); + panic("quest_uefi failed"); + }else { + QUEST_PRINT("%s : already entered to ramdump mode and initialized param, so return\n", __func__); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } + }else { + + QUEST_PRINT("%s : uefi_remain_count remained(%d)\n", + __func__, param_quest_data.quest_uefi_remain_count ); + + if ( param_quest_data.quest_uefi_remain_count > 0 ) { + + QUEST_PRINT("%s : quest_uefi will be started\n", __func__); + + param_quest_data.quest_uefi_result = + SET_UEFI_RESULT(CAL_QUEST, param_quest_data.quest_uefi_result, 0x0); + param_quest_data.magic = QUEST_SMD_MAGIC; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + mutex_unlock(&common_lock); + kernel_restart(NULL); + // not reached + }else { + param_quest_data.quest_step = -1; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + QUEST_PRINT("%s : quest_uefi done, so will run quest_hlos\n", __func__ ); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } + } + } +#endif + + + // 1. if smd magic is not written, NOT_TESTED + if (param_quest_data.magic != QUEST_SMD_MAGIC) { + QUEST_PRINT("SMD QUEST NOT_TESTED\n"); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "NOT_TESTED\n"); + } + + + // 2. parsing the result + hlos_result = get_quest_hlos_result(SMD_QUEST); + + + // 2.1. in case of SMD, let's save result to param + param_quest_data.smd_test_result = hlos_result; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n",__func__); + goto err_out; + } + + + // 3. result processing + switch (hlos_result) { + case TOTALTEST_PASS: { + // there is "AllTestDone" at SMD/test_result.csv + QUEST_PRINT("%s : SMD QUEST PASS\n", __func__); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } break; + case TOTALTEST_FAIL: { + // there is "FAIL" at SMD/test_result.csv + char strResult[BUFF_SZ-14] = { '\0', }; + if( !get_smd_item_result(strResult, ITEM_CNT) ) { + QUEST_PRINT("%s : wrong param_quest_data state\n", __func__); + mutex_unlock(&common_lock); + panic("abnormal param_quest_data state"); + } + QUEST_PRINT("%s : SMD QUEST FAIL\n", __func__); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "NG_2.0_FAIL_%s\n", strResult); + } break; + + case TOTALTEST_NO_RESULT_FILE: { + if (quest_step == SMD_QUEST) { + // will be executed soon + QUEST_PRINT("SMD QUEST TESTING\n"); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "TESTING\n"); + } else { + // not exeuted by unknown reasons + // ex1) magic exists but /data/log/quest was removed + // ex2) fail to execute quest.sh + // ex3) fail to make test_result.csv + // ex4) etc... + QUEST_PRINT("SMD QUEST NO_RESULT_FILE && not started\n"); + + // we want to know lastkmsg + if( first_check ) { + snprintf(options, 50, "action:lastkmsg output_log_path:%s%c", SMD_QUEST_LOGPATH, '\0'); + make_debugging_logs(options); + } + + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "RE_WORK\n"); + } + } break; + + case TOTALTEST_NO_RESULT_STRING: { + // sm8150 does not execute quest at hlos + if (quest_step == SMD_QUEST) { + // will be completed + QUEST_PRINT("SMD QUEST TESTING\n"); + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "TESTING\n"); + } else { + // need to rework + QUEST_PRINT("SMD QUEST NO_RESULT_STRING && not started\n"); + + // we want to know lastkmsg + if( first_check ) { + snprintf(options, 50, "action:lastkmsg output_log_path:%s%c", SMD_QUEST_LOGPATH, '\0'); + make_debugging_logs(options); + } + + mutex_unlock(&common_lock); + return snprintf(buf, BUFF_SZ, "RE_WORK\n"); + } + } break; + } + + + // (skip) 4. continue scenario and return current progress to factory app + + +err_out: + mutex_unlock(&common_lock); + QUEST_PRINT("SMD QUEST UNKNOWN\n"); + return snprintf(buf, BUFF_SZ, "RE_WORK\n"); +} + +static DEVICE_ATTR(nad_stat, S_IRUGO, show_quest_stat, NULL); + +static ssize_t show_ddrtest_remain_count(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + return snprintf(buf, BUFF_SZ, "%d\n", + param_quest_data.ddrtest_remain_count); +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_ddrtest_remain_count, S_IRUGO, show_ddrtest_remain_count, + NULL); + +static ssize_t show_quest_hlos_remain_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + return snprintf(buf, BUFF_SZ, "%d\n", param_quest_data.quest_hlos_remain_count); +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_qmvs_remain_count, S_IRUGO, show_quest_hlos_remain_count, NULL); + +static ssize_t store_quest_erase(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!strncmp(buf, "erase", 5)) { + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[3] = { NULL, NULL, NULL }; + int ret_userapp; + int api_gpio_test = 0; + char api_gpio_test_result[256] = { 0, }; + + argv[0] = ERASE_QUEST_PRG; + argv[1] = "all"; + + envp[0] = "HOME=/"; + envp[1] = + "PATH=/system/bin/quest/:/system/bin:/system/xbin"; + ret_userapp = + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + + if (!ret_userapp) { + QUEST_PRINT + ("remove_files.sh is executed. ret_userapp = %d\n", + ret_userapp); + erase_pass = 1; + } else { + QUEST_PRINT + ("remove_files.sh is NOT executed. ret_userapp = %d\n", + ret_userapp); + erase_pass = 0; + } + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", + __func__); + goto err_out; + } + + param_quest_data.magic = 0x0; +#if defined(CONFIG_SEC_QUEST_UEFI) + param_quest_data.quest_uefi_remain_count = 0x0; + param_quest_data.quest_uefi_result = 0x0; +#endif + param_quest_data.quest_hlos_remain_count = 0x0; + param_quest_data.ddrtest_remain_count = 0x0; + param_quest_data.ddrtest_result = 0x0; + param_quest_data.ddrtest_mode = 0x0; + param_quest_data.bitmap_item_result = 0x0; + param_quest_data.smd_test_result = 0x0; + param_quest_data.quest_step = -1; + + // flushing to param partition + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", + __func__); + goto err_out; + } + + QUEST_PRINT("clearing MAGIC code done = %d\n", + param_quest_data.magic); + QUEST_PRINT("quest_hlos_remain_count = %d\n", + param_quest_data.quest_hlos_remain_count); + QUEST_PRINT("ddrtest_remain_count = %d\n", + param_quest_data.ddrtest_remain_count); + QUEST_PRINT("ddrtest_result = 0x%8X\n", + param_quest_data.ddrtest_result); + QUEST_PRINT("clearing smd ddr test MAGIC code done = %d\n", + param_quest_data.magic); + + // clearing API test result + if (!sec_set_param(param_index_api_gpio_test, &api_gpio_test)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", + __func__); + goto err_out; + } + + if (!sec_set_param + (param_index_api_gpio_test_result, api_gpio_test_result)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", + __func__); + goto err_out; + } + return count; + } else + return count; +err_out: + return count; +} + +static ssize_t show_quest_erase(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (erase_pass) + return snprintf(buf, BUFF_SZ, "OK\n"); + else + return snprintf(buf, BUFF_SZ, "NG\n"); +} + +static DEVICE_ATTR(nad_erase, S_IRUGO | S_IWUSR, show_quest_erase, store_quest_erase); + +static ssize_t show_quest_dram(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ddr_result = DDRTEST_INCOMPLETED; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + // The factory app needs only the ddrtest result of ACAT now. + // If the ddrtest result of SMD and MAIN are also needed, + // implement an additional sysfs node or a modification of app. + ddr_result = get_ddr_scan_result(CAL_QUEST); + + if (ddr_result == DDRTEST_PASS) + return snprintf(buf, BUFF_SZ, "OK_DRAM\n"); + else if (ddr_result == DDRTEST_FAIL) + return snprintf(buf, BUFF_SZ, "NG_DRAM_DATA\n"); + else + return snprintf(buf, BUFF_SZ, "NO_DRAMTEST\n"); +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_dram, S_IRUGO, show_quest_dram, NULL); + +static ssize_t show_quest_dram_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ +// int ret=0; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + return snprintf(buf, BUFF_SZ, "0x%x\n", param_quest_data.ddrtest_result); +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_dram_debug, S_IRUGO, show_quest_dram_debug, NULL); + +static ssize_t show_quest_dram_err_addr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + int i = 0; + struct param_quest_ddr_test_result_t param_quest_ddr_test_result_data; + + if (!sec_get_param + (param_index_quest_ddr_result, ¶m_quest_ddr_test_result_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_ddr_test_result_data\n", + __func__); + goto err_out; + } + + ret = + snprintf(buf, BUFF_SZ, "Total : %d\n\n", + param_quest_ddr_test_result_data.ddr_err_addr_total); + for (i = 0; i < param_quest_ddr_test_result_data.ddr_err_addr_total; i++) { + ret += + snprintf(buf + ret - 1, BUFF_SZ, "[%d] 0x%llx\n", i, + param_quest_ddr_test_result_data.ddr_err_addr[i]); + } + + return ret; +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_dram_err_addr, S_IRUGO, show_quest_dram_err_addr, NULL); + +static ssize_t show_quest_support(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(CONFIG_ARCH_MSM8998) || defined(CONFIG_ARCH_MSM8996) || defined(CONFIG_ARCH_SDM845) || defined(CONFIG_ARCH_SM8150) || defined(CONFIG_ARCH_SDM670) + return snprintf(buf, BUFF_SZ, "SUPPORT\n"); +#else + return snprintf(buf, BUFF_SZ, "NOT_SUPPORT\n"); +#endif +} + +static DEVICE_ATTR(nad_support, S_IRUGO, show_quest_support, NULL); + +static ssize_t store_quest_logs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int fd = 0, idx = 0; + char path[100] = { '\0' }; + char temp[1]={'\0'}, tempbuf[BUFF_SZ]={'\0',}; + + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + + QUEST_PRINT("%s\n", buf); + + sscanf(buf, "%s", path); + fd = sys_open(path, O_RDONLY, 0); + + if (fd >= 0) { + while (sys_read(fd, temp, 1) == 1) { + tempbuf[idx++] = temp[0]; + if( temp[0]=='\n' ) { + tempbuf[idx] = '\0'; + QUEST_PRINT("%s", tempbuf); + idx = 0; + } + } + + sys_close(fd); + } else { + QUEST_PRINT("The File is not existed. %s\n", __func__); + } + + set_fs(old_fs); + return count; +} + +static DEVICE_ATTR(nad_logs, 0200, NULL, store_quest_logs); + +static ssize_t store_quest_end(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char result[20] = { '\0' }; + + QUEST_PRINT("result : (step=%d) %s\n", quest_step, buf); + + sscanf(buf, "%s", result); + + if (!strcmp(result, "quest_pass")) { + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, quest_uevent.envp); + if( quest_step==SMD_QUEST ) + QUEST_PRINT("%s : let's update smd_test_result when show_quest_stat is called\n", __func__); + QUEST_PRINT + ("QUEST result : %s, quest_pass, Send to Process App for Quest test end : %s\n", + result, __func__); + } else { + if (quest_step == MAIN_QUEST || quest_step == CAL_QUEST) { + QUEST_PRINT + ("QUEST result : %s, Device enter the upload mode because it is SHORT_QUEST : %s\n", + result, __func__); + panic(result); + } else { + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, + quest_uevent.envp); + QUEST_PRINT + ("QUEST result : %s, Send to Process App for Quest test end : %s\n", + result, __func__); + } + } + + return count; +} + +static DEVICE_ATTR(nad_end, S_IWUSR, NULL, store_quest_end); + + +char *STR_TEST_ITEM[ITEM_CNT + 1] = { + "UFS", + "QMESACACHE", + "QMESADDR", + "VDDMIN", + "SUSPEND", + "CCOHERENCY", + "ICACHE", + "CRYPTO", + "DDRSCAN", + "SENSOR", + "GFX", + // + "FULL" +}; + +static ssize_t show_smd_quest_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t info_size = 0; + int iCnt; + + for (iCnt = 0; iCnt < ITEM_CNT; iCnt++) { + char strResult[QUEST_BUFF_SIZE] = { '\0', }; + + if (!get_smd_item_result(strResult, iCnt)) + goto err_out; + + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"%s\":\"%s\",", STR_TEST_ITEM[iCnt], strResult); + } + info_size += snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size,"\n"); + + QUEST_PRINT("%s, result=%s\n", __func__, buf); + + return info_size; +err_out: + return snprintf(buf, BUFF_SZ, "UNKNOWN\n"); +} + +static ssize_t store_smd_quest_result(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int _result = -1; + char test_name[QUEST_BUFF_SIZE * 2] = { '\0', }; + char temp[QUEST_BUFF_SIZE * 3] = { '\0', }; + char quest_test[2][QUEST_BUFF_SIZE * 2]; // 2: "test_name", "result" + char result_string[QUEST_BUFF_SIZE] = { '\0', }; + char *quest_ptr, *string; + int item = -1; + + if (quest_step != SMD_QUEST) { + QUEST_PRINT("store quest_result only at smd_quest\n"); + return -EIO; + } + + QUEST_PRINT("buf : %s count : %d\n", buf, (int)count); + + if (QUEST_BUFF_SIZE * 3 < (int)count || (int)count < 4) { + QUEST_PRINT("result cmd size too long : QUEST_BUFF_SIZE<%d\n", + (int)count); + return -EINVAL; + } + + /* Copy buf to quest temp */ + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + + quest_ptr = strsep(&string, ","); + strlcpy(quest_test[0], quest_ptr, QUEST_BUFF_SIZE * 2); + quest_ptr = strsep(&string, ","); + strlcpy(quest_test[1], quest_ptr, QUEST_BUFF_SIZE * 2); + + sscanf(quest_test[0], "%s", test_name); + sscanf(quest_test[1], "%s", result_string); + + QUEST_PRINT("test_name : %s, test result=%s\n", test_name, result_string); + + if (TEST_PASS(result_string)) + _result = QUEST_ITEM_TEST_PASS; + else if (TEST_FAIL(result_string)) + _result = QUEST_ITEM_TEST_FAIL; + else + _result = QUEST_ITEM_TEST_INCOMPLETED; + + if (TEST_CRYPTO(test_name)) + item = CRYPTO; + else if (TEST_ICACHE(test_name)) + item = ICACHE; + else if (TEST_CCOHERENCY(test_name)) + item = CCOHERENCY; + else if (TEST_SUSPEND(test_name)) + item = SUSPEND; + else if (TEST_VDDMIN(test_name)) + item = VDDMIN; + else if (TEST_QMESADDR(test_name)) + item = QMESADDR; + else if (TEST_QMESACACHE(test_name)) + item = QMESACACHE; + else if (TEST_UFS(test_name)) + item = UFS; + else if (TEST_SENSOR(test_name)) + item = SENSOR; + else if (TEST_GFX(test_name)) + item = GFX; + else + item = NOT_ASSIGN; + + if (item == NOT_ASSIGN) { + QUEST_PRINT("%s : fail - get test item in QUEST!! \n", __func__); + return count; + } + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + return -EINVAL; + } + + param_quest_data.bitmap_item_result |= + TEST_ITEM_RESULT(quest_step, item, _result); + + QUEST_PRINT("%s : bitmap_item_result=%u, quest_step=%d, item=%d, _result=%d\n", + __func__, param_quest_data.bitmap_item_result, quest_step, item, _result); + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + return -EINVAL; + } + + return count; +} + + +static DEVICE_ATTR(nad_result, S_IRUGO | S_IWUSR, show_smd_quest_result, store_smd_quest_result); + + + +static ssize_t store_quest_main(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0; + int ret = -1; + char temp[QUEST_BUFF_SIZE * 3]; + char quest_cmd[QUEST_MAIN_CMD_LIST][QUEST_BUFF_SIZE]; + char *quest_ptr, *string; + int running_time; + + /* Copy buf to quest temp */ + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + + while (idx < QUEST_MAIN_CMD_LIST) { + quest_ptr = strsep(&string, ","); + strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE); + } + + if (quest_step == MAIN_QUEST) { + QUEST_PRINT("duplicated!\n"); + return count; + } + + if (!strncmp(buf, "start", 5)) { + ret = sscanf(quest_cmd[1], "%d\n", &running_time); + if (ret != 1) + return -EINVAL; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + return -1; + } + + param_quest_data.ddrtest_mode = UPLOAD_CAUSE_QUEST_DDR_TEST_MAIN; + param_quest_data.ddrtest_remain_count = 1; + param_quest_data.quest_hlos_remain_count = 0; +#if defined(CONFIG_SEC_QUEST_UEFI) + // the concept of main quest is + // hlos long -> repeat 5 times (uefi -> boot to idle -> parsing -> reboot ) -> ddr -> main quest done + // we will decrease hlos count when parsing the result of quest_uefi forcely + param_quest_data.quest_hlos_remain_count = 5; + param_quest_data.quest_uefi_remain_count = 5; + + // (skip) 1. let's remove previous questresult + // let's backup previous uefi log into backup dir using quest_main.sh becuase hlos run first + //arrange_quest_logs(MAIN_QUEST_LOGPATH); + + // 2. initialize quest_uefi_result + param_quest_data.quest_uefi_result + = SET_UEFI_RESULT(MAIN_QUEST, param_quest_data.quest_uefi_result, 0x0); + +#endif + // 3. set quest_step + param_quest_data.quest_step = MAIN_QUEST; + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + return -1; + } + + quest_step = MAIN_QUEST; + do_quest(); + } + + return count; +} + +static ssize_t show_quest_main(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(CONFIG_SEC_QUEST_UEFI) + int uefi_result = TOTALTEST_UNKNOWN; + int hlos_result = TOTALTEST_UNKNOWN; +#endif + int total_result = TOTALTEST_UNKNOWN; + + + QUEST_PRINT("%s : called\n", __func__); + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + print_param_quest_data(); + +#if defined(CONFIG_SEC_QUEST_UEFI) + // uefi logs were moved into sub dir at show_main_quest_run + // so just call get_quest_uefi_result + uefi_result = get_quest_uefi_result(MAIN_QUEST_LOGPATH, 1); + hlos_result = get_quest_hlos_result(MAIN_QUEST); + + // In case of main quest, we ran both quest_uefi and quest_hlos. + // So, we have to check all result of them. + if( uefi_result==TOTALTEST_PASS && hlos_result==TOTALTEST_PASS ) + total_result = TOTALTEST_PASS; + else if( uefi_result==TOTALTEST_FAIL || hlos_result==TOTALTEST_FAIL ) + total_result = TOTALTEST_FAIL; + else + total_result = TOTALTEST_UNKNOWN; + QUEST_PRINT("%s : uefi_result(%d) / hlos_result(%d) / total_result(%d)\n", + __func__, uefi_result, hlos_result, total_result ); +#else + total_result = get_quest_hlos_result(MAIN_QUEST); // get quest_hlos result +#endif + + switch (total_result) { + case TOTALTEST_PASS: { + QUEST_PRINT("MAIN QUEST Passed\n"); + return snprintf(buf, BUFF_SZ, "OK_2.0\n"); + } break; + + case TOTALTEST_FAIL: { + QUEST_PRINT("MAIN QUEST fail\n"); + return snprintf(buf, BUFF_SZ, "NG_2.0_FAIL\n"); + } break; + + default: { + QUEST_PRINT("MAIN QUEST No Run\n"); + return snprintf(buf, BUFF_SZ, "OK\n"); + } + } + +err_out: + return snprintf(buf, BUFF_SZ, "MAIN QUEST UNKNOWN\n"); +} + +static DEVICE_ATTR(balancer, S_IRUGO | S_IWUSR, show_quest_main, store_quest_main); + +static ssize_t show_main_quest_timeout(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, BUFF_SZ, "%d\n", MAIN_QUEST_TIMEOUT); +} +static DEVICE_ATTR(timeout, 0444, show_main_quest_timeout, NULL); + + +static ssize_t store_main_quest_run(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#if defined(CONFIG_SEC_QUEST_UEFI) + + int uefi_remain_count =0; + int ddrtest_remain_count = 0; + + QUEST_PRINT("%s is called\n", __func__); + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + return -EINVAL; + } + uefi_remain_count = param_quest_data.quest_uefi_remain_count; + ddrtest_remain_count = param_quest_data.ddrtest_remain_count; + + // if the count of quest_uefi remains, let's trigger quest_uefi + if( uefi_remain_count > 0 || ddrtest_remain_count > 0 ) { + QUEST_PRINT("%s : uefi_remain_count remained(%d)\n", + __func__, uefi_remain_count ); + + if( uefi_remain_count > 0 ) { + QUEST_PRINT("%s : will trigger quest_uefi\n", __func__); + + // 1. let's remove previous questresult + arrange_quest_logs(MAIN_QUEST_LOGPATH); + + // 2. initialize quest_uefi_result + param_quest_data.quest_uefi_result + = SET_UEFI_RESULT(MAIN_QUEST, param_quest_data.quest_uefi_result, 0x0); + } + + // 3. we do not repeat quest_hlos, so let's decrease hlos count forcely + param_quest_data.quest_hlos_remain_count--; + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + return -EINVAL; + } + + // 4. reboot device + msleep(3000); // to guarantee saving FLOG + kernel_restart(NULL); + }else + panic("abnormal uefi_remain_count"); + +#endif + return count; +} + + +// parsing the result of quest_uefi and return the status of main quest scenario +static ssize_t show_main_quest_run(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(CONFIG_SEC_QUEST_UEFI) + + int uefi_remain_count = 0; + int ddrtest_remain_count = 0; + int uefi_result = TOTALTEST_UNKNOWN; + char options[50] = { '\0', }; + + QUEST_PRINT("%s : at boot time, always first call from factory app (read sec_nad_balancer/run)\n", __func__); + + // refer to qpnp_pon_reason (index=boot_reason-1) + QUEST_PRINT("%s : boot_reason was %d\n", __func__, boot_reason); + + // 0. print param informations + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + print_param_quest_data(); + uefi_remain_count = param_quest_data.quest_uefi_remain_count; + ddrtest_remain_count = param_quest_data.ddrtest_remain_count; + + + // 0.1. if now is not main quest, return + if( param_quest_data.quest_step != MAIN_QUEST ) { + QUEST_PRINT("%s : now step is not main quest, so will be returned\n", __func__); + return snprintf(buf, BUFF_SZ, "END\n"); + }else + QUEST_PRINT("%s : now step is main quest\n", __func__); + + + // 1. move questresult logs to sub directory + QUEST_PRINT("%s : if questresult logs exist, let's move them to main directory\n", __func__); + move_questresult_to_sub_dir(MAIN_QUEST); + + + // 2. parsing the result of quest_uefi + uefi_result = get_quest_uefi_result(MAIN_QUEST_LOGPATH, 1); + QUEST_PRINT("%s : update param_quest_data.quest_uefi_result \n", __func__); + param_quest_data.quest_uefi_result + = SET_UEFI_RESULT(MAIN_QUEST, param_quest_data.quest_uefi_result, uefi_result); + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + goto err_out; + } + + + // 3. result processing + switch( uefi_result ) { + case TOTALTEST_NO_RESULT_FILE : + case TOTALTEST_NO_RESULT_STRING : { + QUEST_PRINT("%s : uefi_result = %d, so let's make lastkmsg\n", __func__, uefi_result ); + snprintf(options, 50, "action:lastkmsg output_log_path:%s%c", MAIN_QUEST_LOGPATH, '\0'); + make_debugging_logs(options); + break; + } + case TOTALTEST_FAIL : { + if( !(uefi_remain_count==0 && ddrtest_remain_count==0) ) { + QUEST_PRINT("%s : quest_uefi result was failed, so trigger panic\n", __func__ ); + + // if failed, let's do not run quest_uefi any more. + param_quest_data.quest_uefi_remain_count = 0; + param_quest_data.quest_hlos_remain_count = 0; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + goto err_out; + } + + // trigger panic + panic("quest_uefi failed"); + } + } + } + + + // 4. continue scenario and return current progress to factory app + if( uefi_remain_count > 0 || ddrtest_remain_count > 0 ) { + + // if quest_uefi count remains, let's return NOT_END + QUEST_PRINT("%s : NOT_END (uefi=%d, ddrtest=%d)\n", __func__, + uefi_remain_count, ddrtest_remain_count); + return snprintf(buf, BUFF_SZ, "NOT_END\n"); + + } else if ( uefi_remain_count == 0 && ddrtest_remain_count == 0) { + + // initialize quest_step + param_quest_data.quest_step = -1; + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + goto err_out; + } + return snprintf(buf, BUFF_SZ, "END\n"); + } + + +err_out: + return snprintf(buf, BUFF_SZ, "UNKNOWN\n"); + +#else + return snprintf(buf, BUFF_SZ, "END\n"); +#endif +} +static DEVICE_ATTR(run, S_IRUGO | S_IWUSR, show_main_quest_run, store_main_quest_run); + + +static ssize_t store_quest_info(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char info_name[QUEST_BUFF_SIZE * 2] = { '\0', }; + char temp[QUEST_BUFF_SIZE * 3] = { '\0', }; + char quest_test[2][QUEST_BUFF_SIZE * 2]; // 2: "info_name", "result" + int resultValue; + char *quest_ptr, *string; + + QUEST_PRINT("buf : %s count : %d\n", buf, (int)count); + + if (QUEST_BUFF_SIZE * 3 < (int)count || (int)count < 4) { + QUEST_PRINT("result cmd size too long : QUEST_BUFF_SIZE<%d\n", + (int)count); + return -EINVAL; + } + + /* Copy buf to quest temp */ + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + + quest_ptr = strsep(&string, ","); + strlcpy(quest_test[0], quest_ptr, QUEST_BUFF_SIZE * 2); + quest_ptr = strsep(&string, ","); + strlcpy(quest_test[1], quest_ptr, QUEST_BUFF_SIZE * 2); + + sscanf(quest_test[0], "%s", info_name); + sscanf(quest_test[1], "%d", &resultValue); + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + return -EINVAL; + } + + if (!strcmp("thermal", info_name)) + param_quest_data.thermal = resultValue; + else if (!strcmp("clock", info_name)) + param_quest_data.tested_clock = resultValue; + + QUEST_PRINT("info_name : %s, result=%d\n", info_name, resultValue); + + if (!sec_set_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - set param!! param_quest_data\n", __func__); + return -EINVAL; + } + + return count; +} + +static ssize_t show_quest_info(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t info_size = 0; + + if (!sec_get_param(param_index_quest, ¶m_quest_data)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"REMAIN_CNT\":\"%d\",", + param_quest_data.quest_hlos_remain_count); + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"THERMAL\":\"%d\",", param_quest_data.thermal); + info_size += + snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size, + "\"CLOCK\":\"%d\",", param_quest_data.tested_clock); + + return info_size; +err_out: + return snprintf(buf, BUFF_SZ, "PARAM ERROR\n"); +} + +static DEVICE_ATTR(nad_info, S_IRUGO | S_IWUSR, show_quest_info, store_quest_info); + +static ssize_t show_quest_api(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int api_gpio_test; + char api_gpio_test_result[256]; + + if (!sec_get_param(param_index_api_gpio_test, &api_gpio_test)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", __func__); + goto err_out; + } + + if (api_gpio_test) { + if (!sec_get_param + (param_index_api_gpio_test_result, api_gpio_test_result)) { + QUEST_PRINT("%s : fail - get param!! param_quest_data\n", + __func__); + goto err_out; + } + return snprintf(buf, BUFF_SZ, "%s", api_gpio_test_result); + } else + return snprintf(buf, BUFF_SZ, "NONE\n"); + +err_out: + return snprintf(buf, BUFF_SZ, "READ ERROR\n"); +} + +static DEVICE_ATTR(nad_api, 0444, show_quest_api, NULL); + +static ssize_t show_quest_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + #if 0 + //QUEST_2.0.1_SS_SLT_06142018_NS_GCM - INITPORT + return snprintf(buf, BUFF_SZ, "SM8150.0204.01.INITPORT\n"); + + //QUEST_1.0_SS_07172018_SM8150_preR2_sdcard + return snprintf(buf, BUFF_SZ, "SM8150.0101.01.INITPORT\n"); + #endif + #if defined(CONFIG_ARCH_SDM710) || defined(CONFIG_ARCH_SDM670) + return snprintf(buf, BUFF_SZ, "SDM710.1004.01.RELEASE\n"); + #else + //QUEST_1.0_SS_08312018_SDM855_gcm_flashval_temp + return snprintf(buf, BUFF_SZ, "SM8150.0102.01.PRERELEASE\n"); + #endif +} +static DEVICE_ATTR(nad_version, 0444, show_quest_version, NULL); + + + +static ssize_t show_quest_qdaf_control(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if( qdaf_cur_cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC || + qdaf_cur_cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC ) + return snprintf(buf, BUFF_SZ, "RUNNING\n"); + + return snprintf(buf, BUFF_SZ, "READY\n"); +} +static ssize_t store_quest_qdaf_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0, cmd_mode=0, wait_val=0; + char temp[QUEST_BUFF_SIZE * 3]; + char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE]; + char *quest_ptr, *string; + int ret_userapp; + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + + QUEST_PRINT("%s : is called\n",__func__); + + if ((int)count < QUEST_BUFF_SIZE) { + QUEST_PRINT("%s : return error (count=%d)\n", __func__, (int)count); + return -EINVAL;; + } + + if (strncmp(buf, "nad_qdaf_control", 16)) { + QUEST_PRINT("%s : return error (buf=%s)\n", __func__, buf); + return -EINVAL;; + } + + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + while (idx < QUEST_CMD_LIST) { + quest_ptr = strsep(&string, ","); + strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE); + } + sscanf(quest_cmd[1], "%d", &cmd_mode); + + // let's just return if receiving the same command as before one + if ( qdaf_cur_cmd_mode==cmd_mode ) { + if( cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC || + cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC) { + QUEST_PRINT("%s : return because qdaf.sh already has been running.\n", __func__); + }else if( cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP || + cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP_WATING) { + QUEST_PRINT("%s : return because qdaf.sh has not been running yet.\n", __func__); + } + return count; + } + + // if receiving control command from AtNadCheck when quest is running, let's return NG + // if receiving control command from quest.sh, invoke qdaf.sh to stop qdaf with UMH_WAIT_PROC + if( quest_step!=-1 && + (cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC || + cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC || + cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP) ) { + QUEST_PRINT("%s : return because quest is running.\n", __func__); + return count; + } + + argv[0] = QDAF_PROG; + argv[1] = quest_cmd[1]; + switch (cmd_mode) { + case QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC : + QUEST_PRINT("%s : qdaf will be started (without panic)\n",__func__); + wait_val = UMH_WAIT_EXEC; + break; + case QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC: + QUEST_PRINT("%s : qdaf will be started (with panic)\n",__func__); + wait_val = UMH_WAIT_EXEC; + break; + case QUEST_QDAF_ACTION_CONTROL_STOP : + QUEST_PRINT("%s : qdaf will be stopped\n",__func__); + wait_val = UMH_WAIT_EXEC; + break; + case QUEST_QDAF_ACTION_CONTROL_STOP_WATING : + QUEST_PRINT("%s : qdaf will be stopped (waiting)\n",__func__); + // should wait for completion to gurantee not working of qdaf + wait_val = UMH_WAIT_PROC; + break; + default : + QUEST_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode); + return count; + } + + ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val); + if (!ret_userapp) { + qdaf_cur_cmd_mode = cmd_mode; + QUEST_PRINT("%s : succeded to trigger qdaf.sh\n", __func__); + QUEST_PRINT("%s : qdaf_cur_cmd_mode=%s\n", __func__, quest_cmd[1]); + }else { + qdaf_cur_cmd_mode = QUEST_QDAF_ACTION_EMPTY; + // evaulate return value after ret_userapp>>8 + QUEST_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp); + } + return count; +} +static DEVICE_ATTR(nad_qdaf_control, S_IRUGO | S_IWUSR, show_quest_qdaf_control, store_quest_qdaf_control); + +/* +static int qdaf_check_prev_result(void) +{ + int fd = 0; + int ret; + char buf[512] = { '\0', }; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + + fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0); + if (fd >= 0) { + int found = 0; + + printk(KERN_DEBUG); + + while (sys_read(fd, buf, 512)) { + char *ptr; + char *div = "\n"; + char *tok = NULL; + + ptr = buf; + while ((tok = strsep(&ptr, div)) != NULL) { + if ((strstr(tok, "failure"))) { + ret = QUEST_QDAF_TEST_RESULT_NG; + found = 1; + break; + } + } + + if (found) break; + } + + if (!found) ret = QUEST_QDAF_TEST_RESULT_OK; + sys_close(fd); + } else { + QUEST_PRINT("%s : result file is not existed\n", __func__); + ret = QUEST_QDAF_TEST_RESULT_NONE; + } + + set_fs(old_fs); + return ret; +} + +static ssize_t show_quest_qdaf_prev_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int qdaf_result = qdaf_check_prev_result(); + switch (qdaf_result) { + case QUEST_QDAF_TEST_RESULT_OK: + QUEST_PRINT("%s : previous test result : pass\n", __func__); + return snprintf(buf, BUFF_SZ, "OK\n"); + break; + case QUEST_QDAF_TEST_RESULT_NG: + QUEST_PRINT("%s : previous test result : fail\n", __func__); + return snprintf(buf, BUFF_SZ, "NG\n"); + break; + default: + QUEST_PRINT("%s : previous test result : unknown\n", __func__); + return snprintf(buf, BUFF_SZ, "NONE\n"); + } + + return snprintf(buf, BUFF_SZ, "NONE\n"); + +} +*/ + +static ssize_t show_quest_qdaf_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret_userapp, wait_val=0, failed_cnt=0; + char act[QUEST_BUFF_SIZE]={0,}; + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + + QUEST_PRINT("%s : is called\n",__func__); + + // refer to qpnp_pon_reason (index=boot_reason-1) + QUEST_PRINT("%s : boot_reason was %d\n", __func__, boot_reason); + + snprintf(act, 1, "%d", QUEST_QDAF_ACTION_RESULT_GET); + + argv[0] = QDAF_PROG; + // TODO : conversion int->string + argv[1] = "6"; //QUEST_QDAF_ACTION_RESULT_GET + wait_val = UMH_WAIT_PROC; + ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val); + if (!ret_userapp) { + QUEST_PRINT("%s : succeded to trigger qdaf.sh and failed_cnt=0\n", __func__); + return snprintf(buf, BUFF_SZ, "OK\n"); + }else if (ret_userapp > 0 ) { + // evaulate return value after ret_userapp>>8 + // qdaf.sh exit with failed_cnt if persist.qdaf.failed_cnt>0, so let's use return value from call_usermodehelper + failed_cnt = ret_userapp>>8; + QUEST_PRINT("%s : succeded to trigger qdaf.sh and return_value=%d(failed_cnt=%d)\n", + __func__, ret_userapp, failed_cnt); + return snprintf(buf, BUFF_SZ, "NG,%d\n", failed_cnt); + }else { + // evaulate return value after ret_userapp>>8 + QUEST_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp); + return snprintf(buf, BUFF_SZ, "NONE\n"); + } + + return snprintf(buf, BUFF_SZ, "NONE\n"); +} + +static ssize_t store_quest_qdaf_result(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0, cmd_mode=0, wait_val=0; + int ret_userapp; + char temp[QUEST_BUFF_SIZE * 3]; + char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE]; + char *quest_ptr, *string; + char *argv[4] = { NULL, NULL, NULL, NULL }; + char *envp[5] = { + "HOME=/", + "PATH=/system/bin/quest:/system/bin:/system/xbin", + "ANDROID_DATA=/data", + "ANDROID_ROOT=/system", + NULL }; + + QUEST_PRINT("%s : is called\n",__func__); + + if ((int)count < QUEST_BUFF_SIZE) { + QUEST_PRINT("%s : return error (count=%d)\n", __func__, (int)count); + return -EINVAL;; + } + + if (strncmp(buf, "nad_qdaf_result", 15)) { + QUEST_PRINT("%s : return error (buf=%s)\n", __func__, buf); + return -EINVAL;; + } + + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + while (idx < QUEST_CMD_LIST) { + quest_ptr = strsep(&string, ","); + strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE); + } + sscanf(quest_cmd[1], "%d", &cmd_mode); + + argv[0] = QDAF_PROG; + argv[1] = quest_cmd[1]; + switch (cmd_mode) { + case QUEST_QDAF_ACTION_RESULT_ERASE : + QUEST_PRINT("%s : qdaf will erase failed count\n",__func__); + wait_val = UMH_WAIT_PROC; + break; + default : + QUEST_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode); + return count; + } + + ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val); + if (!ret_userapp) + QUEST_PRINT("%s : succeded to trigger qdaf.sh\n", __func__); + else { + // evaulate return value after ret_userapp>>8 + QUEST_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp); + } + return count; +} + +static DEVICE_ATTR(nad_qdaf_result, S_IRUGO | S_IWUSR, show_quest_qdaf_result, store_quest_qdaf_result); + +static void qdaf_save_logs(void) +{ + int fd, idx=0; + char temp[1]={'\0'}, buf[BUFF_SZ]={'\0',}; + + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0); + if (fd >= 0) { + while (sys_read(fd, temp, 1) == 1) { + buf[idx++] = temp[0]; + if( temp[0]=='\n' ) { + buf[idx] = '\0'; + QUEST_PRINT("%s", buf); + idx = 0; + } + } + sys_close(fd); + } + set_fs(old_fs); +} + +static ssize_t store_quest_qdaf_debug(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int idx = 0, cmd_mode=0; + char temp[QUEST_BUFF_SIZE * 3]; + char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE]; + char *quest_ptr, *string; + + QUEST_PRINT("%s : is called\n",__func__); + + if ((int)count < QUEST_BUFF_SIZE) { + QUEST_PRINT("%s : return error (count=%d)\n", __func__, (int)count); + return -EINVAL;; + } + + if (strncmp(buf, "nad_qdaf_debug", 14)) { + QUEST_PRINT("%s : return error (buf=%s)\n", __func__, buf); + return -EINVAL;; + } + + strlcpy(temp, buf, QUEST_BUFF_SIZE * 3); + string = temp; + while (idx < QUEST_CMD_LIST) { + quest_ptr = strsep(&string, ","); + strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE); + } + sscanf(quest_cmd[1], "%d", &cmd_mode); + + switch (cmd_mode) { + case QUEST_QDAF_ACTION_DEBUG_SAVE_LOGS : + QUEST_PRINT("%s : qdaf will save log into kmsg\n",__func__); + qdaf_save_logs(); + return count; + case QUEST_QDAF_ACTION_DEBUG_TRIGGER_PANIC : + QUEST_PRINT("%s : will trigger panic\n",__func__); + panic("qdaf_fail"); + return count; + } + + return count; +} +static DEVICE_ATTR(nad_qdaf_debug, S_IWUSR, NULL, store_quest_qdaf_debug); + + +static int __init sec_quest_init(void) +{ + int ret = 0; + struct device *sec_nad; + struct device *sec_nad_balancer; + + QUEST_PRINT("%s\n", __func__); + + /* Skip quest init when device goes to lp charging */ + if (lpcharge) + return ret; + + mutex_init(&uefi_parse_lock); + mutex_init(&hlos_parse_lock); + mutex_init(&common_lock); + + sec_nad = sec_device_create(0, NULL, "sec_nad"); + + if (IS_ERR(sec_nad)) { + QUEST_PRINT("%s Failed to create device(sec_nad)!\n", __func__); + return PTR_ERR(sec_nad); + } + + ret = device_create_file(sec_nad, &dev_attr_nad_stat); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_ddrtest_remain_count); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qmvs_remain_count); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_erase); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_acat); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_dram); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_support); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_logs); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_end); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_dram_debug); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_dram_err_addr); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_result); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_api); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_info); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_version); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_control); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_debug); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_result); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + if (add_uevent_var(&quest_uevent, "NAD_TEST=%s", "DONE")) { + QUEST_PRINT("%s : uevent NAD_TEST_AND_PASS is failed to add\n", + __func__); + goto err_create_nad_sysfs; + } + + sec_nad_balancer = sec_device_create(0, NULL, "sec_nad_balancer"); + + if (IS_ERR(sec_nad)) { + QUEST_PRINT("%s Failed to create device(sec_nad)!\n", __func__); + return PTR_ERR(sec_nad); + } + + ret = device_create_file(sec_nad_balancer, &dev_attr_balancer); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad_balancer, &dev_attr_timeout); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + ret = device_create_file(sec_nad_balancer, &dev_attr_run); + if (ret) { + QUEST_PRINT("%s: Failed to create device file\n", __func__); + goto err_create_nad_sysfs; + } + + return 0; +err_create_nad_sysfs: + return ret; +} + +module_init(sec_quest_init); diff --git a/drivers/debug/spinlock_test.c b/drivers/debug/spinlock_test.c new file mode 100644 index 000000000000..900bf50963e7 --- /dev/null +++ b/drivers/debug/spinlock_test.c @@ -0,0 +1,522 @@ +/* + * File is for internal use only, and not for distribution + * If distribution is needed, OS review is required. + * See OSRQCT-5220 for further information. + */ + +#define pr_fmt(fmt) "spinlock-test: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "spinlock_test" +#define CPU_NUM_MAX NR_CPUS +#define TEST_ITERS_MAX 100 +#define INNER_TEST_TIME_SECS_DEFAULT 5 +#define INNER_TEST_TIME_SECS_MAX 100 +#define SPINLOCK_TEST_DELAY_MS 1000 +#define IRQ_DIALBE_TIME_MS (MSEC_PER_SEC/HZ) + +static void spinwork_fn(struct work_struct *work); +static struct delayed_work spinworks[CPU_NUM_MAX]; +static struct dentry *dent; +static DEFINE_PER_CPU(int, cpu_test_idx); + +static u32 start; +static u32 num_test_iters; +static u32 canceltest; +static u32 cpu_start[CPU_NUM_MAX]; +static u32 inner_test_time_secs = INNER_TEST_TIME_SECS_DEFAULT; + +static struct locks { + spinlock_t lock1; + spinlock_t lock2; +} testlocks __cacheline_aligned; + +/* + * global_lock_static will be in the kernel static (BSS?) region. + * global_inner_lock will be kmalloc'ed. The idea here is to use + * different cache-lines for the two locks. + */ +static DEFINE_SPINLOCK(global_lock_static); +static spinlock_t *global_lock_heap; +static spinlock_t *uncached_lock; +static phys_addr_t phys; + +enum { + IRQS_DISABLED_TEST = 1, + STACK_LOCK_TEST, + TWOLOCKS_TEST, + SIMPLE_TEST, + UNCACHED_LOCK_TEST, + NUM_OF_SPINLOCK_TEST = 5, +}; + +static int set_inner_test_time(void *data, u64 val) +{ + if (val != 0) { + inner_test_time_secs = val < INNER_TEST_TIME_SECS_MAX ? \ + val : INNER_TEST_TIME_SECS_MAX; + pr_debug("set_inner_test_time: %u\n", inner_test_time_secs); + } + return 0; +} + +static int get_inner_test_time(void *data, u64 *val) +{ + *val = inner_test_time_secs; + return 0; +} + +static int spinlock_test_start(void); +static int spinlock_test_stop(void); +static int set_spinlock_start(void *data, u64 val) +{ + int ret = 0; + if (val > 0) { + if (start) { + pr_err("The last test is not done yet. Exitting...\n"); + return 0; + } + num_test_iters = val < TEST_ITERS_MAX ? val : TEST_ITERS_MAX; + ret = spinlock_test_start(); + } else if (val == 0) { + if (!start) { + pr_err("No spinlock test is running. Exitting...\n"); + return 0; + } + ret = spinlock_test_stop(); + } + return ret; +} + +static int get_spinlock_start(void *data, u64 *val) +{ + *val = start; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(spinlock_start_ops, + get_spinlock_start, set_spinlock_start, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(inner_test_time_ops, + get_inner_test_time, set_inner_test_time, "%llu\n"); + +static int creat_spinlock_debugfs(void) +{ + /* + * Create a simple debugfs with the name of "spinlock-test", + * + * As this is a simple directory, no uevent will be sent to + * userspace. + */ + struct dentry *phandle; + + dent = debugfs_create_dir("spinlock-test", 0); + if (IS_ERR(dent)) + return -ENOMEM; + + phandle = debugfs_create_file("start", 0666, dent, 0, &spinlock_start_ops); + if (!phandle || IS_ERR(phandle)) + goto err; + + phandle = debugfs_create_file("inner_test_time", 0666, dent, 0, &inner_test_time_ops); + if (!phandle || IS_ERR(phandle)) + goto err; + + return 0; +err: + debugfs_remove_recursive(dent); + return -ENOMEM; +} + +static void sync_cpus(int test_idx, int cpu) +{ + int ocpu, flag; + per_cpu(cpu_test_idx, cpu) = test_idx; + /* + Wait until all other cpus finished the same test case + within the same iteration. + */ + do { + flag = 0; + for_each_online_cpu(ocpu) { + if (per_cpu(cpu_test_idx, ocpu) != test_idx || + cpu_start[ocpu] != cpu_start[cpu]) { + flag = 1; + break; + } + } + } while (flag); +} + +static void irqs_disabled_spintest(int cpu) +{ + unsigned long flags, irqflags; + int incritical = 0; + ktime_t test_time = ktime_add_us(ktime_get(), + (inner_test_time_secs * USEC_PER_SEC)); + pr_info("(irq_disabled): Running test on CPU %d for %d seconds.\n", cpu, + inner_test_time_secs); + + while (ktime_compare(ktime_get(), test_time) < 0) + { + ktime_t irq_disable_test_time = ktime_add_us(ktime_get(), + (IRQ_DIALBE_TIME_MS * USEC_PER_MSEC)); + local_irq_save(irqflags); + while (ktime_compare(ktime_get(), irq_disable_test_time) < 0) { + spin_lock_irqsave(&global_lock_static, flags); + spin_lock(global_lock_heap); + incritical++; + spin_unlock(global_lock_heap); + spin_unlock_irqrestore(&global_lock_static, flags); + } + local_irq_restore(irqflags); + } + + pr_info("(irqs disabled) CPU%d entered critical section %d times.\n", + cpu, incritical); + sync_cpus(IRQS_DISABLED_TEST, cpu); +} + +static void spintest_two_lock_single_cacheline(int cpu) +{ + unsigned long flags; + int incritical = 0; + ktime_t test_time = ktime_add_us(ktime_get(), + (inner_test_time_secs * USEC_PER_SEC)); + pr_info("(two-locks): Running test on CPU %d for %d seconds.\n", cpu, + inner_test_time_secs); + + while (ktime_compare(ktime_get(), test_time) < 0) + { + spin_lock_irqsave(&testlocks.lock1, flags); + spin_lock(&testlocks.lock2); + incritical++; + spin_unlock(&testlocks.lock2); + spin_unlock_irqrestore(&testlocks.lock1, flags); + } + + pr_info("(two-locks): CPU%d entered critical section %d times.\n", + cpu, incritical); + sync_cpus(TWOLOCKS_TEST, cpu); +} + +static void spintest_simple(int cpu) +{ + unsigned long flags; + int incritical = 0; + ktime_t test_time = ktime_add_us(ktime_get(), + (inner_test_time_secs * USEC_PER_SEC)); + pr_info("(simple): Running test on CPU %d for %d seconds.\n", cpu, + inner_test_time_secs); + + while (ktime_compare(ktime_get(), test_time) < 0) + { + spin_lock_irqsave(&global_lock_static, flags); + incritical++; + spin_unlock_irqrestore(&global_lock_static, flags); + } + + pr_info("(simple): CPU%d entered critical section %d times.\n", + cpu, incritical); + sync_cpus(SIMPLE_TEST, cpu); +} + +static void spintest_stack_lock(int cpu) +{ + unsigned long flags; + int incritical = 0; + ktime_t test_time = ktime_add_us(ktime_get(), + (inner_test_time_secs * USEC_PER_SEC)); + spinlock_t stack_lock; + spin_lock_init(&stack_lock); + + pr_info("(stack-lock): Running test on CPU %d for %d seconds.\n", cpu, + inner_test_time_secs); + + while (ktime_compare(ktime_get(), test_time) < 0) + { + spin_lock_irqsave(&global_lock_static, flags); + spin_lock(&stack_lock); + incritical++; + spin_unlock(&stack_lock); + spin_unlock_irqrestore(&global_lock_static, flags); + } + + pr_info("(stack-lock): CPU%d entered critical section %d times.\n", + cpu, incritical); + sync_cpus(STACK_LOCK_TEST, cpu); +} + +static void spintest_uncached_lock_test(int cpu) +{ + unsigned long flags; + int incritical = 0; + ktime_t test_time = ktime_add_us(ktime_get(), + (inner_test_time_secs * USEC_PER_SEC)); + pr_info("(uncached-lock): Running test on CPU %d for %d seconds.\n", cpu, + inner_test_time_secs); + + while (ktime_compare(ktime_get(), test_time) < 0) + { + spin_lock_irqsave(&global_lock_static, flags); + spin_lock(uncached_lock); + incritical++; + spin_unlock(uncached_lock); + spin_unlock_irqrestore(&global_lock_static, flags); + } + + pr_info("(uncached-lock): CPU%d entered critical section %d times.\n", + cpu, incritical); + + sync_cpus(UNCACHED_LOCK_TEST, cpu); +} + +static void spinwork_fn(struct work_struct *work) +{ + struct delayed_work *dwork = (struct delayed_work *)work; + int cpu = smp_processor_id(); + + if (canceltest) { + pr_err("test cancelled!\n"); + return; + } + + /* + * Start interrupt disabled test. This should provide maximum + * contention. + */ + irqs_disabled_spintest(cpu); + pr_info("irqs_disabled_spintest is done (CPU:%d iteration:%d)\n", + cpu, cpu_start[cpu]); + msleep(SPINLOCK_TEST_DELAY_MS); + + spintest_simple(cpu); + pr_info("spintest_simple is done (CPU:%d iteration:%d)\n", + cpu, cpu_start[cpu]); + msleep(SPINLOCK_TEST_DELAY_MS); + + /* Use a spinlock allocated on the stack */ + spintest_stack_lock(cpu); + pr_info("spintest_stack_lock is done (CPU:%d iteration:%d)\n", + cpu, cpu_start[cpu]); + msleep(SPINLOCK_TEST_DELAY_MS); + + spintest_two_lock_single_cacheline(cpu); + pr_info("spintest_two_lock is done (CPU:%d iteration:%d)\n", + cpu, cpu_start[cpu]); + msleep(SPINLOCK_TEST_DELAY_MS); + + spintest_uncached_lock_test(cpu); + pr_info("spintest_uncached_lock_test is done (CPU:%d iteration:%d)\n", + cpu, cpu_start[cpu]); + msleep(SPINLOCK_TEST_DELAY_MS); + + if (++cpu_start[cpu] < num_test_iters) { + schedule_work_on(cpu, &dwork->work); + } +} + +static int spinlock_test_start(void) +{ + int i, flag; + ktime_t test_time; + unsigned long total_wait_time; + + start = 1; + canceltest = 0; + pr_info("Starting spinlock test\n"); + +#ifdef CONFIG_HOTPLUG_CPU + /* Online all the cores */ + for_each_present_cpu(i) { + if (!cpu_online(i)) { + cpu_up(i); + } + } +#endif + + get_online_cpus(); + for_each_online_cpu(i) { + cpu_start[i] = 0; + INIT_DEFERRABLE_WORK(&spinworks[i], spinwork_fn); + schedule_delayed_work_on(i, &spinworks[i], msecs_to_jiffies(150)); + pr_info("Scheduling spinlock test on cpu %d\n", i); + } + + /* + * Estimate and calculate total wait time according to + * - number of toal test cases. (e.g irq_disable, stack spinlock, etc..) + * - inner test time for each test case + * - number of iterations for each test case + * - sleep time during two test cases + */ + total_wait_time = (NUM_OF_SPINLOCK_TEST + 1) * (inner_test_time_secs * \ + num_test_iters + SPINLOCK_TEST_DELAY_MS / MSEC_PER_SEC); + pr_info("Total test time might be ~%lu seconds\n", total_wait_time); + + test_time = ktime_add_us(ktime_get(), + (total_wait_time * USEC_PER_SEC)); + while (ktime_compare(ktime_get(), test_time) < 0) { + flag = 0; + for_each_online_cpu(i) { + if (cpu_start[i] != num_test_iters) { + flag = 1; + break; + } + } + if (!flag) { + start = 0; + break; + } + msleep(SPINLOCK_TEST_DELAY_MS); + } + + if (!start) { + pr_info("spinlock test is done successfully!\n"); + put_online_cpus(); + return 0; + } else { + pr_err("spinlock test Failed!\n"); + spinlock_test_stop(); + return -1; + } +} + +static int spinlock_test_stop(void) +{ + int i; + canceltest = 1; + if (!start) { + pr_err("No spinlock test is running. Exitting...\n"); + return -1; + } + for(i = 0; i < CPU_NUM_MAX; i++){ + if (spinworks[i].wq != NULL) { + pr_info("canceling work oncpu %d\n", i); + cancel_delayed_work_sync(&spinworks[i]); + } + } + /* "start" might be updated by the test work thread */ + if (start) + put_online_cpus(); + start = 0; + return 0; +} + +static int spinlock_test_remove(struct platform_device *pdev) +{ + /* Cancel the test if it's not done yet */ + if (start) + spinlock_test_stop(); + + if (dent) { + debugfs_remove_recursive(dent); + dent = NULL; + } + if (global_lock_heap) { + kfree(global_lock_heap); + global_lock_heap = NULL; + } + + if (uncached_lock) { + dma_free_coherent(&pdev->dev, sizeof(spinlock_t), uncached_lock, phys); + uncached_lock = NULL; + } + + return 0; +} + +static int spinlock_test_probe(struct platform_device *pdev) +{ + int ret = 0; + if (num_possible_cpus() > CPU_NUM_MAX) { + dev_err(&pdev->dev, "Number of possible cpus on this target \ + exceeds the limit %d\n", CPU_NUM_MAX); + return -EINVAL; + } + + global_lock_heap = kzalloc(sizeof(spinlock_t), GFP_KERNEL); + if (!global_lock_heap) { + dev_err(&pdev->dev, "unable to alloc memory for global lock\n"); + return -ENOMEM; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)) + arch_setup_dma_ops(&pdev->dev, 0, U64_MAX, NULL, 0); +#endif + + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + uncached_lock = dma_alloc_coherent(&pdev->dev, sizeof(spinlock_t), &phys, GFP_KERNEL); + if (!uncached_lock) { + dev_err(&pdev->dev ,"Failed to allocate Uncached memory for lock\n"); + kfree(global_lock_heap); + return -ENOMEM; + } + + spin_lock_init(&testlocks.lock1); + spin_lock_init(&testlocks.lock2); + spin_lock_init(global_lock_heap); + spin_lock_init(uncached_lock); + + ret = creat_spinlock_debugfs(); + if (ret) { + dev_err(&pdev->dev, "create spinlock debugfs failed!\n"); + kfree(global_lock_heap); + dma_free_coherent(&pdev->dev, sizeof(spinlock_t), uncached_lock, phys); + uncached_lock = NULL; + return ret; + } + return 0; +} + +static struct platform_driver spinlock_test_driver = { + .probe = spinlock_test_probe, + .remove = spinlock_test_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static void platform_spinlock_release(struct device* dev) +{ + return; +} + +static struct platform_device spinlock_test_device = { + .name = MODULE_NAME, + .id = -1, + .dev = { + .release = platform_spinlock_release, + } +}; + +static int __init spinlock_test_init(void) +{ + platform_device_register(&spinlock_test_device); + return platform_driver_register(&spinlock_test_driver); +} + +static void __exit spinlock_test_exit(void) +{ + platform_driver_unregister(&spinlock_test_driver); + platform_device_unregister(&spinlock_test_device); +} + +MODULE_DESCRIPTION("SPINLOCK TEST"); +MODULE_LICENSE("GPL v2"); +module_init(spinlock_test_init); +module_exit(spinlock_test_exit); diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index e223a8c77946..72c8f3749dee 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -224,7 +224,7 @@ struct extcon_cable { }; static struct class *extcon_class; -#if defined(CONFIG_ANDROID) +#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH) static struct class_compat *switch_class; #endif /* CONFIG_ANDROID */ @@ -1011,7 +1011,7 @@ static int create_extcon_class(void) return PTR_ERR(extcon_class); extcon_class->dev_groups = extcon_groups; -#if defined(CONFIG_ANDROID) +#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH) switch_class = class_compat_register("switch"); if (WARN(!switch_class, "cannot allocate")) return -ENOMEM; @@ -1237,7 +1237,7 @@ int extcon_dev_register(struct extcon_dev *edev) put_device(&edev->dev); goto err_dev; } -#if defined(CONFIG_ANDROID) +#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH) if (switch_class) ret = class_compat_create_link(switch_class, &edev->dev, NULL); #endif /* CONFIG_ANDROID */ @@ -1333,7 +1333,7 @@ void extcon_dev_unregister(struct extcon_dev *edev) kfree(edev->cables); } -#if defined(CONFIG_ANDROID) +#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH) if (switch_class) class_compat_remove_link(switch_class, &edev->dev, NULL); #endif @@ -1407,7 +1407,7 @@ module_init(extcon_class_init); static void __exit extcon_class_exit(void) { -#if defined(CONFIG_ANDROID) +#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH) class_compat_unregister(switch_class); #endif class_destroy(extcon_class); diff --git a/drivers/fingerprint/Kconfig b/drivers/fingerprint/Kconfig new file mode 100755 index 000000000000..26401872929e --- /dev/null +++ b/drivers/fingerprint/Kconfig @@ -0,0 +1,32 @@ +# +# Sensor drivers configuration +# +menuconfig SENSORS_FINGERPRINT + bool "Finger Print Sensor devices" + help + Say Y here, and a list of sensors drivers will be displayed. + Everything that didn't fit into the other categories is here. This option + doesn't affect the kernel. + +if SENSORS_FINGERPRINT + +config SENSORS_VFS8XXX + tristate "VFS8XXX fingerprint sensor support" + default n + help + If you say yes here you get support for Synaptics's + fingerprint sensor NAMSAN. + +config SENSORS_ET5XX + tristate "ET5XX fingerprint sensor support" + default n + help + If you say yes here you get support for Egistec's + fingerprint sensor ET5XX. + +config SENSORS_GW32X + tristate "generic goodix fingerprint driver" + default n + help + add support for goodix fingerprint driver. +endif # SENSORS_FINGERPRINT diff --git a/drivers/fingerprint/Makefile b/drivers/fingerprint/Makefile new file mode 100755 index 000000000000..4e12605a6f89 --- /dev/null +++ b/drivers/fingerprint/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the sensors drivers. +# + +# Each configuration option enables a list of files. + +ccflags-y := $(KBUILD_FP_SENSOR_CFLAGS) + +obj-$(CONFIG_SENSORS_FINGERPRINT) += fingerprint_sysfs.o +obj-$(CONFIG_SENSORS_VFS8XXX) += vfs8xxx.o +obj-$(CONFIG_SENSORS_ET5XX) += et5xx-spi.o et5xx-spi_data_transfer.o +obj-$(CONFIG_SENSORS_GW32X) += gf_common.o gf_platform.o \ No newline at end of file diff --git a/drivers/fingerprint/et5xx-spi.c b/drivers/fingerprint/et5xx-spi.c new file mode 100755 index 000000000000..0645b2155523 --- /dev/null +++ b/drivers/fingerprint/et5xx-spi.c @@ -0,0 +1,1398 @@ +/* + * Copyright (C) 2016 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 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 "fingerprint.h" +#include "et5xx.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DECLARE_BITMAP(minors, N_SPI_MINORS); + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +static int gpio_irq; +static struct etspi_data *g_data; +static DECLARE_WAIT_QUEUE_HEAD(interrupt_waitq); +static unsigned int bufsiz = 1024; +module_param(bufsiz, uint, 0444); +MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); + +void etspi_pin_control(struct etspi_data *etsspi, bool pin_set) +{ + int status = 0; + + etsspi->p->state = NULL; + if (pin_set) { + if (!IS_ERR(etsspi->pins_idle)) { + status = pinctrl_select_state(etsspi->p, + etsspi->pins_idle); + if (status) + pr_err("%s: can't set pin default state\n", + __func__); + pr_debug("%s idle\n", __func__); + } + } else { + if (!IS_ERR(etsspi->pins_sleep)) { + status = pinctrl_select_state(etsspi->p, + etsspi->pins_sleep); + if (status) + pr_err("%s: can't set pin sleep state\n", + __func__); + pr_debug("%s sleep\n", __func__); + } + } +} + +static irqreturn_t etspi_fingerprint_interrupt(int irq, void *dev_id) +{ + struct etspi_data *etspi = (struct etspi_data *)dev_id; + + etspi->int_count++; + etspi->finger_on = 1; + disable_irq_nosync(gpio_irq); + wake_up_interruptible(&interrupt_waitq); + wake_lock_timeout(&etspi->fp_signal_lock, 1 * HZ); + pr_info("%s FPS triggered.int_count(%d) On(%d)\n", __func__, + etspi->int_count, etspi->finger_on); + etspi->interrupt_count++; + return IRQ_HANDLED; +} + +int etspi_Interrupt_Init( + struct etspi_data *etspi, + int int_ctrl, + int detect_period, + int detect_threshold) +{ + int status = 0; + + etspi->finger_on = 0; + etspi->int_count = 0; + pr_info("%s int_ctrl = %d detect_period = %d detect_threshold = %d\n", + __func__, + int_ctrl, + detect_period, + detect_threshold); + + etspi->detect_period = detect_period; + etspi->detect_threshold = detect_threshold; + gpio_irq = gpio_to_irq(etspi->drdyPin); + + if (gpio_irq < 0) { + pr_err("%s gpio_to_irq failed\n", __func__); + status = gpio_irq; + goto done; + } + + if (etspi->drdy_irq_flag == DRDY_IRQ_DISABLE) { + if (request_irq + (gpio_irq, etspi_fingerprint_interrupt + , int_ctrl, "etspi_irq", etspi) < 0) { + pr_err("%s drdy request_irq failed\n", __func__); + status = -EBUSY; + goto done; + } else { + enable_irq_wake(gpio_irq); + etspi->drdy_irq_flag = DRDY_IRQ_ENABLE; + } + } +done: + return status; +} + +int etspi_Interrupt_Free(struct etspi_data *etspi) +{ + pr_info("%s\n", __func__); + + if (etspi != NULL) { + if (etspi->drdy_irq_flag == DRDY_IRQ_ENABLE) { + if (!etspi->int_count) + disable_irq_nosync(gpio_irq); + + disable_irq_wake(gpio_irq); + free_irq(gpio_irq, etspi); + etspi->drdy_irq_flag = DRDY_IRQ_DISABLE; + } + etspi->finger_on = 0; + etspi->int_count = 0; + } + return 0; +} + +void etspi_Interrupt_Abort(struct etspi_data *etspi) +{ + wake_up_interruptible(&interrupt_waitq); +} + +unsigned int etspi_fps_interrupt_poll( + struct file *file, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct etspi_data *etspi = file->private_data; + + pr_debug("%s FPS fps_interrupt_poll, finger_on(%d), int_count(%d)\n", + __func__, etspi->finger_on, etspi->int_count); + + if (!etspi->finger_on) + poll_wait(file, &interrupt_waitq, wait); + + if (etspi->finger_on) { + mask |= POLLIN | POLLRDNORM; + etspi->finger_on = 0; + } + return mask; +} + +/*-------------------------------------------------------------------------*/ + +static void etspi_reset(struct etspi_data *etspi) +{ + pr_info("%s\n", __func__); + + gpio_set_value(etspi->sleepPin, 0); + usleep_range(1050, 1100); + gpio_set_value(etspi->sleepPin, 1); + etspi->reset_count++; +} + +static void etspi_power_control(struct etspi_data *etspi, int status) +{ + pr_info("%s status = %d\n", __func__, status); + if (status == 1) { + if (etspi->ldo_pin) + gpio_set_value(etspi->ldo_pin, 1); + usleep_range(1100, 1150); + if (etspi->sleepPin) + gpio_set_value(etspi->sleepPin, 1); + etspi_pin_control(etspi, true); + usleep_range(10000, 10050); + } else if (status == 0) { + etspi_pin_control(etspi, false); + if (etspi->sleepPin) + gpio_set_value(etspi->sleepPin, 0); + if (etspi->ldo_pin) + gpio_set_value(etspi->ldo_pin, 0); + } else { + pr_err("%s can't support this value. %d\n", __func__, status); + } +} + +static ssize_t etspi_read(struct file *filp, + char __user *buf, + size_t count, + loff_t *f_pos) +{ + /*Implement by vendor if needed*/ + return 0; +} + +static ssize_t etspi_write(struct file *filp, + const char __user *buf, + size_t count, + loff_t *f_pos) +{ +/*Implement by vendor if needed*/ + return 0; +} + + +static long etspi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0, retval = 0; + struct etspi_data *etspi; + struct spi_device *spi; + u32 tmp; + struct egis_ioc_transfer *ioc = NULL; + u8 *buf, *address, *result, *fr; + /* Check type and command number */ + if (_IOC_TYPE(cmd) != EGIS_IOC_MAGIC) { + pr_err("%s _IOC_TYPE(cmd) != EGIS_IOC_MAGIC", __func__); + return -ENOTTY; + } + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, + (void __user *)arg, + _IOC_SIZE(cmd)); + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, + (void __user *)arg, + _IOC_SIZE(cmd)); + if (err) { + pr_err("%s err", __func__); + return -EFAULT; + } + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + etspi = filp->private_data; + spin_lock_irq(&etspi->spi_lock); + spi = spi_dev_get(etspi->spi); + spin_unlock_irq(&etspi->spi_lock); + + if (spi == NULL) { + pr_err("%s spi == NULL", __func__); + return -ESHUTDOWN; + } + + mutex_lock(&etspi->buf_lock); + + /* segmented and/or full-duplex I/O request */ + if (_IOC_NR(cmd) != _IOC_NR(EGIS_IOC_MESSAGE(0)) + || _IOC_DIR(cmd) != _IOC_WRITE) { + retval = -ENOTTY; + goto out; + } + + tmp = _IOC_SIZE(cmd); + if ((tmp == 0) || (tmp % sizeof(struct egis_ioc_transfer)) != 0) { + pr_err("%s ioc size error\n", __func__); + retval = -EINVAL; + goto out; + } + /* copy into scratch area */ + ioc = kmalloc(tmp, GFP_KERNEL); + if (!ioc) { + retval = -ENOMEM; + goto out; + } + if (__copy_from_user(ioc, (void __user *)arg, tmp)) { + pr_err("%s __copy_from_user error\n", __func__); + retval = -EFAULT; + goto out; + } + + switch (ioc->opcode) { + /* + * Read register + * tx_buf include register address will be read + */ + case FP_REGISTER_READ: + address = ioc->tx_buf; + result = ioc->rx_buf; + pr_debug("etspi FP_REGISTER_READ\n"); + + retval = etspi_io_read_register(etspi, address, result); + if (retval < 0) { + pr_err("%s FP_REGISTER_READ error retval = %d\n" + , __func__, retval); + } + break; + + /* + * Write data to register + * tx_buf includes address and value will be wrote + */ + case FP_REGISTER_WRITE: + buf = ioc->tx_buf; + pr_debug("%s FP_REGISTER_WRITE\n", __func__); + + retval = etspi_io_write_register(etspi, buf); + if (retval < 0) { + pr_err("%s FP_REGISTER_WRITE error retval = %d\n" + , __func__, retval); + } + break; + case FP_REGISTER_MREAD: + address = ioc->tx_buf; + result = ioc->rx_buf; + pr_debug("%s FP_REGISTER_MREAD\n", __func__); + retval = etspi_io_read_registerex(etspi, address, result, + ioc->len); + if (retval < 0) { + pr_err("%s FP_REGISTER_MREAD error retval = %d\n" + , __func__, retval); + } + break; + case FP_REGISTER_BREAD: + pr_debug("%s FP_REGISTER_BREAD\n", __func__); + retval = etspi_io_burst_read_register(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_REGISTER_BREAD error retval = %d\n" + , __func__, retval); + } + break; + case FP_REGISTER_BWRITE: + pr_debug("%s FP_REGISTER_BWRITE\n", __func__); + retval = etspi_io_burst_write_register(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_REGISTER_BWRITE error retval = %d\n" + , __func__, retval); + } + break; + case FP_REGISTER_BREAD_BACKWARD: + pr_debug("%s FP_REGISTER_BREAD_BACKWARD\n", __func__); + retval = etspi_io_burst_read_register_backward(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_REGISTER_BREAD_BACKWARD error retval = %d\n" + , __func__, retval); + } + break; + case FP_REGISTER_BWRITE_BACKWARD: + pr_debug("%s FP_REGISTER_BWRITE_BACKWARD\n", __func__); + retval = etspi_io_burst_write_register_backward(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_REGISTER_BWRITE_BACKWARD error retval = %d\n" + , __func__, retval); + } + break; + case FP_NVM_READ: + pr_debug("%s FP_NVM_READ, (%d)\n", __func__, spi->max_speed_hz); + retval = etspi_io_nvm_read(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_READ error retval = %d\n" + , __func__, retval); + } + retval = etspi_io_nvm_off(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_OFF error retval = %d\n" + , __func__, retval); + } else { + pr_debug("%s FP_NVM_OFF\n", __func__); + } + break; + case FP_NVM_WRITE: + pr_debug("%s FP_NVM_WRITE, (%d)\n", __func__, + spi->max_speed_hz); + retval = etspi_io_nvm_write(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_WRITE error retval = %d\n" + , __func__, retval); + } + retval = etspi_io_nvm_off(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_OFF error retval = %d\n" + , __func__, retval); + } else { + pr_debug("%s FP_NVM_OFF\n", __func__); + } + break; + case FP_NVM_WRITEEX: + pr_debug("%s FP_NVM_WRITEEX, (%d)\n", __func__, + spi->max_speed_hz); + retval = etspi_io_nvm_writeex(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_WRITEEX error retval = %d\n" + , __func__, retval); + } + retval = etspi_io_nvm_off(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_OFF error retval = %d\n" + , __func__, retval); + } else { + pr_debug("%s FP_NVM_OFF\n", __func__); + } + break; + case FP_NVM_OFF: + pr_debug("%s FP_NVM_OFF\n", __func__); + retval = etspi_io_nvm_off(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_NVM_OFF error retval = %d\n" + , __func__, retval); + } + break; + case FP_VDM_READ: + pr_debug("%s FP_VDM_READ\n", __func__); + retval = etspi_io_vdm_read(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_VDM_READ error retval = %d\n" + , __func__, retval); + } else { + pr_debug("%s FP_VDM_READ finished.\n", __func__); + } + break; + case FP_VDM_WRITE: + pr_debug("%s FP_VDM_WRITE\n", __func__); + retval = etspi_io_vdm_write(etspi, ioc); + if (retval < 0) { + pr_err("%s FP_VDM_WRITE error retval = %d\n" + , __func__, retval); + } else { + pr_debug("%s FP_VDM_WRTIE finished.\n", __func__); + } + break; + /* + * Get one frame data from sensor + */ + case FP_GET_ONE_IMG: + fr = ioc->rx_buf; + pr_debug("%s FP_GET_ONE_IMG\n", __func__); + + retval = etspi_io_get_frame(etspi, fr, ioc->len); + if (retval < 0) { + pr_err("%s FP_GET_ONE_IMG error retval = %d\n" + , __func__, retval); + } + break; + + case FP_SENSOR_RESET: + pr_info("%s FP_SENSOR_RESET\n", __func__); + etspi_reset(etspi); + break; + + case FP_RESET_SET: + break; + + + case FP_POWER_CONTROL: + case FP_POWER_CONTROL_ET5XX: + pr_info("%s FP_POWER_CONTROL, status = %d\n", __func__, + ioc->len); + etspi_power_control(etspi, ioc->len); + break; + + case FP_SET_SPI_CLOCK: + pr_info("%s FP_SET_SPI_CLOCK, clock = %d\n", __func__, + ioc->speed_hz); +#ifdef ENABLE_SENSORS_FPRINT_SECURE + if (etspi->enabled_clk) { + if (spi->max_speed_hz != ioc->speed_hz) { + pr_info("%s already enabled. DISABLE_SPI_CLOCK\n", + __func__); + wake_unlock(&etspi->fp_spi_lock); + etspi->enabled_clk = false; + } else { + pr_info("%s already enabled same clock.\n", + __func__); + break; + } + } + spi->max_speed_hz = ioc->speed_hz; + wake_lock(&etspi->fp_spi_lock); + etspi->enabled_clk = true; +#else + spi->max_speed_hz = ioc->speed_hz; +#endif + break; + + /* + * Trigger initial routine + */ + case INT_TRIGGER_INIT: + pr_debug("%s Trigger function init\n", __func__); + retval = etspi_Interrupt_Init( + etspi, + (int)ioc->pad[0], + (int)ioc->pad[1], + (int)ioc->pad[2]); + break; + /* trigger */ + case INT_TRIGGER_CLOSE: + pr_debug("%s Trigger function close\n", __func__); + retval = etspi_Interrupt_Free(etspi); + break; + /* Poll Abort */ + case INT_TRIGGER_ABORT: + pr_debug("%s Trigger function abort\n", __func__); + etspi_Interrupt_Abort(etspi); + break; +#ifdef ENABLE_SENSORS_FPRINT_SECURE + case FP_DISABLE_SPI_CLOCK: + pr_info("%s FP_DISABLE_SPI_CLOCK\n", __func__); + + if (etspi->enabled_clk) { + pr_info("%s DISABLE_SPI_CLOCK\n", __func__); + wake_unlock(&etspi->fp_spi_lock); + etspi->enabled_clk = false; + } + break; + case FP_CPU_SPEEDUP: + pr_info("%s FP_CPU_SPEEDUP\n", __func__); + + if (ioc->len) { + u8 retry_cnt = 0; + pr_info("%s FP_CPU_SPEEDUP ON:%d, retry: %d\n", + __func__, ioc->len, retry_cnt); + if (etspi->min_cpufreq_limit) { + pm_qos_add_request(&etspi->pm_qos, + PM_QOS_CPU_DMA_LATENCY, 0); +#ifdef CONFIG_CPU_FREQ_LIMIT + do { + retval = set_freq_limit(DVFS_FINGER_ID, + etspi->min_cpufreq_limit); + retry_cnt++; + if (retval) { + pr_err("%s: booster start failed. (%d) retry: %d\n" + , __func__, retval, + retry_cnt); + usleep_range(500, 510); + } + } while (retval && retry_cnt < 7); +#endif + } + } else { + pr_info("%s FP_CPU_SPEEDUP OFF\n", __func__); +#ifdef CONFIG_CPU_FREQ_LIMIT + retval = set_freq_limit(DVFS_FINGER_ID, -1); + if (retval) + pr_err("%s: booster stop failed. (%d)\n" + , __func__, retval); +#endif + pm_qos_remove_request(&etspi->pm_qos); + } + break; + case FP_SET_SENSOR_TYPE: + if ((int)ioc->len >= SENSOR_OOO && + (int)ioc->len < SENSOR_MAXIMUM) { + if ((int)ioc->len == SENSOR_OOO && + etspi->sensortype == SENSOR_FAILED) { + pr_info("%s maintain type check from out of order :%s\n", + __func__, + sensor_status[g_data->sensortype + 2]); + } else { + etspi->sensortype = (int)ioc->len; + pr_info("%s FP_SET_SENSOR_TYPE :%s\n", + __func__, + sensor_status[g_data->sensortype + 2]); + } + } else { + pr_err("%s FP_SET_SENSOR_TYPE invalid value %d\n", + __func__, (int)ioc->len); + etspi->sensortype = SENSOR_UNKNOWN; + } + break; + case FP_SET_LOCKSCREEN: + pr_info("%s FP_SET_LOCKSCREEN\n", __func__); + break; + case FP_SET_WAKE_UP_SIGNAL: + pr_info("%s FP_SET_WAKE_UP_SIGNAL\n", __func__); + break; +#endif + case FP_SENSOR_ORIENT: + pr_info("%s: orient is %d\n", __func__, etspi->orient); + + retval = put_user(etspi->orient, (u8 __user *) (uintptr_t)ioc->rx_buf); + if (retval != 0) + pr_err("%s FP_SENSOR_ORIENT put_user fail: %d\n", __func__, retval); + break; + + case FP_SPI_VALUE: + etspi->spi_value = ioc->len; + pr_info("%s spi_value: 0x%x\n", __func__,etspi->spi_value); + break; + case FP_IOCTL_RESERVED_01: + case FP_IOCTL_RESERVED_02: + break; + default: + pr_info("%s undefined case\n", __func__); + retval = -EFAULT; + break; + + } + +out: + if (ioc != NULL) + kfree(ioc); + + mutex_unlock(&etspi->buf_lock); + spi_dev_put(spi); + if (retval < 0) + pr_err("%s retval = %d\n", __func__, retval); + return retval; +} + +#ifdef CONFIG_COMPAT +static long etspi_compat_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + return etspi_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define etspi_compat_ioctl NULL +#endif +/* CONFIG_COMPAT */ + +static int etspi_open(struct inode *inode, struct file *filp) +{ + struct etspi_data *etspi; + int status = -ENXIO; + + pr_info("%s\n", __func__); + mutex_lock(&device_list_lock); + + list_for_each_entry(etspi, &device_list, device_entry) { + if (etspi->devt == inode->i_rdev) { + status = 0; + break; + } + } + if (status == 0) { + if (etspi->buf == NULL) { + etspi->buf = kmalloc(bufsiz, GFP_KERNEL); + if (etspi->buf == NULL) { + dev_dbg(&etspi->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; + } + } + if (status == 0) { + etspi->users++; + filp->private_data = etspi; + nonseekable_open(inode, filp); + etspi->bufsiz = bufsiz; + } + } else + pr_debug("%s nothing for minor %d\n" + , __func__, iminor(inode)); + + mutex_unlock(&device_list_lock); + return status; +} + +static int etspi_release(struct inode *inode, struct file *filp) +{ + struct etspi_data *etspi; + + pr_info("%s\n", __func__); + mutex_lock(&device_list_lock); + etspi = filp->private_data; + filp->private_data = NULL; + + /* last close? */ + etspi->users--; + if (etspi->users == 0) { + int dofree; + + kfree(etspi->buf); + etspi->buf = NULL; + + /* ... after we unbound from the underlying device? */ + spin_lock_irq(&etspi->spi_lock); + dofree = (etspi->spi == NULL); + spin_unlock_irq(&etspi->spi_lock); + + if (dofree) + kfree(etspi); + } + mutex_unlock(&device_list_lock); + + return 0; +} + +int etspi_platformInit(struct etspi_data *etspi) +{ + int status = 0; + + pr_info("%s\n", __func__); + /* gpio setting for ldo, ldo2, sleep, drdy pin */ + if (etspi != NULL) { + etspi->drdy_irq_flag = DRDY_IRQ_DISABLE; + + if (etspi->ldo_pin) { + status = gpio_request(etspi->ldo_pin, "etspi_ldo_en"); + if (status < 0) { + pr_err("%s gpio_request etspi_ldo_en failed\n", + __func__); + goto etspi_platformInit_ldo_failed; + } + gpio_direction_output(etspi->ldo_pin, 0); + } + status = gpio_request(etspi->sleepPin, "etspi_sleep"); + if (status < 0) { + pr_err("%s gpio_requset etspi_sleep failed\n", + __func__); + goto etspi_platformInit_sleep_failed; + } + + gpio_direction_output(etspi->sleepPin, 0); + if (status < 0) { + pr_err("%s gpio_direction_output SLEEP failed\n", + __func__); + status = -EBUSY; + goto etspi_platformInit_sleep_failed; + } + + status = gpio_request(etspi->drdyPin, "etspi_drdy"); + if (status < 0) { + pr_err("%s gpio_request etspi_drdy failed\n", + __func__); + goto etspi_platformInit_drdy_failed; + } + + status = gpio_direction_input(etspi->drdyPin); + if (status < 0) { + pr_err("%s gpio_direction_input DRDY failed\n", + __func__); + goto etspi_platformInit_gpio_init_failed; + } + + pr_info("%s sleep value =%d\n" + "%s ldo en value =%d\n", + __func__, gpio_get_value(etspi->sleepPin), + __func__, gpio_get_value(etspi->ldo_pin)); + } else { + status = -EFAULT; + } + + +#ifdef ENABLE_SENSORS_FPRINT_SECURE + wake_lock_init(&etspi->fp_spi_lock, + WAKE_LOCK_SUSPEND, "etspi_wake_lock"); +#endif + wake_lock_init(&etspi->fp_signal_lock, + WAKE_LOCK_SUSPEND, "etspi_sigwake_lock"); + + pr_info("%s successful status=%d\n", __func__, status); + return status; +etspi_platformInit_gpio_init_failed: + gpio_free(etspi->drdyPin); +etspi_platformInit_drdy_failed: + gpio_free(etspi->sleepPin); +etspi_platformInit_sleep_failed: + gpio_free(etspi->ldo_pin); +etspi_platformInit_ldo_failed: + pr_err("%s is failed\n", __func__); + return status; +} + +void etspi_platformUninit(struct etspi_data *etspi) +{ + pr_info("%s\n", __func__); + + if (etspi != NULL) { + disable_irq_wake(gpio_irq); + disable_irq(gpio_irq); + etspi_pin_control(etspi, false); + free_irq(gpio_irq, etspi); + etspi->drdy_irq_flag = DRDY_IRQ_DISABLE; + if (etspi->ldo_pin) + gpio_free(etspi->ldo_pin); + gpio_free(etspi->sleepPin); + gpio_free(etspi->drdyPin); +#ifdef ENABLE_SENSORS_FPRINT_SECURE + wake_lock_destroy(&etspi->fp_spi_lock); +#endif + wake_lock_destroy(&etspi->fp_signal_lock); + } +} + +static int etspi_parse_dt(struct device *dev, + struct etspi_data *data) +{ + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + int errorno = 0; + int gpio; + + gpio = of_get_named_gpio_flags(np, "etspi-sleepPin", + 0, &flags); + if (gpio < 0) { + errorno = gpio; + goto dt_exit; + } else { + data->sleepPin = gpio; + pr_info("%s: sleepPin=%d\n", + __func__, data->sleepPin); + } + gpio = of_get_named_gpio_flags(np, "etspi-drdyPin", + 0, &flags); + if (gpio < 0) { + errorno = gpio; + goto dt_exit; + } else { + data->drdyPin = gpio; + pr_info("%s: drdyPin=%d\n", + __func__, data->drdyPin); + } + gpio = of_get_named_gpio_flags(np, "etspi-ldoPin", + 0, &flags); + if (gpio < 0) { + data->ldo_pin = 0; + pr_err("%s: fail to get ldo_pin\n", __func__); + } else { + data->ldo_pin = gpio; + pr_info("%s: ldo_pin=%d\n", + __func__, data->ldo_pin); + } + if (of_property_read_u32(np, "etspi-min_cpufreq_limit", + &data->min_cpufreq_limit)) + data->min_cpufreq_limit = 0; + + if (of_property_read_string_index(np, "etspi-chipid", 0, + (const char **)&data->chipid)) { + data->chipid = NULL; + } + pr_info("%s: chipid: %s\n", __func__, data->chipid); + + if (of_property_read_u32(np, "etspi-orient", &data->orient)) + data->orient = 0; + pr_info("%s: orient: %d\n", __func__, data->orient); + + data->p = pinctrl_get_select(dev, "default"); + if (IS_ERR(data->p)) { + errorno = -EINVAL; + pr_err("%s: failed pinctrl_get\n", __func__); + goto dt_exit; + } + + data->pins_sleep = pinctrl_lookup_state(data->p, "sleep"); + if (IS_ERR(data->pins_sleep)) { + errorno = -EINVAL; + pr_err("%s : could not get pins sleep_state (%li)\n", + __func__, PTR_ERR(data->pins_sleep)); + goto fail_pinctrl_get; + } + + data->pins_idle = pinctrl_lookup_state(data->p, "idle"); + if (IS_ERR(data->pins_idle)) { + errorno = -EINVAL; + pr_err("%s : could not get pins idle_state (%li)\n", + __func__, PTR_ERR(data->pins_idle)); + goto fail_pinctrl_get; + } + + etspi_pin_control(data, false); + pr_info("%s is successful\n", __func__); + return errorno; +fail_pinctrl_get: + pinctrl_put(data->p); +dt_exit: + pr_err("%s is failed\n", __func__); + return errorno; +} + +static const struct file_operations etspi_fops = { + .owner = THIS_MODULE, + .write = etspi_write, + .read = etspi_read, + .unlocked_ioctl = etspi_ioctl, + .compat_ioctl = etspi_compat_ioctl, + .open = etspi_open, + .release = etspi_release, + .llseek = no_llseek, + .poll = etspi_fps_interrupt_poll +}; + +#ifndef ENABLE_SENSORS_FPRINT_SECURE +static int etspi_type_check(struct etspi_data *etspi) +{ + u8 buf1, buf2, buf3, buf4, buf5, buf6, buf7; + + etspi_power_control(g_data, 1); + + msleep(20); + + etspi_read_register(etspi, 0x00, &buf1); + if (buf1 != 0xAA) { + etspi->sensortype = SENSOR_FAILED; + pr_info("%s sensor not ready, status = %x\n", __func__, buf1); + etspi_power_control(g_data, 0); + return -ENODEV; + } + + etspi_read_register(etspi, 0xFD, &buf1); + etspi_read_register(etspi, 0xFE, &buf2); + etspi_read_register(etspi, 0xFF, &buf3); + + etspi_read_register(etspi, 0x20, &buf4); + etspi_read_register(etspi, 0x21, &buf5); + etspi_read_register(etspi, 0x23, &buf6); + etspi_read_register(etspi, 0x24, &buf7); + + etspi_power_control(g_data, 0); + + pr_info("%s buf1-7: %x, %x, %x, %x, %x, %x, %x\n", + __func__, buf1, buf2, buf3, buf4, buf5, buf6, buf7); + /* + * type check return value + * ET510C : 0X00 / 0X66 / 0X00 / 0X33 + * ET510D : 0x03 / 0x0A / 0x05 + * ET516A : 0x00 / 0x10 / 0x05 + * ET516B : 0x01 or 0x02 / 0x10 / 0x05 + * ET520 : 0x03 / 0x14 / 0x05 + * ET520E : 0x04 / 0x14 / 0x05 + * ET523 : 0x00 / 0x17 / 0x05 + */ + if ((buf1 == 0x00) && (buf2 == 0x10) && (buf3 == 0x05)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET516A sensor\n", __func__); + } else if (((buf1 == 0x01) || (buf1 == 0x02)) + && (buf2 == 0x10) && (buf3 == 0x05)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET516B sensor\n", __func__); + } else if ((buf1 == 0x03) && (buf2 == 0x0A) && (buf3 == 0x05)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET510D sensor\n", __func__); + } else if ((buf1 == 0x03) && (buf2 == 0x14) && (buf3 == 0x05)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET520 sensor\n", __func__); + } else if ((buf1 == 0x04) && (buf2 == 0x14) && (buf3 == 0x05)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET520E sensor\n", __func__); + } else if((buf1 == 0x00) && (buf2 == 0x17) && (buf3 == 0x05)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET523 sensor\n", __func__); + } else { + if ((buf4 == 0x00) && (buf5 == 0x66) + && (buf6 == 0x00) && (buf7 == 0x33)) { + etspi->sensortype = SENSOR_EGIS; + pr_info("%s sensor type is EGIS ET510C sensor\n", + __func__); + } else { + etspi->sensortype = SENSOR_FAILED; + pr_info("%s sensor type is FAILED\n", __func__); + return -ENODEV; + } + } + return 0; +} +#endif + +static ssize_t etspi_bfs_values_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etspi_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "\"FP_SPICLK\":\"%d\"\n", + data->spi->max_speed_hz); +} + +static ssize_t etspi_type_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etspi_data *data = dev_get_drvdata(dev); +#ifndef ENABLE_SENSORS_FPRINT_SECURE + int retry = 0; + int status = 0; + + do { + status = etspi_type_check(data); + pr_info("%s type (%u), retry (%d)\n" + , __func__, data->sensortype, retry); + } while (!data->sensortype && ++retry < 3); + + if (status == -ENODEV) + pr_info("%s type check fail\n", __func__); +#endif + return snprintf(buf, PAGE_SIZE, "%d\n", data->sensortype); +} + +static ssize_t etspi_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t etspi_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", g_data->chipid); +} + +static ssize_t etspi_adm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", DETECT_ADM); +} + +static ssize_t etspi_intcnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etspi_data *data = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", data->interrupt_count); +} + +static ssize_t etspi_intcnt_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct etspi_data *data = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "c")) { + data->interrupt_count = 0; + pr_info("initialization is done\n"); + } + return size; +} + +static ssize_t etspi_resetcnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etspi_data *data = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", data->reset_count); +} + +static ssize_t etspi_resetcnt_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct etspi_data *data = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "c")) { + data->reset_count = 0; + pr_info("initialization is done\n"); + } + return size; +} + +static DEVICE_ATTR(bfs_values, 0444, etspi_bfs_values_show, NULL); +static DEVICE_ATTR(type_check, 0444, etspi_type_check_show, NULL); +static DEVICE_ATTR(vendor, 0444, etspi_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, etspi_name_show, NULL); +static DEVICE_ATTR(adm, 0444, etspi_adm_show, NULL); +static DEVICE_ATTR(intcnt, 0664, etspi_intcnt_show, etspi_intcnt_store); +static DEVICE_ATTR(resetcnt, 0664, etspi_resetcnt_show, etspi_resetcnt_store); + +static struct device_attribute *fp_attrs[] = { + &dev_attr_bfs_values, + &dev_attr_type_check, + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_adm, + &dev_attr_intcnt, + &dev_attr_resetcnt, + NULL, +}; + +static void etspi_work_func_debug(struct work_struct *work) +{ + u8 ldo_value = 0; + + if (g_data->ldo_pin) + ldo_value = gpio_get_value(g_data->ldo_pin); + + pr_info("%s ldo: %d, sleep: %d, tz: %d, spi_value: 0x%x, type: %s\n", + __func__, + ldo_value, gpio_get_value(g_data->sleepPin), + g_data->tz_mode, g_data->spi_value, + sensor_status[g_data->sensortype + 2]); +} + +static void etspi_enable_debug_timer(void) +{ + mod_timer(&g_data->dbg_timer, + round_jiffies_up(jiffies + FPSENSOR_DEBUG_TIMER_SEC)); +} + +static void etspi_disable_debug_timer(void) +{ + del_timer_sync(&g_data->dbg_timer); + cancel_work_sync(&g_data->work_debug); +} + +static void etspi_timer_func(unsigned long ptr) +{ + queue_work(g_data->wq_dbg, &g_data->work_debug); + mod_timer(&g_data->dbg_timer, + round_jiffies_up(jiffies + FPSENSOR_DEBUG_TIMER_SEC)); +} + +static int etspi_set_timer(struct etspi_data *etspi) +{ + int status = 0; + + setup_timer(&etspi->dbg_timer, + etspi_timer_func, (unsigned long)etspi); + etspi->wq_dbg = + create_singlethread_workqueue("etspi_debug_wq"); + if (!etspi->wq_dbg) { + status = -ENOMEM; + pr_err("%s could not create workqueue\n", __func__); + return status; + } + INIT_WORK(&etspi->work_debug, etspi_work_func_debug); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static struct class *etspi_class; + +/*-------------------------------------------------------------------------*/ + +static int etspi_probe(struct spi_device *spi) +{ + struct etspi_data *etspi; + int status; + unsigned long minor; +#ifndef ENABLE_SENSORS_FPRINT_SECURE + int retry = 0; +#endif + pr_info("%s\n", __func__); + + /* Allocate driver data */ + etspi = kzalloc(sizeof(*etspi), GFP_KERNEL); + if (!etspi) + return -ENOMEM; + + /* device tree call */ + if (spi->dev.of_node) { + status = etspi_parse_dt(&spi->dev, etspi); + if (status) { + pr_err("%s - Failed to parse DT\n", __func__); + goto etspi_probe_parse_dt_failed; + } + } + + /* Initialize the driver data */ + etspi->spi = spi; + g_data = etspi; + + spin_lock_init(&etspi->spi_lock); + mutex_init(&etspi->buf_lock); + mutex_init(&device_list_lock); + + INIT_LIST_HEAD(&etspi->device_entry); + + /* platform init */ + status = etspi_platformInit(etspi); + if (status != 0) { + pr_err("%s platforminit failed\n", __func__); + goto etspi_probe_platformInit_failed; + } + + spi->bits_per_word = 8; + spi->max_speed_hz = SLOW_BAUD_RATE; + spi->mode = SPI_MODE_0; + spi->chip_select = 0; +#ifndef ENABLE_SENSORS_FPRINT_SECURE + status = spi_setup(spi); + if (status != 0) { + pr_err("%s spi_setup() is failed. status : %d\n", + __func__, status); + return status; + } +#endif + etspi->spi_value = 0; + +#ifdef ENABLE_SENSORS_FPRINT_SECURE + etspi->sensortype = SENSOR_UNKNOWN; +#else + /* sensor hw type check */ + do { + status = etspi_type_check(etspi); + pr_info("%s type (%u), retry (%d)\n" + , __func__, etspi->sensortype, retry); + } while (!etspi->sensortype && ++retry < 3); + + if (status == -ENODEV) + pr_info("%s type check fail\n", __func__); +#endif + +#ifdef ENABLE_SENSORS_FPRINT_SECURE + etspi->tz_mode = true; +#endif + etspi->reset_count = 0; + etspi->interrupt_count = 0; + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + //delete this condition to make device live always + //if (etspi->sensortype != SENSOR_FAILED) { + + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + + etspi->devt = MKDEV(ET5XX_MAJOR, minor); + dev = device_create(etspi_class, &spi->dev, + etspi->devt, etspi, "esfp0"); + status = IS_ERR(dev) ? PTR_ERR(dev) : 0; + } else { + dev_dbg(&spi->dev, "no minor number available!\n"); + status = -ENODEV; + } + if (status == 0) { + set_bit(minor, minors); + list_add(&etspi->device_entry, &device_list); + } + mutex_unlock(&device_list_lock); + + if (status == 0) + spi_set_drvdata(spi, etspi); + else + goto etspi_probe_failed; + + status = fingerprint_register(etspi->fp_device, + etspi, fp_attrs, "fingerprint"); + if (status) { + pr_err("%s sysfs register failed\n", __func__); + goto etspi_probe_failed; + } + + status = etspi_set_timer(etspi); + if (status) + goto etspi_sysfs_failed; + etspi_enable_debug_timer(); + + pr_info("%s is successful\n", __func__); + + return status; + +etspi_sysfs_failed: + fingerprint_unregister(etspi->fp_device, fp_attrs); + +etspi_probe_failed: + device_destroy(etspi_class, etspi->devt); + class_destroy(etspi_class); + etspi_platformUninit(etspi); +etspi_probe_platformInit_failed: +etspi_probe_parse_dt_failed: + kfree(etspi); + pr_err("%s is failed\n", __func__); + + return status; +} + +static int etspi_remove(struct spi_device *spi) +{ + struct etspi_data *etspi = spi_get_drvdata(spi); + + pr_info("%s\n", __func__); + + if (etspi != NULL) { + etspi_disable_debug_timer(); + etspi_platformUninit(etspi); + + /* make sure ops on existing fds can abort cleanly */ + spin_lock_irq(&etspi->spi_lock); + etspi->spi = NULL; + spi_set_drvdata(spi, NULL); + spin_unlock_irq(&etspi->spi_lock); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + fingerprint_unregister(etspi->fp_device, fp_attrs); + + list_del(&etspi->device_entry); + device_destroy(etspi_class, etspi->devt); + clear_bit(MINOR(etspi->devt), minors); + if (etspi->users == 0) + kfree(etspi); + mutex_unlock(&device_list_lock); + } + return 0; +} + +static int etspi_pm_suspend(struct device *dev) +{ + pr_info("%s\n", __func__); + + if (g_data != NULL) { + etspi_disable_debug_timer(); + if (!g_data->drdy_irq_flag) { + g_data->drdy_irq_flag = DRDY_IRQ_DISABLE; + } + } + return 0; +} + +static int etspi_pm_resume(struct device *dev) +{ + pr_info("%s\n", __func__); + if (g_data != NULL) { + etspi_enable_debug_timer(); + } + return 0; +} + +static const struct dev_pm_ops etspi_pm_ops = { + .suspend = etspi_pm_suspend, + .resume = etspi_pm_resume +}; + +static const struct of_device_id etspi_match_table[] = { + { .compatible = "etspi,et5xx",}, + {}, +}; + +static struct spi_driver etspi_spi_driver = { + .driver = { + .name = "egis_fingerprint", + .owner = THIS_MODULE, + .pm = &etspi_pm_ops, + .of_match_table = etspi_match_table + }, + .probe = etspi_probe, + .remove = etspi_remove, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init etspi_init(void) +{ + int status; + + pr_info("%s\n", __func__); + + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(ET5XX_MAJOR, "egis_fingerprint", &etspi_fops); + if (status < 0) { + pr_err("%s register_chrdev error.status:%d\n", __func__, + status); + return status; + } + + etspi_class = class_create(THIS_MODULE, "egis_fingerprint"); + if (IS_ERR(etspi_class)) { + pr_err("%s class_create error.\n", __func__); + unregister_chrdev(ET5XX_MAJOR, etspi_spi_driver.driver.name); + return PTR_ERR(etspi_class); + } + + status = spi_register_driver(&etspi_spi_driver); + if (status < 0) { + pr_err("%s spi_register_driver error.\n", __func__); + class_destroy(etspi_class); + unregister_chrdev(ET5XX_MAJOR, etspi_spi_driver.driver.name); + return status; + } + + pr_info("%s is successful\n", __func__); + + return status; +} + +static void __exit etspi_exit(void) +{ + pr_info("%s\n", __func__); + + spi_unregister_driver(&etspi_spi_driver); + class_destroy(etspi_class); + unregister_chrdev(ET5XX_MAJOR, etspi_spi_driver.driver.name); +} + +module_init(etspi_init); +module_exit(etspi_exit); + +MODULE_AUTHOR("Wang YuWei, "); +MODULE_DESCRIPTION("SPI Interface for ET5XX"); +MODULE_LICENSE("GPL"); diff --git a/drivers/fingerprint/et5xx-spi_data_transfer.c b/drivers/fingerprint/et5xx-spi_data_transfer.c new file mode 100755 index 000000000000..d15a41dabbf9 --- /dev/null +++ b/drivers/fingerprint/et5xx-spi_data_transfer.c @@ -0,0 +1,1056 @@ +/* + * Copyright (C) 2016 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 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 +#include +#include +#include + +#include "et5xx.h" + + +int etspi_io_burst_write_register(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + struct spi_message m; + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .len = ioc->len + 1, + }; + + if (ioc->len <= 0 || ioc->len + 2 > etspi->bufsiz) { + status = -ENOMEM; + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + + memset(etspi->buf, 0, ioc->len + 1); + *etspi->buf = OP_REG_W_C; + if (copy_from_user(etspi->buf + 1, + (const u8 __user *) (uintptr_t) ioc->tx_buf, + ioc->len)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + goto end; + } + pr_debug("%s tx_buf = %p op = %x reg = %x, len = %d\n", __func__, + ioc->tx_buf, *etspi->buf, *(etspi->buf + 1), xfer.len); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status < 0) { + pr_err("%s error status = %d\n", __func__, status); + goto end; + } +end: + return status; +#endif +} + +int etspi_io_burst_write_register_backward(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + struct spi_message m; + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .len = ioc->len + 1, + }; + + if (ioc->len <= 0 || ioc->len + 2 > etspi->bufsiz) { + status = -ENOMEM; + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + + memset(etspi->buf, 0, ioc->len + 1); + *etspi->buf = OP_REG_W_C_BW; + if (copy_from_user(etspi->buf + 1, + (const u8 __user *) (uintptr_t)ioc->tx_buf, ioc->len)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + goto end; + } + pr_debug("%s tx_buf = %p op = %x reg = %x, len = %d\n", __func__, + ioc->tx_buf, *etspi->buf, *(etspi->buf + 1), xfer.len); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status < 0) { + pr_err("%s error status = %d\n", __func__, status); + goto end; + } +end: + return status; +#endif +} + +int etspi_io_burst_read_register(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + struct spi_message m; + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .rx_buf = etspi->buf, + .len = ioc->len + 2, + }; + + if (ioc->len <= 0 || ioc->len + 2 > etspi->bufsiz) { + status = -ENOMEM; + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + + memset(etspi->buf, 0, xfer.len); + *etspi->buf = OP_REG_R_C; + if (copy_from_user(etspi->buf + 1, + (const u8 __user *) (uintptr_t) ioc->tx_buf, 1)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + goto end; + } + pr_debug("%s tx_buf = %p op = %x reg = %x, len = %d\n", __func__, + ioc->tx_buf, *etspi->buf, *(etspi->buf + 1), xfer.len); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status < 0) { + status = -ENOMEM; + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + + if (copy_to_user((u8 __user *) (uintptr_t)ioc->rx_buf, etspi->buf + 2, + ioc->len)) { + status = -EFAULT; + pr_err("%s buffer copy_to_user fail status\n", __func__); + goto end; + } +end: + return status; +#endif +} + +int etspi_io_burst_read_register_backward(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + struct spi_message m; + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .rx_buf = etspi->buf, + .len = ioc->len + 2, + }; + + if (ioc->len <= 0 || ioc->len + 2 > etspi->bufsiz) { + status = -ENOMEM; + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + + memset(etspi->buf, 0, xfer.len); + *etspi->buf = OP_REG_R_C_BW; + if (copy_from_user(etspi->buf + 1, + (const u8 __user *) (uintptr_t)ioc->tx_buf, 1)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + goto end; + } + pr_debug("%s tx_buf = %p op = %x reg = %x, len = %d\n", __func__, + ioc->tx_buf, *etspi->buf, *(etspi->buf + 1), xfer.len); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status < 0) { + status = -ENOMEM; + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + + if (copy_to_user((u8 __user *) (uintptr_t)ioc->rx_buf, etspi->buf + 2, + ioc->len)) { + status = -EFAULT; + pr_err("%s buffer copy_to_user fail status\n", __func__); + goto end; + } +end: + return status; +#endif +} + +int etspi_io_read_registerex(struct etspi_data *etspi, u8 *addr, u8 *buf, + u32 len) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + struct spi_message m; + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .rx_buf = etspi->buf, + .len = len + 2, + }; + + if (len <= 0 || len + 2 > etspi->bufsiz) { + status = -ENOMEM; + pr_err("%s error status = %d", __func__, status); + goto end; + } + + memset(etspi->buf, 0, xfer.len); + *etspi->buf = OP_REG_R; + + if (copy_from_user(etspi->buf + 1, (const u8 __user *) (uintptr_t) addr + , 1)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + goto end; + } + + pr_debug("%s addr = %p op = %x reg = %x len = %d tx = %p, rx = %p", + __func__, addr, etspi->buf[0], etspi->buf[1], len, + xfer.tx_buf, xfer.rx_buf); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s read data error status = %d\n", __func__, status); + goto end; + } + + + if (copy_to_user((u8 __user *) (uintptr_t) buf, etspi->buf + 2, len)) { + pr_err("%s buffer copy_to_user fail status\n", __func__); + status = -EFAULT; + goto end; + } +end: + return status; +#endif +} + +/* Read io register */ +int etspi_io_read_register(struct etspi_data *etspi, u8 *addr, u8 *buf) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + struct spi_message m; + int read_len = 1; + + u8 val, addrval; + + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .rx_buf = etspi->buf, + .len = 3, + }; + + memset(etspi->buf, 0, xfer.len); + *etspi->buf = OP_REG_R; + + if (copy_from_user(&addrval, (const u8 __user *) (uintptr_t) addr + , read_len)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + return status; + } + + *(etspi->buf + 1) = addrval; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s read data error status = %d\n", __func__, status); + return status; + } + + val = *(etspi->buf + 2); + + pr_debug("%s len = %d addr = %x val = %x\n", __func__, + read_len, addrval, val); + + if (copy_to_user((u8 __user *) (uintptr_t) buf, &val, read_len)) { + pr_err("%s buffer copy_to_user fail status\n", __func__); + status = -EFAULT; + return status; + } + + return status; +#endif +} + +/* Write data to register */ +int etspi_io_write_register(struct etspi_data *etspi, u8 *buf) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status = 0; + int write_len = 2; + struct spi_message m; + + u8 val[3]; + + struct spi_transfer xfer = { + .tx_buf = etspi->buf, + .len = 3, + }; + + memset(etspi->buf, 0, xfer.len); + *etspi->buf = OP_REG_W; + + if (copy_from_user(val, (const u8 __user *) (uintptr_t) buf, + write_len)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + return status; + } + + pr_debug("%s write_len = %d addr = %x data = %x\n", __func__, + write_len, val[0], val[1]); + + *(etspi->buf + 1) = val[0]; + *(etspi->buf + 2) = val[1]; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s read data error status = %d\n", __func__, status); + return status; + } + + return status; +#endif +} + +int etspi_write_register(struct etspi_data *etspi, u8 addr, u8 buf) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + int i, buf_size; + struct spi_message m; + + u8 tx[] = {OP_REG_W, addr, buf}; + u8 *tx_buffer = NULL; + + struct spi_transfer xfer = { + .rx_buf = NULL, + .len = 3, + }; + + buf_size = 3; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + for (i = 0; i < buf_size; i++) + tx_buffer[i] = tx[i]; + xfer.tx_buf = tx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) { + DEBUG_PRINT("%s address = %x\n",__func__, addr); + } else { + pr_err("%s read data error status = %d\n", __func__, status); + } + + kfree(tx_buffer); + return status; +#endif +} +int etspi_read_register(struct etspi_data *etspi, u8 addr, u8 *buf) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + int i, buf_size; + struct spi_message m; + + u8 read_value[] = {OP_REG_R, addr, 0x00}; + u8 result[] = {0xFF, 0xFF, 0xFF}; + + u8 *tx_buffer = NULL; + u8 *rx_buffer = NULL; + + struct spi_transfer xfer = { + .len = 3, + }; + + buf_size = 3; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + rx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (rx_buffer == NULL) { + kfree(tx_buffer); + return -ENOMEM; + } + for (i = 0; i < buf_size; i++) { + tx_buffer[i] = read_value[i]; + rx_buffer[i] = result[i]; + } + + xfer.tx_buf = tx_buffer; + xfer.rx_buf = rx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) { + *buf = rx_buffer[2]; + DEBUG_PRINT("%s address = %x result = %x %x\n" + __func__, addr, rx_buffer[1], rx_buffer[2]); + } else { + pr_err("%s read data error status = %d\n", __func__, status); + } + + kfree(tx_buffer); + kfree(rx_buffer); + + return status; +#endif +} + +int etspi_io_nvm_read(struct etspi_data *etspi, struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + int i, buf_size; + struct spi_message m; + + u8 addr; /* nvm logical address */ + u8 buf[] = {OP_NVM_RE, 0x00}; + u8 *tx_buffer = NULL; + + struct spi_transfer xfer = { + .rx_buf = NULL, + .len = 2, + }; + + buf_size = 2; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + for (i = 0; i < buf_size; i++) + tx_buffer[i] = buf[i]; + xfer.tx_buf = tx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) + DEBUG_PRINT("%s nvm enabled\n", __func__); + else + pr_err("%s nvm enable error status = %d\n", __func__, status); + + kfree(tx_buffer); + + usleep_range(10, 50); + + if (copy_from_user(&addr, (const u8 __user *) (uintptr_t) ioc->tx_buf + , 1)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + return status; + } + + etspi->buf[0] = OP_NVM_ON_R; + + pr_debug("%s logical addr(%x) len(%d)\n", __func__, addr, ioc->len); + if ((addr + ioc->len) > MAX_NVM_LEN) + return -EINVAL; + + /* transfer to nvm physical address*/ + etspi->buf[1] = ((addr % 2) ? (addr - 1) : addr) / 2; + /* thansfer to nvm physical length */ + xfer.len = ((ioc->len % 2) ? ioc->len + 1 : + (addr % 2 ? ioc->len + 2 : ioc->len)) + 3; + if (xfer.len >= LARGE_SPI_TRANSFER_BUFFER) { + if ((xfer.len) % DIVISION_OF_IMAGE != 0) + xfer.len = xfer.len + (DIVISION_OF_IMAGE - + (xfer.len % DIVISION_OF_IMAGE)); + } + xfer.tx_buf = xfer.rx_buf = etspi->buf; + + pr_debug("%s nvm read addr(%d) len(%d) xfer.rx_buf(%p), etspi->buf(%p)\n", + __func__, etspi->buf[1], + xfer.len, xfer.rx_buf, etspi->buf); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s error status = %d\n", __func__, status); + return status; + } + + if (copy_to_user((u8 __user *) (uintptr_t) ioc->rx_buf, xfer.rx_buf + 3 + , ioc->len)) { + pr_err("%s buffer copy_to_user fail status\n", __func__); + status = -EFAULT; + return status; + } + + return status; +#endif +} + +int etspi_io_nvm_write(struct etspi_data *etspi, struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status, i, j, len/* physical nvm length */; + int buf_size; + struct spi_message m; + u8 *bufw = NULL; + u8 buf[MAX_NVM_LEN + 1] = {OP_NVM_WE, 0x00}; + u8 addr/* nvm physical addr */; + u8 *tx_buffer = NULL; + + struct spi_transfer xfer = { + .rx_buf = NULL, + .len = 2, + }; + + if (ioc->len > (MAX_NVM_LEN + 1)) + return -EINVAL; + + buf_size = MAX_NVM_LEN+1; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + for (i = 0; i < buf_size; i++) + tx_buffer[i] = buf[i]; + + xfer.tx_buf = tx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) + DEBUG_PRINT("%s nvm enabled\n", __func__); + else + pr_err("%s nvm enable error status = %d\n", __func__, status); + + kfree(tx_buffer); + + usleep_range(10, 50); + + pr_debug("%s buf(%p) tx_buf(%p) len(%d)\n", __func__, buf, + ioc->tx_buf, ioc->len); + if (copy_from_user(buf, (const u8 __user *) (uintptr_t) ioc->tx_buf, + ioc->len)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + return status; + } + + if ((buf[0] + (ioc->len - 1)) > MAX_NVM_LEN) + return -EINVAL; + if ((buf[0] % 2) || ((ioc->len - 1) % 2)) { + /* TODO: add non alignment handling */ + pr_err("%s can't handle address alignment issue. %d %d\n", + __func__, buf[0], ioc->len); + return -EINVAL; + } + + bufw = kzalloc(NVM_WRITE_LENGTH, GFP_KERNEL | GFP_DMA); + /*TODO: need to dynamic assign nvm length*/ + if (bufw == NULL) { + status = -ENOMEM; + pr_err("%s bufw kmalloc error\n", __func__); + return status; + } + xfer.tx_buf = xfer.rx_buf = bufw; + xfer.len = NVM_WRITE_LENGTH; + + len = (ioc->len - 1) / 2; + pr_debug("%s nvm write addr(%d) len(%d) xfer.tx_buf(%p), etspi->buf(%p)\n", + __func__, buf[0], len, xfer.tx_buf, etspi->buf); + for (i = 0, addr = buf[0] / 2/* thansfer to nvm physical length */; + i < len; i++) { + bufw[0] = OP_NVM_ON_W; + bufw[1] = addr++; + bufw[2] = buf[i * 2 + 1]; + bufw[3] = buf[i * 2 + 2]; + memset(bufw + 4, 1, NVM_WRITE_LENGTH - 4); + + pr_debug("%s write transaction (%d): %x %x %x %x\n", + __func__, i, + bufw[0], bufw[1], bufw[2], bufw[3]); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + for (j = 0; j < NVM_WRITE_LENGTH - 4; j++) { + if (bufw[4 + j] == 0) { + pr_debug("%s nvm write ready(%d)\n", + __func__, j); + break; + } + if (j == NVM_WRITE_LENGTH - 5) { + pr_err("%s nvm write fail(timeout)\n", + __func__); + status = -EIO; + goto end; + } + } + } +end: + kfree(bufw); + return status; +#endif +} + +int etspi_nvm_read(struct etspi_data *etspi, struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + int i, buf_size; + struct spi_message m; + + u8 addr; /* nvm logical address */ + u8 buf[] = {OP_NVM_RE, 0x00}; + u8 *tx_buffer = NULL; + + struct spi_transfer xfer = { + .rx_buf = NULL, + .len = 2, + }; + + buf_size = 2; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + for (i = 0; i < buf_size; i++) + tx_buffer[i] = buf[i]; + + xfer.tx_buf = tx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) + DEBUG_PRINT("%s nvm enabled\n", __func__); + else + pr_err("%s nvm enable error status = %d\n", __func__, status); + + kfree(tx_buffer); + + usleep_range(10, 50); + + addr = ioc->tx_buf[0]; + + etspi->buf[0] = OP_NVM_ON_R; + + pr_debug("%s logical addr(%x) len(%d)\n", __func__, addr, ioc->len); + if ((addr + ioc->len) > MAX_NVM_LEN) + return -EINVAL; + + /* transfer to nvm physical address*/ + etspi->buf[1] = ((addr % 2) ? (addr - 1) : addr) / 2; + /* thansfer to nvm physical length */ + xfer.len = ((ioc->len % 2) ? ioc->len + 1 : + (addr % 2 ? ioc->len + 2 : ioc->len)) + 3; + if (xfer.len >= LARGE_SPI_TRANSFER_BUFFER) { + if ((xfer.len) % DIVISION_OF_IMAGE != 0) + xfer.len = xfer.len + (DIVISION_OF_IMAGE - + (xfer.len % DIVISION_OF_IMAGE)); + } + xfer.tx_buf = xfer.rx_buf = etspi->buf; + + pr_debug("%s nvm read addr(%d) len(%d) xfer.rx_buf(%p), etspi->buf(%p)\n", + __func__, etspi->buf[1], + xfer.len, xfer.rx_buf, etspi->buf); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s error status = %d\n", __func__, status); + return status; + } + + if (memcpy((u8 __user *) (uintptr_t) ioc->rx_buf, xfer.rx_buf + 3, + ioc->len)) { + pr_err("%s buffer copy_to_user fail status\n", __func__); + status = -EFAULT; + return status; + } + + return status; +#endif +} +int etspi_io_nvm_writeex(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status, i, j, len/* physical nvm length */, wlen; + int buf_size; + struct spi_message m; + u8 *bufw = NULL; + u8 bufr[MAX_NVM_LEN + 3]; + u8 buf[MAX_NVM_LEN + 3] = {OP_NVM_WE, 0x00}; + u8 addr/* nvm physical addr */, *tmp = NULL; + u8 *tx_buffer = NULL; + struct egis_ioc_transfer r; + + struct spi_transfer xfer = { + .rx_buf = NULL, + .len = 2, + }; + + pr_debug("%s buf(%p) tx_buf(%p) len(%d)\n", __func__, + buf, ioc->tx_buf, ioc->len); + if (copy_from_user(buf, (const u8 __user *) (uintptr_t) ioc->tx_buf + , ioc->len)) { + pr_err("%s buffer copy_from_user fail\n", __func__); + status = -EFAULT; + return status; + } + + if ((buf[0] + (ioc->len - 3)) > MAX_NVM_LEN) + return -EINVAL; + if ((buf[0] % 2) || ((ioc->len - 3) % 2)) { + /* address non-alignment handling */ + pr_debug("%s handle address alignment issue. %d %d\n", + __func__, buf[0], ioc->len); + + r.tx_buf = r.rx_buf = bufr; + r.len = ioc->len; + if (buf[0] % 2) { + r.tx_buf[0] = buf[0] - 1; + r.len = ioc->len % 2 ? r.len + 1 : r.len + 2; + } else { + if (ioc->len % 2) + r.len++; + } + pr_debug("%s fixed address alignment issue. %d %d\n", + __func__, r.tx_buf[0], r.len); + etspi_nvm_read(etspi, &r); + + tmp = bufr; + if (buf[0] % 2) + tmp++; + memcpy(tmp, buf, ioc->len); + } + + buf[0] = OP_NVM_WE; + + buf_size = MAX_NVM_LEN+1; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + for (i = 0; i < buf_size; i++) + tx_buffer[i] = buf[i]; + xfer.tx_buf = tx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) + DEBUG_PRINT("%s nvm enabled\n", __func__); + else + pr_err("%s nvm enable error status = %d\n", __func__, status); + + kfree(tx_buffer); + + usleep_range(10, 50); + + wlen = *(u16 *)(buf + 1); + pr_debug("%s wlen(%d)\n", __func__, wlen); + if (wlen > 8192) + wlen = 8196; + bufw = kzalloc(wlen, GFP_KERNEL | GFP_DMA); + if (bufw == NULL) { + status = -ENOMEM; + pr_err("%s bufw kmalloc error\n", __func__); + return status; + } + xfer.tx_buf = xfer.rx_buf = bufw; + xfer.len = wlen; + + if ((buf[0] % 2) || ((ioc->len - 3) % 2)) { + memcpy(buf, bufr, r.len); + ioc->len = r.len; + } + len = (ioc->len - 3) / 2; + pr_debug("%s nvm write addr(%d) len(%d) xfer.tx_buf(%p), etspi->buf(%p), wlen(%d)\n", + __func__, buf[0], len, xfer.tx_buf, etspi->buf, wlen); + for (i = 0, addr = buf[0] / 2/* thansfer to nvm physical length */; + i < len; i++) { + bufw[0] = OP_NVM_ON_W; + bufw[1] = addr++; + bufw[2] = buf[i * 2 + 3]; + bufw[3] = buf[i * 2 + 4]; + memset(bufw + 4, 1, wlen - 4); + + pr_debug("%s write transaction (%d): %x %x %x %x\n", + __func__, i, + bufw[0], bufw[1], bufw[2], bufw[3]); + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s error status = %d\n", __func__, status); + goto end; + } + for (j = 0; j < wlen - 4; j++) { + if (bufw[4 + j] == 0) { + pr_debug("%s nvm write ready(%d)\n", + __func__, j); + break; + } + if (j == wlen - 5) { + pr_err("%s nvm write fail(timeout)\n", + __func__); + status = -EIO; + goto end; + } + } + } +end: + kfree(bufw); + return status; +#endif +} + +int etspi_io_nvm_off(struct etspi_data *etspi, struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + int i, buf_size; + struct spi_message m; + + u8 buf[] = {OP_NVM_OFF, 0x00}; + u8 *tx_buffer = NULL; + + struct spi_transfer xfer = { + .rx_buf = NULL, + .len = 2, + }; + + buf_size = 2; + tx_buffer = kzalloc(buf_size*sizeof(u8), GFP_KERNEL | GFP_DMA); + if (tx_buffer == NULL) + return -ENOMEM; + for (i = 0; i < buf_size; i++) + tx_buffer[i] = buf[i]; + xfer.tx_buf = tx_buffer; + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status == 0) + DEBUG_PRINT("%s nvm disabled\n", __func__); + else + pr_err("%s nvm disable error status = %d\n", __func__, status); + + kfree(tx_buffer); + return status; +#endif +} +int etspi_io_vdm_read(struct etspi_data *etspi, struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + struct spi_message m; + u8 *buf = NULL; + + struct spi_transfer xfer = { + .tx_buf = NULL, + .rx_buf = NULL, + .len = ioc->len + 1, + }; + + if (xfer.len >= LARGE_SPI_TRANSFER_BUFFER) { + if ((xfer.len) % DIVISION_OF_IMAGE != 0) + xfer.len = xfer.len + (DIVISION_OF_IMAGE - + (xfer.len % DIVISION_OF_IMAGE)); + } + + buf = kzalloc(xfer.len, GFP_KERNEL | GFP_DMA); + + if (buf == NULL) + return -ENOMEM; + + xfer.tx_buf = xfer.rx_buf = buf; + buf[0] = OP_VDM_R; + + pr_debug("%s len = %d, xfer.len = %d, buf = %p, rx_buf = %p\n", + __func__, ioc->len, xfer.len, buf, ioc->rx_buf); + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s read data error status = %d\n", __func__, status); + goto end; + } + + if (copy_to_user((u8 __user *) (uintptr_t) ioc->rx_buf, buf + 1, + ioc->len)) { + pr_err("%s buffer copy_to_user fail status\n", __func__); + status = -EFAULT; + } +end: + kfree(buf); + return status; +#endif +} + +int etspi_io_vdm_write(struct etspi_data *etspi, struct egis_ioc_transfer *ioc) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + struct spi_message m; + u8 *buf = NULL; + + struct spi_transfer xfer = { + .tx_buf = NULL, + .rx_buf = NULL, + .len = ioc->len + 1, + }; + + if (xfer.len >= LARGE_SPI_TRANSFER_BUFFER) { + if ((xfer.len) % DIVISION_OF_IMAGE != 0) + xfer.len = xfer.len + (DIVISION_OF_IMAGE - + (xfer.len % DIVISION_OF_IMAGE)); + } + + buf = kzalloc(xfer.len, GFP_KERNEL | GFP_DMA); + if (buf == NULL) + return -ENOMEM; + + if (copy_from_user((u8 __user *) (uintptr_t) buf + 1, ioc->tx_buf, + ioc->len)) { + pr_err("buffer copy_from_user fail status\n"); + status = -EFAULT; + goto end; + } + + xfer.tx_buf = xfer.rx_buf = buf; + buf[0] = OP_VDM_W; + + pr_debug("%s len = %d, xfer.len = %d, buf = %p, tx_buf = %p\n", + __func__, ioc->len, xfer.len, buf, ioc->tx_buf); + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + + if (status < 0) + pr_err("%s read data error status = %d\n", __func__, status); +end: + kfree(buf); + return status; +#endif +} + +int etspi_io_get_frame(struct etspi_data *etspi, u8 *fr, u32 size) +{ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + return 0; +#else + int status; + struct spi_message m; + u8 *buf = NULL; + + struct spi_transfer xfer = { + .tx_buf = NULL, + .rx_buf = NULL, + .len = size + 1, + }; + + if (xfer.len >= LARGE_SPI_TRANSFER_BUFFER) { + if ((xfer.len) % DIVISION_OF_IMAGE != 0) + xfer.len = xfer.len + (DIVISION_OF_IMAGE - + (xfer.len % DIVISION_OF_IMAGE)); + } + + buf = kzalloc(xfer.len, GFP_KERNEL | GFP_DMA); + + if (buf == NULL) + return -ENOMEM; + + xfer.tx_buf = xfer.rx_buf = buf; + buf[0] = OP_IMG_R; + + pr_debug("%s size = %d, xfer.len = %d, buf = %p, fr = %p\n", __func__, + size, xfer.len, buf, fr); + + spi_message_init(&m); + spi_message_add_tail(&xfer, &m); + status = spi_sync(etspi->spi, &m); + if (status < 0) { + pr_err("%s read data error status = %d\n", __func__, status); + goto end; + } + + if (copy_to_user((u8 __user *) (uintptr_t) fr, buf + 1, size)) { + pr_err("%s buffer copy_to_user fail status\n", __func__); + status = -EFAULT; + } +end: + kfree(buf); + return status; +#endif +} diff --git a/drivers/fingerprint/et5xx.h b/drivers/fingerprint/et5xx.h new file mode 100755 index 000000000000..2d426d9b4656 --- /dev/null +++ b/drivers/fingerprint/et5xx.h @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2016 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 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 _ET5XX_LINUX_DIRVER_H_ +#define _ET5XX_LINUX_DIRVER_H_ + +#include +#include +#include +#ifdef ENABLE_SENSORS_FPRINT_SECURE +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include "../pinctrl/core.h" +#include + +/*#define ET5XX_SPI_DEBUG*/ + +#ifdef ET5XX_SPI_DEBUG +#define DEBUG_PRINT(fmt, args...) pr_err(fmt, ## args) +#else +#define DEBUG_PRINT(fmt, args...) +#endif + +#define VENDOR "EGISTEC" +#define CHIP_ID "ET5XX" + +/* assigned */ +#define ET5XX_MAJOR 152 +/* ... up to 256 */ +#define N_SPI_MINORS 32 + +#define OP_REG_R 0x20 +#define OP_REG_R_C 0x22 +#define OP_REG_R_C_BW 0x23 +#define OP_REG_W 0x24 +#define OP_REG_W_C 0x26 +#define OP_REG_W_C_BW 0x27 +#define OP_NVM_ON_R 0x40 +#define OP_NVM_ON_W 0x42 +#define OP_NVM_RE 0x44 +#define OP_NVM_WE 0x46 +#define OP_NVM_OFF 0x48 +#define OP_IMG_R 0x50 +#define OP_VDM_R 0x60 +#define OP_VDM_W 0x62 +#define BITS_PER_WORD 8 + +#define SLOW_BAUD_RATE 12500000 + +#define DRDY_IRQ_ENABLE 1 +#define DRDY_IRQ_DISABLE 0 + +#define ET5XX_INT_DETECTION_PERIOD 10 +#define ET5XX_DETECTION_THRESHOLD 10 + +#define FP_REGISTER_READ 0x01 +#define FP_REGISTER_WRITE 0x02 +#define FP_GET_ONE_IMG 0x03 +#define FP_SENSOR_RESET 0x04 +#define FP_POWER_CONTROL 0x05 +#define FP_SET_SPI_CLOCK 0x06 +#define FP_RESET_SET 0x07 +#define FP_REGISTER_BREAD 0x20 +#define FP_REGISTER_BWRITE 0x21 +#define FP_REGISTER_MREAD 0x22 +#define FP_REGISTER_MWRITE 0x23 +#define FP_REGISTER_BREAD_BACKWARD 0x24 +#define FP_REGISTER_BWRITE_BACKWARD 0x25 +#define FP_VDM_READ 0x30 +#define FP_VDM_WRITE 0x31 +#define FP_NVM_READ 0X40 +#define FP_NVM_WRITE 0x41 +#define FP_NVM_OFF 0x42 +#define FP_NVM_WRITEEX 0x43 + +#ifdef ENABLE_SENSORS_FPRINT_SECURE +#define FP_DISABLE_SPI_CLOCK 0x10 +#define FP_CPU_SPEEDUP 0x11 +#define FP_SET_SENSOR_TYPE 0x14 +/* Do not use ioctl number 0x15 */ +#define FP_SET_LOCKSCREEN 0x16 +#define FP_SET_WAKE_UP_SIGNAL 0x17 +#endif +#define FP_POWER_CONTROL_ET5XX 0x18 +#define FP_SENSOR_ORIENT 0x19 +#define FP_SPI_VALUE 0x1a +#define FP_IOCTL_RESERVED_01 0x1b +#define FP_IOCTL_RESERVED_02 0x1c + + + +/* trigger signal initial routine */ +#define INT_TRIGGER_INIT 0xa4 +/* trigger signal close routine */ +#define INT_TRIGGER_CLOSE 0xa5 +/* read trigger status */ +#define INT_TRIGGER_READ 0xa6 +/* polling trigger status */ +#define INT_TRIGGER_POLLING 0xa7 +/* polling abort */ +#define INT_TRIGGER_ABORT 0xa8 +/* Sensor Registers */ +#define FDATA_ET5XX_ADDR 0x00 +#define FSTATUS_ET5XX_ADDR 0x01 +/* Detect Define */ +#define FRAME_READY_MASK 0x01 + +#define SHIFT_BYTE_OF_IMAGE 0 +#define DIVISION_OF_IMAGE 4 +#define LARGE_SPI_TRANSFER_BUFFER 64 +#define MAX_NVM_LEN (32 * 2) /* NVM length in bytes (32 * 16 bits internally) */ +#define NVM_WRITE_LENGTH 4096 +#define DETECT_ADM 1 + +struct egis_ioc_transfer { + u8 *tx_buf; + u8 *rx_buf; + + __u32 len; + __u32 speed_hz; + + __u16 delay_usecs; + __u8 bits_per_word; + __u8 cs_change; + __u8 opcode; + __u8 pad[3]; + +}; + +#define EGIS_IOC_MAGIC 'k' +#define EGIS_MSGSIZE(N) \ + ((((N)*(sizeof(struct egis_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ + ? ((N)*(sizeof(struct egis_ioc_transfer))) : 0) +#define EGIS_IOC_MESSAGE(N) _IOW(EGIS_IOC_MAGIC, 0, char[EGIS_MSGSIZE(N)]) + +struct etspi_data { + dev_t devt; + spinlock_t spi_lock; + struct spi_device *spi; + struct list_head device_entry; + + /* buffer is NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned int users; + u8 *buf;/* tx buffer for sensor register read/write */ + unsigned int bufsiz; /* MAX size of tx and rx buffer */ + unsigned int drdyPin; /* DRDY GPIO pin number */ + unsigned int sleepPin; /* Sleep GPIO pin number */ + unsigned int ldo_pin; /* Ldo GPIO pin number */ + unsigned int min_cpufreq_limit; + unsigned int spi_cs; /* spi cs pin */ + + unsigned int drdy_irq_flag; /* irq flag */ + bool ldo_onoff; + + /* For polling interrupt */ + int int_count; + struct timer_list timer; + struct work_struct work_debug; + struct workqueue_struct *wq_dbg; + struct timer_list dbg_timer; + int sensortype; + u32 spi_value; + struct device *fp_device; + int reset_count; + int interrupt_count; +#ifdef ENABLE_SENSORS_FPRINT_SECURE + bool enabled_clk; + bool isGpio_cfgDone; + struct wake_lock fp_spi_lock; +#endif + struct wake_lock fp_signal_lock; + bool tz_mode; + int detect_period; + int detect_threshold; + bool finger_on; + const char *chipid; + unsigned int orient; + struct pinctrl *p; + struct pinctrl_state *pins_sleep; + struct pinctrl_state *pins_idle; +#ifdef NONTZ_BOOST_CONTROL + atomic_t is_boosting; + atomic_t is_sensor_ok; +#endif + struct pm_qos_request pm_qos; +}; + +int etspi_io_burst_read_register(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_burst_read_register_backward(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_burst_write_register(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_burst_write_register_backward(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_read_register(struct etspi_data *etspi, + u8 *addr, u8 *buf); +int etspi_io_read_registerex(struct etspi_data *etspi, + u8 *addr, u8 *buf, u32 len); +int etspi_io_write_register(struct etspi_data *etspi, + u8 *buf); +int etspi_read_register(struct etspi_data *etspi, + u8 addr, u8 *buf); +int etspi_write_register(struct etspi_data *etspi, + u8 addr, u8 buf); +int etspi_io_nvm_read(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_nvm_write(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_nvm_writeex(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_nvm_off(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_vdm_read(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_vdm_write(struct etspi_data *etspi, + struct egis_ioc_transfer *ioc); +int etspi_io_get_frame(struct etspi_data *etspi, u8 *frame, u32 size); +#endif diff --git a/drivers/fingerprint/fingerprint.h b/drivers/fingerprint/fingerprint.h new file mode 100755 index 000000000000..9c1cd1e7d03b --- /dev/null +++ b/drivers/fingerprint/fingerprint.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 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 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 FINGERPRINT_H_ +#define FINGERPRINT_H_ + +#include +#include "fingerprint_sysfs.h" + +/* fingerprint debug timer */ +#define FPSENSOR_DEBUG_TIMER_SEC (10 * HZ) + +enum { + DETECT_NORMAL = 0, + DETECT_ADM, /* Always on Detect Mode */ +}; + +/* For Sensor Type Check */ +enum { + SENSOR_OOO = -2, + SENSOR_UNKNOWN, + SENSOR_FAILED, + SENSOR_VIPER, + SENSOR_RAPTOR, + SENSOR_EGIS, + SENSOR_VIPER_WOG, + SENSOR_NAMSAN, + SENSOR_GOODIX, + SENSOR_QBT2000, + SENSOR_MAXIMUM, +}; + +#define SENSOR_STATUS_SIZE 10 +static char sensor_status[SENSOR_STATUS_SIZE][10] = {"ooo", "unknown", "failed", + "viper", "raptor", "egis", "viper_wog", "namsan", "goodix", "qbt2000"}; + +#endif diff --git a/drivers/fingerprint/fingerprint_sysfs.c b/drivers/fingerprint/fingerprint_sysfs.c new file mode 100755 index 000000000000..a65f9fd0c5f4 --- /dev/null +++ b/drivers/fingerprint/fingerprint_sysfs.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 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 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. + */ + +/* + * fingerprint sysfs class + */ + +#include "fingerprint_sysfs.h" + +struct class *fingerprint_class; +EXPORT_SYMBOL_GPL(fingerprint_class); + +/* + * Create sysfs interface + */ +void set_fingerprint_attr(struct device *dev, + struct device_attribute *attributes[]) +{ + int i; + + for (i = 0; attributes[i] != NULL; i++) + if ((device_create_file(dev, attributes[i])) < 0) + pr_err("%s: fail device_create_file (dev, attributes[%d])\n", __func__, i); +} + +int fingerprint_register(struct device *dev, void *drvdata, + struct device_attribute *attributes[], char *name) +{ + int ret = 0; + + if (!fingerprint_class) { + fingerprint_class = class_create(THIS_MODULE, "fingerprint"); + if (IS_ERR(fingerprint_class)) + return PTR_ERR(fingerprint_class); + } + + dev = device_create(fingerprint_class, NULL, 0, drvdata, "%s", name); + + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + pr_err("%s: device_create failed! [%d]\n", __func__, ret); + return ret; + } + + set_fingerprint_attr(dev, attributes); + + return 0; +} +EXPORT_SYMBOL_GPL(fingerprint_register); + +void fingerprint_unregister(struct device *dev, + struct device_attribute *attributes[]) +{ + int i; + + for (i = 0; attributes[i] != NULL; i++) + device_remove_file(dev, attributes[i]); +} +EXPORT_SYMBOL_GPL(fingerprint_unregister); + +void destroy_fingerprint_class(void) +{ + if (fingerprint_class) { + class_destroy(fingerprint_class); + fingerprint_class = NULL; + } +} +EXPORT_SYMBOL_GPL(destroy_fingerprint_class); + +static int __init fingerprint_class_init(void) +{ + pr_info("%s\n", __func__); + fingerprint_class = class_create(THIS_MODULE, "fingerprint"); + + if (IS_ERR(fingerprint_class)) { + pr_err("%s, create fingerprint_class is failed.\n", + __func__); + return PTR_ERR(fingerprint_class); + } + + fingerprint_class->dev_uevent = NULL; + + return 0; +} + +static void __exit fingerprint_class_exit(void) +{ + if (fingerprint_class) { + class_destroy(fingerprint_class); + fingerprint_class = NULL; + } +} + +subsys_initcall(fingerprint_class_init); +module_exit(fingerprint_class_exit); + +MODULE_DESCRIPTION("fingerprint sysfs class"); +MODULE_LICENSE("GPL"); diff --git a/drivers/fingerprint/fingerprint_sysfs.h b/drivers/fingerprint/fingerprint_sysfs.h new file mode 100755 index 000000000000..7cb30fe9eedf --- /dev/null +++ b/drivers/fingerprint/fingerprint_sysfs.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 FINGERPRINTSYSFS_H_ +#define FINGERPRINTSYSFS_H_ + +#include +#include +#include +#include +#include +#include + +void set_fingerprint_attr(struct device *dev, + struct device_attribute *attributes[]); +int fingerprint_register(struct device *dev, void *drvdata, + struct device_attribute *attributes[], char *name); +void fingerprint_unregister(struct device *dev, + struct device_attribute *attributes[]); +void destroy_fingerprint_class(void); + +#endif diff --git a/drivers/fingerprint/gf_common.c b/drivers/fingerprint/gf_common.c new file mode 100644 index 000000000000..d5202856e473 --- /dev/null +++ b/drivers/fingerprint/gf_common.c @@ -0,0 +1,1162 @@ +/* + * Copyright (C) 2018, Samsung Electronics Co. Ltd. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#else +#include +#endif + +#ifdef CONFIG_OF +#include +#include +#include +#endif + +#ifdef CONFIG_COMPAT +#include +#endif + +#include +#include +#include +#include + +#include "gf_common.h" +#include "fingerprint.h" + +#define GF_DEV_NAME "goodix_fp" +#define GF_DEV_MAJOR 0 /* assigned */ +#define GF_CLASS_NAME "goodix_fp" +#define GF_NETLINK_ROUTE 25 +#define MAX_NL_MSG_LEN 16 +#define WAKELOCK_HOLD_TIME 500 /* in ms */ + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +static unsigned int bufsiz = (50 * 1024); +module_param(bufsiz, uint, S_IRUGO); +MODULE_PARM_DESC(bufsiz, "maximum data bytes for SPI message"); + +#ifdef CONFIG_OF +static const struct of_device_id gfspi_of_match[] = { + { .compatible = "goodix,fingerprint", }, + {}, +}; +MODULE_DEVICE_TABLE(of, gfspi_of_match); +#endif + +extern int fingerprint_register(struct device *dev, void *drvdata, + struct device_attribute *attributes[], char *name); +extern void fingerprint_unregister(struct device *dev, + struct device_attribute *attributes[]); + +static struct gf_device *g_data; + +#ifdef ENABLE_SENSORS_FPRINT_SECURE +#if !defined(CONFIG_SENSORS_ET5XX) && !defined(CONFIG_SENSORS_VFS8XXX) +int fpsensor_goto_suspend = 0; +#endif +#endif + +#if defined(ENABLE_SENSORS_FPRINT_SECURE) +int fps_resume_set(void){ + int ret =0; + + if (fpsensor_goto_suspend) { + fpsensor_goto_suspend = 0; + } + return ret; +} +#endif + +static ssize_t gfspi_bfs_values_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gf_device *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "\"FP_SPICLK\":\"%d\"\n", + data->spi->max_speed_hz); +} + +static ssize_t gfspi_type_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gf_device *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->sensortype); +} + +static ssize_t gfspi_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", "GOODIX"); +} + +static ssize_t gfspi_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gf_device *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", data->chipid); +} + +static ssize_t gfspi_adm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", DETECT_ADM); +} + +static ssize_t gfspi_intcnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gf_device *data = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", data->interrupt_count); +} + +static ssize_t gfspi_intcnt_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct gf_device *data = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "c")) { + data->interrupt_count = 0; + pr_info("initialization is done\n"); + } + return size; +} + +static ssize_t gfspi_resetcnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gf_device *data = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", data->reset_count); +} + +static ssize_t gfspi_resetcnt_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct gf_device *data = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "c")) { + data->reset_count = 0; + pr_info("initialization is done\n"); + } + return size; +} + +static DEVICE_ATTR(bfs_values, 0444, gfspi_bfs_values_show, NULL); +static DEVICE_ATTR(type_check, 0444, gfspi_type_check_show, NULL); +static DEVICE_ATTR(vendor, 0444, gfspi_vendor_show, NULL); +static DEVICE_ATTR(name, 0444, gfspi_name_show, NULL); +static DEVICE_ATTR(adm, 0444, gfspi_adm_show, NULL); +static DEVICE_ATTR(intcnt, 0664, gfspi_intcnt_show, gfspi_intcnt_store); +static DEVICE_ATTR(resetcnt, 0664, gfspi_resetcnt_show, gfspi_resetcnt_store); + +static struct device_attribute *fp_attrs[] = { + &dev_attr_bfs_values, + &dev_attr_type_check, + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_adm, + &dev_attr_intcnt, + &dev_attr_resetcnt, + NULL, +}; + +static void gfspi_enable_irq(struct gf_device *gf_dev) +{ + if (gf_dev->irq_enabled == 1) { + pr_err("%s, irq already enabled\n", __func__); + } else { + enable_irq(gf_dev->irq); + enable_irq_wake(gf_dev->irq); + gf_dev->irq_enabled = 1; + pr_debug("%s enable interrupt!\n", __func__); + } +} + +static void gfspi_disable_irq(struct gf_device *gf_dev) +{ + if (gf_dev->irq_enabled == 0) { + pr_err("%s, irq already disabled\n", __func__); + } else { + disable_irq_wake(gf_dev->irq); + disable_irq(gf_dev->irq); + gf_dev->irq_enabled = 0; + pr_debug("%s disable interrupt!\n", __func__); + } +} + +static void gfspi_netlink_send(struct gf_device *gf_dev, const int command) +{ + struct nlmsghdr *nlh = NULL; + struct sk_buff *skb = NULL; + int ret; + + if (gf_dev->nl_sk == NULL) { + pr_err("%s : invalid socket\n", __func__); + return; + } + + if (gf_dev->pid == 0) { + pr_err("%s : invalid native process pid\n", __func__); + return; + } + + /* alloc data buffer for sending to native */ + /* malloc data space at least 1500 bytes, which is ethernet data length */ + skb = alloc_skb(MAX_NL_MSG_LEN, GFP_ATOMIC); + if (skb == NULL) + return; + + nlh = nlmsg_put(skb, 0, 0, 0, MAX_NL_MSG_LEN, 0); + if (!nlh) { + pr_err("%s : nlmsg_put failed\n", __func__); + kfree_skb(skb); + return; + } + + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + + *(char *)NLMSG_DATA(nlh) = command; + ret = netlink_unicast(gf_dev->nl_sk, skb, gf_dev->pid, MSG_DONTWAIT); + if (ret == 0) { + pr_err("%s : send failed\n", __func__); + return; + } + +} + +static void gfspi_netlink_recv(struct sk_buff *__skb) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + char str[128]; + + skb = skb_get(__skb); + if (skb == NULL) { + pr_err("%s : skb_get return NULL\n", __func__); + return; + } + + /* presume there is 5byte payload at leaset */ + if (skb->len >= NLMSG_SPACE(0)) { + nlh = nlmsg_hdr(skb); + memcpy(str, NLMSG_DATA(nlh), sizeof(str)); + g_data->pid = nlh->nlmsg_pid; + pr_info("%s : pid: %d, msg: %s\n", + __func__, g_data->pid, str); + } else { + pr_err("%s : not enough data length\n", __func__); + } + + kfree_skb(skb); +} + +static int gfspi_netlink_init(struct gf_device *gf_dev) +{ + struct netlink_kernel_cfg cfg; + + memset(&cfg, 0, sizeof(struct netlink_kernel_cfg)); + cfg.input = gfspi_netlink_recv; + + gf_dev->nl_sk = + netlink_kernel_create(&init_net, GF_NETLINK_ROUTE, &cfg); + if (gf_dev->nl_sk == NULL) { + pr_err("%s : netlink create failed\n", __func__); + return -1; + } + + pr_info("%s : netlink create success\n", __func__); + return 0; +} + +static int gfspi_netlink_destroy(struct gf_device *gf_dev) +{ + if (gf_dev->nl_sk != NULL) { + netlink_kernel_release(gf_dev->nl_sk); + gf_dev->nl_sk = NULL; + return 0; + } + + pr_err("%s : no netlink socket yet\n", __func__); + return -1; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void gfspi_early_suspend(struct early_suspend *handler) +{ + struct gf_device *gf_dev = NULL; + + gf_dev = container_of(handler, struct gf_device, early_suspend); + pr_info("%s\n", __func__); + + gfspi_netlink_send(gf_dev, GF_NETLINK_SCREEN_OFF); +} + +static void gfspi_late_resume(struct early_suspend *handler) +{ + struct gf_device *gf_dev = NULL; + + gf_dev = container_of(handler, struct gf_device, early_suspend); + pr_info("%s\n", __func__); + + gfspi_netlink_send(gf_dev, GF_NETLINK_SCREEN_ON); +} +#else +static int gfspi_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct gf_device *gf_dev = NULL; + struct fb_event *evdata = data; + unsigned int blank; + int retval = 0; + + /* If we aren't interested in this event, skip it immediately ... */ + if (event != FB_EARLY_EVENT_BLANK) { + pr_debug("%s event = %ld", __func__, event); + return 0; + } + + gf_dev = container_of(self, struct gf_device, notifier); + blank = *(int *)evdata->data; + + switch (blank) { + case FB_BLANK_UNBLANK: + pr_debug("%s : lcd on notify\n", __func__); + gfspi_netlink_send(gf_dev, GF_NETLINK_SCREEN_ON); + break; + + case FB_BLANK_POWERDOWN: + pr_debug("%s : lcd off notify\n", __func__); + gfspi_netlink_send(gf_dev, GF_NETLINK_SCREEN_OFF); + break; + + default: + pr_debug("%s : other notifier, ignore\n", __func__); + break; + } + return retval; +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static ssize_t gfspi_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + return -EFAULT; +} + +static ssize_t gfspi_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + return -EFAULT; +} + +static irqreturn_t gfspi_irq(int irq, void *handle) +{ + struct gf_device *gf_dev = (struct gf_device *)handle; + + pr_info("%s\n", __func__); + wake_lock_timeout(&gf_dev->wake_lock, + msecs_to_jiffies(WAKELOCK_HOLD_TIME)); + gfspi_netlink_send(gf_dev, GF_NETLINK_IRQ); + gf_dev->interrupt_count++; + + return IRQ_HANDLED; +} + +static long gfspi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gf_device *gf_dev = NULL; + unsigned int onoff = 0; + int retval = 0; + u8 buf = 0; + u8 netlink_route = GF_NETLINK_ROUTE; + + if (_IOC_TYPE(cmd) != GF_IOC_MAGIC) + return -EINVAL; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) + retval = !access_ok(VERIFY_WRITE, (void __user *)arg, + _IOC_SIZE(cmd)); + + if (retval == 0 && _IOC_DIR(cmd) & _IOC_WRITE) + retval = !access_ok(VERIFY_READ, (void __user *)arg, + _IOC_SIZE(cmd)); + + if (retval) { + pr_err("%s: access NOK\n", __func__); + return -EINVAL; + } + + gf_dev = (struct gf_device *)filp->private_data; + if (!gf_dev) { + pr_err("%s: gf_dev IS NULL\n", __func__); + return -EINVAL; + } + + switch (cmd) { + case GF_IOC_INIT: + pr_info("%s: GF_IOC_INIT\n", __func__); + if (copy_to_user((void __user *)arg, (void *)&netlink_route, + sizeof(u8))) { + retval = -EFAULT; + break; + } + + if (gf_dev->system_status) { + pr_info("%s: system re-started\n", __func__); + break; + } + + gf_dev->sig_count = 0; + gf_dev->system_status = 1; + break; + + case GF_IOC_EXIT: + pr_info("%s: GF_IOC_EXIT\n", __func__); + gfspi_disable_irq(gf_dev); + if (gf_dev->irq) { + free_irq(gf_dev->irq, gf_dev); + gf_dev->irq = 0; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (gf_dev->early_suspend.suspend) + unregister_early_suspend(&gf_dev->early_suspend); +#else + fb_unregister_client(&gf_dev->notifier); +#endif + gf_dev->system_status = 0; + break; + + case GF_IOC_RESET: + pr_info("%s: GF_IOC_RESET\n", __func__); + gfspi_hw_reset(gf_dev, 0); + break; + + case GF_IOC_ENABLE_IRQ: + pr_info("%s: GF_IOC_ENABLE_IRQ\n", __func__); + gfspi_enable_irq(gf_dev); + break; + + case GF_IOC_DISABLE_IRQ: + pr_info("%s: GF_IOC_DISABLE_IRQ\n", __func__); + gfspi_disable_irq(gf_dev); + break; + + case GF_IOC_ENABLE_SPI_CLK: + pr_debug("%s: GF_IOC_ENABLE_SPI_CLK\n", __func__); + gfspi_spi_clk_enable(gf_dev); + break; + + case GF_IOC_DISABLE_SPI_CLK: + pr_debug("%s: GF_IOC_DISABLE_SPI_CLK\n", __func__); + gfspi_spi_clk_disable(gf_dev); + break; + + case GF_IOC_ENABLE_POWER: + pr_debug("%s: GF_IOC_ENABLE_POWER\n", __func__); + gfspi_hw_power_enable(gf_dev, 1); + break; + + case GF_IOC_DISABLE_POWER: + pr_debug("%s: GF_IOC_DISABLE_POWER\n", __func__); + gfspi_hw_power_enable(gf_dev, 0); + break; + + case GF_IOC_POWER_CONTROL: + if (copy_from_user(&onoff, (void __user *)arg, + sizeof(unsigned int))) { + pr_err("Failed to copy onoff value from user to kernel\n"); + retval = -EFAULT; + break; + } + pr_info("%s: GF_IOC_POWER_CONTROL %d\n", __func__, onoff); + gfspi_hw_power_enable(gf_dev, onoff); + break; + + case GF_IOC_ENTER_SLEEP_MODE: + break; + + case GF_IOC_GET_FW_INFO: + buf = gf_dev->need_update; + buf = 1; + pr_debug("%s: GET_FW_INFO : 0x%x\n", __func__, buf); + if (copy_to_user((void __user *)arg, (void *)&buf, + sizeof(u8))) { + pr_err("Failed to copy data to user\n"); + retval = -EFAULT; + } + + break; + case GF_IOC_REMOVE: + break; + +#ifndef ENABLE_SENSORS_FPRINT_SECURE + case GF_IOC_TRANSFER_RAW_CMD: + mutex_lock(&gf_dev->buf_lock); + retval = gfspi_ioctl_transfer_raw_cmd(gf_dev, arg, bufsiz); + mutex_unlock(&gf_dev->buf_lock); + break; +#endif /* !ENABLE_SENSORS_FPRINT_SECURE */ +#ifdef ENABLE_SENSORS_FPRINT_SECURE + case GF_IOC_SET_SENSOR_TYPE: + if (copy_from_user(&onoff, (void __user *)arg, + sizeof(unsigned int)) != 0) { + pr_err("Failed to copy sensor type from user to kernel\n"); + return -EFAULT; + } + if ((int)onoff >= SENSOR_OOO && (int)onoff < SENSOR_MAXIMUM) { + if ((int)onoff == SENSOR_OOO && gf_dev->sensortype == SENSOR_FAILED) { + pr_err("%s Maintain type check from out of oder :%s\n", + __func__, sensor_status[g_data->sensortype + 2]); + } else { + gf_dev->sensortype = (int)onoff; + pr_info("%s SET_SENSOR_TYPE :%s\n", + __func__, + sensor_status[g_data->sensortype + 2]); + } + } else { + pr_err("%s SET_SENSOR_TYPE : invalid value %d\n", + __func__, (int)onoff); + gf_dev->sensortype = SENSOR_UNKNOWN; + } + break; + + case GF_IOC_SPEEDUP: + pr_info("%s FP_CPU_SPEEDUP\n", __func__); + if (copy_from_user(&onoff, (void __user *)arg, + sizeof(unsigned int)) != 0) { + pr_err("Failed to copy speedup from user to kernel\n"); + return -EFAULT; + } + + if (onoff) { + u8 retry_cnt = 0; + pr_info("%s FP_CPU_SPEEDUP ON:%d, retry: %d\n", + __func__, onoff, retry_cnt); + + if (gf_dev->min_cpufreq_limit) { + pm_qos_add_request(&gf_dev->pm_qos, + PM_QOS_CPU_DMA_LATENCY, 0); +#ifdef CONFIG_CPU_FREQ_LIMIT + do { + retval = set_freq_limit(DVFS_FINGER_ID, + gf_dev->min_cpufreq_limit); + retry_cnt++; + if (retval) { + pr_err("%s: booster start failed. (%d) retry: %d\n" + , __func__, retval, + retry_cnt); + usleep_range(500, 510); + } + } while (retval && retry_cnt < 7); +#endif + } + } else { + pr_info("%s FP_CPU_SPEEDUP OFF\n", __func__); +#ifdef CONFIG_CPU_FREQ_LIMIT + retval = set_freq_limit(DVFS_FINGER_ID, -1); + if (retval) + pr_err("%s: booster stop failed. (%d)\n" + , __func__, retval); +#endif + pm_qos_remove_request(&gf_dev->pm_qos); + } + break; + case GF_IOC_SET_LOCKSCREEN: + break; +#endif + case GF_IOC_GET_ORIENT: + pr_info("%s: GET_ORIENT: %d\n", __func__, gf_dev->orient); + if (copy_to_user((void __user *)arg, &(gf_dev->orient), + sizeof(gf_dev->orient))) { + pr_err("Failed to copy data to user\n"); + retval = -EFAULT; + } + break; + default: + pr_err("%s doesn't support this command(%x)\n", __func__, cmd); + break; + } + + return retval; +} + +#ifdef CONFIG_COMPAT +static long gfspi_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int retval = 0; + + retval = filp->f_op->unlocked_ioctl(filp, cmd, arg); + + return retval; +} +#endif + +static unsigned int gfspi_poll(struct file *filp, + struct poll_table_struct *wait) +{ + pr_err("Not support poll opertion in TEE version\n"); + return -EFAULT; +} + +static int gfspi_open(struct inode *inode, struct file *filp) +{ + struct gf_device *gf_dev = NULL; + int status = -ENXIO; + + pr_info("%s\n", __func__); + mutex_lock(&device_list_lock); + list_for_each_entry(gf_dev, &device_list, device_entry) { + if (gf_dev->devno == inode->i_rdev) { + pr_info("%s, Found\n", __func__); + status = 0; + break; + } + } + mutex_unlock(&device_list_lock); + + if (status == 0) { + filp->private_data = gf_dev; + nonseekable_open(inode, filp); + pr_info("%s, Success to open device. irq = %d\n", + __func__, gf_dev->irq); + } else { + pr_err("%s, No device for minor %d\n", + __func__, iminor(inode)); + } + return status; +} + +static int gfspi_release(struct inode *inode, struct file *filp) +{ + struct gf_device *gf_dev = NULL; + int status = 0; + + pr_info("%s\n", __func__); + gf_dev = filp->private_data; + if (gf_dev->irq) + gfspi_disable_irq(gf_dev); + gf_dev->need_update = 0; + return status; +} + +static const struct file_operations gfspi_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .write = gfspi_write, + .read = gfspi_read, + .unlocked_ioctl = gfspi_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gfspi_compat_ioctl, +#endif + .open = gfspi_open, + .release = gfspi_release, + .poll = gfspi_poll, +}; + + +static void gfspi_work_func_debug(struct work_struct *work) +{ + struct gf_device *gf_dev = NULL; + u8 ldo_value = -1; + u8 rst_value = -1; + u8 irq_value = -1; + + gf_dev = container_of(work, struct gf_device, work_debug); + + if (gf_dev->pwr_gpio) + ldo_value = gpio_get_value(gf_dev->pwr_gpio); + if (gf_dev->reset_gpio) + rst_value = gpio_get_value(gf_dev->reset_gpio); + if (gf_dev->irq_gpio) + irq_value = gpio_get_value(gf_dev->irq_gpio); + + pr_info("%s ldo: %d, sleep: %d, irq: %d tz: %d type: %s\n", + __func__, + ldo_value, rst_value, irq_value, gf_dev->tz_mode, + sensor_status[gf_dev->sensortype + 2]); +} + +static void gfspi_enable_debug_timer(struct gf_device *gf_dev) +{ + mod_timer(&gf_dev->dbg_timer, + round_jiffies_up(jiffies + FPSENSOR_DEBUG_TIMER_SEC)); +} + +static void gfspi_disable_debug_timer(struct gf_device *gf_dev) +{ + del_timer_sync(&gf_dev->dbg_timer); + cancel_work_sync(&gf_dev->work_debug); +} + +static void gfspi_timer_func(unsigned long ptr) +{ + queue_work(g_data->wq_dbg, &g_data->work_debug); + mod_timer(&g_data->dbg_timer, + round_jiffies_up(jiffies + FPSENSOR_DEBUG_TIMER_SEC)); +} + +static int gfspi_set_timer(struct gf_device *gf_dev) +{ + int status = 0; + + setup_timer(&gf_dev->dbg_timer, + gfspi_timer_func, (unsigned long)gf_dev); + gf_dev->wq_dbg = create_singlethread_workqueue("gf_debug_wq"); + if (!gf_dev->wq_dbg) { + status = -ENOMEM; + pr_err("%s could not create workqueue\n", __func__); + return status; + } + INIT_WORK(&gf_dev->work_debug, gfspi_work_func_debug); + return status; +} + +void gfspi_hw_power_enable(struct gf_device *gf_dev, u8 onoff) +{ + if (onoff && !gf_dev->ldo_onoff) { + gfspi_pin_control(gf_dev, 1); + if (gf_dev->pwr_gpio) + gpio_set_value(gf_dev->pwr_gpio, 1); + if (gf_dev->reset_gpio) { + mdelay(11); + gpio_set_value(gf_dev->reset_gpio, 1); + } + gf_dev->ldo_onoff = 1; + } else if (!onoff && gf_dev->ldo_onoff) { + if (gf_dev->reset_gpio) { + gpio_set_value(gf_dev->reset_gpio, 0); + mdelay(11); + } + if (gf_dev->pwr_gpio) + gpio_set_value(gf_dev->pwr_gpio, 0); + gf_dev->ldo_onoff = 0; + gfspi_pin_control(gf_dev, 0); + } else if (onoff == 0 || onoff == 1) { + pr_err("%s power is already %s\n", + __func__, + (gf_dev->ldo_onoff ? "Enabled" : "Disabled")); + } else { + pr_err("%s can't support this value:%d\n", __func__, onoff); + } + pr_info("%s status = %d\n", __func__, gf_dev->ldo_onoff); +} + +void gfspi_hw_reset(struct gf_device *gf_dev, u8 delay) +{ + if (gf_dev == NULL) { + pr_err("%s, Input buff is NULL.\n", __func__); + return; + } + gpio_direction_output(gf_dev->reset_gpio, 1); + gpio_set_value(gf_dev->reset_gpio, 0); + mdelay(3); + gpio_set_value(gf_dev->reset_gpio, 1); + mdelay(delay); + gf_dev->reset_count++; +} + +#ifndef ENABLE_SENSORS_FPRINT_SECURE +int gfspi_type_check(struct gf_device *gf_dev) +{ + int status = -ENODEV; + unsigned char chipid[4] = {0x00, 0x00, 0x00, 0x00}; + u32 chipid32 = 0; + + gfspi_hw_power_enable(gf_dev, 1); + usleep_range(4950, 5000); + gfspi_hw_reset(gf_dev, 0); + + gfspi_spi_read_bytes(gf_dev, 0x0000, 4, chipid); + chipid32 = (chipid[2] << 16 | chipid[3] << 8 | chipid[0]); + if (GF_GW32J_CHIP_ID == chipid32) { + gf_dev->sensortype = SENSOR_GOODIX; + pr_info("%s sensor type is GW32J (%s:0x%x)\n", __func__, + sensor_status[gf_dev->sensortype + 2], chipid32); + status = 0; + } else if (GF_GW32N_CHIP_ID == chipid32) { + gf_dev->sensortype = SENSOR_GOODIX; + pr_info("%s sensor type is GW32N (%s:0x%x)\n", __func__, + sensor_status[gf_dev->sensortype + 2], chipid32); + status = 0; + } else if (GF_GW36H_CHIP_ID == chipid32) { + gf_dev->sensortype = SENSOR_GOODIX; + pr_info("%s sensor type is GW36H (%s:0x%x)\n", __func__, + sensor_status[gf_dev->sensortype + 2], chipid32); + status = 0; + } else if (GF_GW36C_CHIP_ID == chipid32) { + gf_dev->sensortype = SENSOR_GOODIX; + pr_info("%s sensor type is GW36C (%s:0x%x)\n", __func__, + sensor_status[gf_dev->sensortype + 2], chipid32); + status = 0; + } else { + gf_dev->sensortype = SENSOR_FAILED; + pr_err("%s sensor type is FAILED 0x%x\n", + __func__, chipid32); + } + gfspi_hw_power_enable(gf_dev, 0); + return status; +} +#endif + + +static int gfspi_probe(struct spi_device *spi) +{ + struct gf_device *gf_dev = NULL; + int status = -EINVAL; +#ifndef ENABLE_SENSORS_FPRINT_SECURE + int retry = 0; +#endif + pr_info("%s\n", __func__); + + /* Allocate driver data */ + gf_dev = kzalloc(sizeof(struct gf_device), GFP_KERNEL); + if (!gf_dev) { + status = -ENOMEM; + return status; + } + + spin_lock_init(&gf_dev->spi_lock); + mutex_init(&gf_dev->buf_lock); + mutex_init(&gf_dev->release_lock); + + INIT_LIST_HEAD(&gf_dev->device_entry); + + gf_dev->device_count = 0; + gf_dev->system_status = 0; + gf_dev->need_update = 0; + gf_dev->ldo_onoff = 0; + gf_dev->pid = 0; + gf_dev->reset_count = 0; + gf_dev->interrupt_count = 0; +#ifdef ENABLE_SENSORS_FPRINT_SECURE + gf_dev->enabled_clk = 0; + gf_dev->tz_mode = true; + fpsensor_goto_suspend = 0; +#else + gf_dev->tz_mode = false; +#endif + + /* Initialize the driver data */ + gf_dev->spi = spi; + g_data = gf_dev; + + gf_dev->irq = 0; + spi_set_drvdata(spi, gf_dev); + + /* allocate buffer for SPI transfer */ +#ifndef ENABLE_SENSORS_FPRINT_SECURE + gf_dev->spi_buffer = kzalloc(bufsiz, GFP_KERNEL); + if (gf_dev->spi_buffer == NULL) { + status = -ENOMEM; + goto err_buf; + } +#endif + + /* get gpio info from dts or defination */ + status = gfspi_get_gpio_dts_info(&spi->dev, gf_dev); + if (status < 0) { + pr_err("%s, Failed to get gpio info:%d\n", __func__, status); + goto err_get_gpio; + } + gfspi_spi_setup_conf(gf_dev, 4); + + /* create class */ + gf_dev->class = class_create(THIS_MODULE, GF_CLASS_NAME); + if (IS_ERR(gf_dev->class)) { + pr_err("%s, Failed to create class.\n", __func__); + status = -ENODEV; + goto err_class_create; + } + + /* get device no */ + if (GF_DEV_MAJOR > 0) { + gf_dev->devno = MKDEV(GF_DEV_MAJOR, gf_dev->device_count++); + status = register_chrdev_region(gf_dev->devno, 1, GF_DEV_NAME); + } else { + status = alloc_chrdev_region(&gf_dev->devno, + gf_dev->device_count++, 1, GF_DEV_NAME); + } + if (status < 0) { + pr_err("%s, Failed to alloc devno.\n", __func__); + goto err_devno; + } else { + pr_info("%s, major=%d, minor=%d\n", + __func__, MAJOR(gf_dev->devno), + MINOR(gf_dev->devno)); + } + + /* create device */ + gf_dev->fp_device = device_create(gf_dev->class, &spi->dev, + gf_dev->devno, gf_dev, GF_DEV_NAME); + if (IS_ERR(gf_dev->fp_device)) { + pr_err("%s, Failed to create device.\n", __func__); + status = -ENODEV; + goto err_device; + } else { + mutex_lock(&device_list_lock); + list_add(&gf_dev->device_entry, &device_list); + mutex_unlock(&device_list_lock); + pr_info("%s, device create success.\n", __func__); + } + +#ifdef ENABLE_SENSORS_FPRINT_SECURE + gf_dev->sensortype = SENSOR_UNKNOWN; +#else + /* sensor hw type check */ + do { + status = gfspi_type_check(gf_dev); + pr_info("%s type (%u), retry (%d)\n", + __func__, gf_dev->sensortype, retry); + } while (!gf_dev->sensortype && ++retry < 3); + + if (status == -ENODEV) + pr_err("%s type_check failed\n", __func__); +#endif + + /* create sysfs */ + status = fingerprint_register(gf_dev->fp_device, + gf_dev, fp_attrs, "fingerprint"); + if (status) { + pr_err("%s sysfs register failed\n", __func__); + goto err_sysfs; + } + + /* cdev init and add */ + cdev_init(&gf_dev->cdev, &gfspi_fops); + gf_dev->cdev.owner = THIS_MODULE; + status = cdev_add(&gf_dev->cdev, gf_dev->devno, 1); + if (status) { + pr_err("%s, Failed to add cdev.\n", __func__); + goto err_cdev; + } + + /* netlink interface init */ + status = gfspi_netlink_init(gf_dev); + if (status == -1) { + pr_err("%s, Failed to init netlink.\n", __func__); + goto err_netlink_init; + } + + wake_lock_init(&gf_dev->wake_lock, WAKE_LOCK_SUSPEND, "gf_wake_lock"); + gf_dev->irq = gpio_to_irq(gf_dev->irq_gpio); + status = request_threaded_irq(gf_dev->irq, NULL, gfspi_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "goodix_fp_irq", + gf_dev); + if (status) { + pr_err("%s irq thread request failed, retval=%d\n", + __func__, status); + goto err_request_irq; + } + + enable_irq_wake(gf_dev->irq); + gf_dev->irq_enabled = 1; + gfspi_disable_irq(gf_dev); + + status = gfspi_set_timer(gf_dev); + if (status) + goto err_debug_timer; + gfspi_enable_debug_timer(gf_dev); + +#if defined(CONFIG_HAS_EARLYSUSPEND) + pr_info("%s : register_early_suspend\n", __func__); + gf_dev->early_suspend.level = (EARLY_SUSPEND_LEVEL_DISABLE_FB - 1); + gf_dev->early_suspend.suspend = gfspi_early_suspend, + gf_dev->early_suspend.resume = gfspi_late_resume, + register_early_suspend(&gf_dev->early_suspend); +#else + /* register screen on/off callback */ + gf_dev->notifier.notifier_call = gfspi_fb_notifier_callback; + fb_register_client(&gf_dev->notifier); +#endif + pr_info("%s probe finished\n", __func__); + return 0; + +err_debug_timer: + free_irq(gf_dev->irq, gf_dev); +err_request_irq: + gfspi_netlink_destroy(gf_dev); +err_netlink_init: + cdev_del(&gf_dev->cdev); +err_cdev: + fingerprint_unregister(gf_dev->fp_device, fp_attrs); +err_sysfs: + device_destroy(gf_dev->class, gf_dev->devno); + list_del(&gf_dev->device_entry); +err_device: + unregister_chrdev_region(gf_dev->devno, 1); +err_devno: + class_destroy(gf_dev->class); +err_class_create: +err_get_gpio: +#ifndef ENABLE_SENSORS_FPRINT_SECURE + kfree(gf_dev->spi_buffer); +err_buf: +#endif + mutex_destroy(&gf_dev->buf_lock); + mutex_destroy(&gf_dev->release_lock); + spi_set_drvdata(spi, NULL); + gf_dev->spi = NULL; + kfree(gf_dev); + gf_dev = NULL; + + pr_err("%s failed. %d", __func__, status); + return status; +} + +static int gfspi_remove(struct spi_device *spi) +{ + struct gf_device *gf_dev = spi_get_drvdata(spi); + + pr_info("%s\n", __func__); + + /* make sure ops on existing fds can abort cleanly */ + if (gf_dev->irq) { + free_irq(gf_dev->irq, gf_dev); + gf_dev->irq_enabled = 0; + gf_dev->irq = 0; + } + + gfspi_disable_debug_timer(gf_dev); + wake_lock_destroy(&gf_dev->wake_lock); + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (gf_dev->early_suspend.suspend) + unregister_early_suspend(&gf_dev->early_suspend); +#else + fb_unregister_client(&gf_dev->notifier); +#endif + +#ifndef ENABLE_SENSORS_FPRINT_SECURE + mutex_lock(&gf_dev->release_lock); + if (gf_dev->spi_buffer != NULL) { + kfree(gf_dev->spi_buffer); + gf_dev->spi_buffer = NULL; + } + mutex_unlock(&gf_dev->release_lock); +#endif + fingerprint_unregister(gf_dev->fp_device, fp_attrs); + gfspi_netlink_destroy(gf_dev); + cdev_del(&gf_dev->cdev); + device_destroy(gf_dev->class, gf_dev->devno); + list_del(&gf_dev->device_entry); + + unregister_chrdev_region(gf_dev->devno, 1); + class_destroy(gf_dev->class); + gfspi_hw_power_enable(gf_dev, 0); + + spin_lock_irq(&gf_dev->spi_lock); + spi_set_drvdata(spi, NULL); + gf_dev->spi = NULL; + spin_unlock_irq(&gf_dev->spi_lock); + + mutex_destroy(&gf_dev->buf_lock); + mutex_destroy(&gf_dev->release_lock); + + kfree(gf_dev); + return 0; +} + +static int gfspi_pm_suspend(struct device *dev) +{ + struct gf_device *gf_dev = dev_get_drvdata(dev); + + pr_info("%s\n", __func__); + gfspi_disable_debug_timer(gf_dev); + return 0; +} + +static int gfspi_pm_resume(struct device *dev) +{ + struct gf_device *gf_dev = dev_get_drvdata(dev); + + pr_info("%s\n", __func__); + gfspi_enable_debug_timer(gf_dev); +#if defined(ENABLE_SENSORS_FPRINT_SECURE) + if (fpsensor_goto_suspend) { + fps_resume_set(); + } +#endif + return 0; +} + +static const struct dev_pm_ops gfspi_pm_ops = { + .suspend = gfspi_pm_suspend, + .resume = gfspi_pm_resume +}; + +static struct spi_driver gfspi_spi_driver = { + .driver = { + .name = GF_DEV_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .pm = &gfspi_pm_ops, +#ifdef CONFIG_OF + .of_match_table = gfspi_of_match, +#endif + }, + .probe = gfspi_probe, + .remove = gfspi_remove, +}; + +static int __init gfspi_init(void) +{ + int status = 0; + + pr_info("%s\n", __func__); + + status = spi_register_driver(&gfspi_spi_driver); + if (status < 0) { + pr_err("%s, Failed to register SPI driver.\n", + __func__); + return -EINVAL; + } + return status; +} +module_init(gfspi_init); + +static void __exit gfspi_exit(void) +{ + pr_info("%s\n", __func__); + spi_unregister_driver(&gfspi_spi_driver); +} +module_exit(gfspi_exit); + + +MODULE_AUTHOR("Samgsung"); +MODULE_DESCRIPTION("Goodix FP sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/fingerprint/gf_common.h b/drivers/fingerprint/gf_common.h new file mode 100644 index 000000000000..11c91a4ea215 --- /dev/null +++ b/drivers/fingerprint/gf_common.h @@ -0,0 +1,227 @@ +#ifndef __GF_SPI_DRIVER_H +#define __GF_SPI_DRIVER_H + +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#else +#include +#endif + +#ifdef ENABLE_SENSORS_FPRINT_SECURE +#if defined (CONFIG_ARCH_EXYNOS9) || defined(CONFIG_ARCH_EXYNOS8)\ + || defined (CONFIG_ARCH_EXYNOS7) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_SECURE_OS_BOOSTER_API) +#if defined(CONFIG_SOC_EXYNOS7870) || defined(CONFIG_SOC_EXYNOS7880)\ + || defined(CONFIG_SOC_EXYNOS7570) || defined(CONFIG_SOC_EXYNOS7885) +#include +#else +#include +#endif +#elif defined(CONFIG_TZDEV_BOOST) +#include <../drivers/misc/tzdev/tz_boost.h> +#endif + +struct sec_spi_info { + int port; + unsigned long speed; +}; +#endif +#include +#include +#include +#include "../pinctrl/core.h" +#include + +/* + * This feature is temporary for exynos AP only. + * It's for control GPIO config on enabled TZ before enable GPIO protection. + * If it's still defined this feature after enable GPIO protection, + * it will be happened kernel panic + * So it should be un-defined after enable GPIO protection + */ +#undef DISABLED_GPIO_PROTECTION + +#define GF_IOC_MAGIC 'g' + +#define GF_GW32J_CHIP_ID 0x00220e +#define GF_GW32N_CHIP_ID 0x002215 +#define GF_GW36H_CHIP_ID 0x002504 +#define GF_GW36C_CHIP_ID 0x002502 + +#define MAX_BAUD_RATE 4800000 + +enum gf_netlink_cmd { + GF_NETLINK_TEST = 0, + GF_NETLINK_IRQ = 1, + GF_NETLINK_SCREEN_OFF, + GF_NETLINK_SCREEN_ON +}; + +struct gf_ioc_transfer { + u8 cmd; /* spi read = 0, spi write = 1 */ + u8 reserved; + u16 addr; + u32 len; + u8 *buf; +}; + +struct gf_ioc_transfer_raw { + u32 len; + u8 *read_buf; + u8 *write_buf; + uint32_t high_time; + uint32_t low_time; +}; + +#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY +struct gf_ioc_transfer_32 { + u8 cmd; /* spi read = 0, spi write = 1 */ + u8 reserved; + u16 addr; + u32 len; + u32 buf; +}; + +struct gf_ioc_transfer_raw_32 { + u32 len; + u32 read_buf; + u32 write_buf; + uint32_t high_time; + uint32_t low_time; +}; +#endif + +/* define commands */ +#define GF_IOC_INIT _IOR(GF_IOC_MAGIC, 0, u8) +#define GF_IOC_EXIT _IO(GF_IOC_MAGIC, 1) +#define GF_IOC_RESET _IO(GF_IOC_MAGIC, 2) +#define GF_IOC_ENABLE_IRQ _IO(GF_IOC_MAGIC, 3) +#define GF_IOC_DISABLE_IRQ _IO(GF_IOC_MAGIC, 4) +#define GF_IOC_ENABLE_SPI_CLK _IOW(GF_IOC_MAGIC, 5, uint32_t) +#define GF_IOC_DISABLE_SPI_CLK _IO(GF_IOC_MAGIC, 6) +#define GF_IOC_ENABLE_POWER _IO(GF_IOC_MAGIC, 7) +#define GF_IOC_DISABLE_POWER _IO(GF_IOC_MAGIC, 8) +#define GF_IOC_ENTER_SLEEP_MODE _IO(GF_IOC_MAGIC, 10) +#define GF_IOC_GET_FW_INFO _IOR(GF_IOC_MAGIC, 11, u8) +#define GF_IOC_REMOVE _IO(GF_IOC_MAGIC, 12) + +/* for SPI REE transfer */ +#define GF_IOC_TRANSFER_CMD _IOWR(GF_IOC_MAGIC, 15, \ + struct gf_ioc_transfer) +#ifndef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY +#define GF_IOC_TRANSFER_RAW_CMD _IOWR(GF_IOC_MAGIC, 16, \ + struct gf_ioc_transfer_raw) +#else +#define GF_IOC_TRANSFER_RAW_CMD _IOWR(GF_IOC_MAGIC, 16, \ + struct gf_ioc_transfer_raw_32) +#endif +#ifdef ENABLE_SENSORS_FPRINT_SECURE +#define GF_IOC_SET_SENSOR_TYPE _IOW(GF_IOC_MAGIC, 18, unsigned int) +#endif +#define GF_IOC_POWER_CONTROL _IOW(GF_IOC_MAGIC, 19, unsigned int) +#ifdef ENABLE_SENSORS_FPRINT_SECURE +#define GF_IOC_SPEEDUP _IOW(GF_IOC_MAGIC, 20, unsigned int) +#define GF_IOC_SET_LOCKSCREEN _IOW(GF_IOC_MAGIC, 21, unsigned int) +#endif +#define GF_IOC_GET_ORIENT _IOR(GF_IOC_MAGIC, 22, unsigned int) + +#define GF_IOC_MAXNR 23 /* THIS MACRO IS NOT USED NOW... */ + +struct gf_device { + dev_t devno; + struct cdev cdev; + struct device *fp_device; + struct class *class; + struct spi_device *spi; + int device_count; + + spinlock_t spi_lock; + struct list_head device_entry; +#ifndef ENABLE_SENSORS_FPRINT_SECURE + u8 *spi_buffer; +#endif + struct mutex buf_lock; + struct mutex release_lock; + struct sock *nl_sk; + u8 buf_status; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#else + struct notifier_block notifier; +#endif + + u8 irq_enabled; + u8 sig_count; + u8 system_status; + + u32 pwr_gpio; + u32 reset_gpio; + u32 irq_gpio; + u32 irq; + u8 need_update; + /* for netlink use */ + int pid; + + struct work_struct work_debug; + struct workqueue_struct *wq_dbg; + struct timer_list dbg_timer; + +#ifdef ENABLE_SENSORS_FPRINT_SECURE + bool enabled_clk; +#endif + unsigned int current_spi_speed; + unsigned int orient; + unsigned int min_cpufreq_limit; + int sensortype; + int reset_count; + int interrupt_count; + bool ldo_onoff; + bool tz_mode; + const char *chipid; + struct wake_lock wake_lock; + + struct pinctrl *p; + struct pinctrl_state *pins_sleep; + struct pinctrl_state *pins_idle; + struct pm_qos_request pm_qos; +}; + + +int gfspi_get_gpio_dts_info(struct device *dev, struct gf_device *gf_dev); +void gfspi_cleanup_info(struct gf_device *gf_dev); +void gfspi_hw_power_enable(struct gf_device *gf_dev, u8 onoff); +int gfspi_spi_clk_enable(struct gf_device *gf_dev); +int gfspi_spi_clk_disable(struct gf_device *gf_dev); +void gfspi_hw_reset(struct gf_device *gf_dev, u8 delay); +void gfspi_spi_setup_conf(struct gf_device *gf_dev, u32 speed); +int gfspi_pin_control(struct gf_device *gf_dev, bool pin_set); + +#ifndef ENABLE_SENSORS_FPRINT_SECURE +int gfspi_spi_read_bytes(struct gf_device *gf_dev, u16 addr, + u32 data_len, u8 *rx_buf); +int gfspi_spi_write_bytes(struct gf_device *gf_dev, u16 addr, + u32 data_len, u8 *tx_buf); +int gfspi_spi_read_byte(struct gf_device *gf_dev, u16 addr, u8 *value); +int gfspi_spi_write_byte(struct gf_device *gf_dev, u16 addr, u8 value); +int gfspi_ioctl_transfer_raw_cmd(struct gf_device *gf_dev, + unsigned long arg, unsigned int bufsiz); +int gfspi_ioctl_spi_init_cfg_cmd(struct gf_device *gf_dev, + unsigned long arg); +#endif /* !ENABLE_SENSORS_FPRINT_SECURE */ + +#endif /* __GF_SPI_DRIVER_H */ diff --git a/drivers/fingerprint/gf_platform.c b/drivers/fingerprint/gf_platform.c new file mode 100644 index 000000000000..2e17adcdf671 --- /dev/null +++ b/drivers/fingerprint/gf_platform.c @@ -0,0 +1,470 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gf_common.h" + +void gfspi_spi_setup_conf(struct gf_device *gf_dev, u32 speed) +{ + u32 max_speed_hz; + + switch (speed) { + case 1: + case 4: + case 6: + case 8: + default: + max_speed_hz = MAX_BAUD_RATE; + break; + } + + gf_dev->spi->mode = SPI_MODE_0; + gf_dev->spi->max_speed_hz = max_speed_hz; + gf_dev->spi->bits_per_word = 8; + + gf_dev->current_spi_speed = max_speed_hz; +#ifndef ENABLE_SENSORS_FPRINT_SECURE + if (spi_setup(gf_dev->spi)) + pr_err("%s, failed to setup spi conf\n", __func__); +#endif +} + +#ifndef ENABLE_SENSORS_FPRINT_SECURE +int gfspi_spi_read_bytes(struct gf_device *gf_dev, u16 addr, + u32 data_len, u8 *rx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfer = NULL; + u8 *tmp_buf = NULL; + + xfer = kzalloc(sizeof(*xfer) * 2, GFP_KERNEL); + if (xfer == NULL) + return -ENOMEM; + + tmp_buf = gf_dev->spi_buffer; + + spi_message_init(&msg); + *tmp_buf = 0xF0; + *(tmp_buf + 1) = (u8)((addr >> 8) & 0xFF); + *(tmp_buf + 2) = (u8)(addr & 0xFF); + xfer[0].tx_buf = tmp_buf; + xfer[0].len = 3; + xfer[0].delay_usecs = 5; + spi_message_add_tail(&xfer[0], &msg); + spi_sync(gf_dev->spi, &msg); + + spi_message_init(&msg); + /* memset((tmp_buf + 4), 0x00, data_len + 1); */ + /* 4 bytes align */ + *(tmp_buf + 4) = 0xF1; + xfer[1].tx_buf = tmp_buf + 4; + xfer[1].rx_buf = tmp_buf + 4; + xfer[1].len = data_len + 1; + xfer[1].delay_usecs = 5; + spi_message_add_tail(&xfer[1], &msg); + spi_sync(gf_dev->spi, &msg); + + memcpy(rx_buf, (tmp_buf + 5), data_len); + + kfree(xfer); + if (xfer != NULL) + xfer = NULL; + + return 0; +} + +int gfspi_spi_write_bytes(struct gf_device *gf_dev, u16 addr, + u32 data_len, u8 *tx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfer = NULL; + u8 *tmp_buf = NULL; + + xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); + if (xfer == NULL) + return -ENOMEM; + + tmp_buf = gf_dev->spi_buffer; + + spi_message_init(&msg); + *tmp_buf = 0xF0; + *(tmp_buf + 1) = (u8)((addr >> 8) & 0xFF); + *(tmp_buf + 2) = (u8)(addr & 0xFF); + memcpy(tmp_buf + 3, tx_buf, data_len); + xfer[0].len = data_len + 3; + xfer[0].tx_buf = tmp_buf; + xfer[0].delay_usecs = 5; + spi_message_add_tail(&xfer[0], &msg); + spi_sync(gf_dev->spi, &msg); + + kfree(xfer); + if (xfer != NULL) + xfer = NULL; + + return 0; +} + +int gfspi_spi_read_byte(struct gf_device *gf_dev, u16 addr, u8 *value) +{ + struct spi_message msg; + struct spi_transfer *xfer = NULL; + + xfer = kzalloc(sizeof(*xfer) * 2, GFP_KERNEL); + if (xfer == NULL) + return -ENOMEM; + + spi_message_init(&msg); + *gf_dev->spi_buffer = 0xF0; + *(gf_dev->spi_buffer + 1) = (u8)((addr >> 8) & 0xFF); + *(gf_dev->spi_buffer + 2) = (u8)(addr & 0xFF); + + xfer[0].tx_buf = gf_dev->spi_buffer; + xfer[0].len = 3; + xfer[0].delay_usecs = 5; + spi_message_add_tail(&xfer[0], &msg); + spi_sync(gf_dev->spi, &msg); + + spi_message_init(&msg); + /* 4 bytes align */ + *(gf_dev->spi_buffer + 4) = 0xF1; + xfer[1].tx_buf = gf_dev->spi_buffer + 4; + xfer[1].rx_buf = gf_dev->spi_buffer + 4; + xfer[1].len = 2; + xfer[1].delay_usecs = 5; + spi_message_add_tail(&xfer[1], &msg); + spi_sync(gf_dev->spi, &msg); + + *value = *(gf_dev->spi_buffer + 5); + + kfree(xfer); + if (xfer != NULL) + xfer = NULL; + + return 0; +} + +int gfspi_spi_write_byte(struct gf_device *gf_dev, u16 addr, u8 value) +{ + struct spi_message msg; + struct spi_transfer *xfer = NULL; + + xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); + if (xfer == NULL) + return -ENOMEM; + + spi_message_init(&msg); + *gf_dev->spi_buffer = 0xF0; + *(gf_dev->spi_buffer + 1) = (u8)((addr >> 8) & 0xFF); + *(gf_dev->spi_buffer + 2) = (u8)(addr & 0xFF); + *(gf_dev->spi_buffer + 3) = value; + + xfer[0].tx_buf = gf_dev->spi_buffer; + xfer[0].len = 3 + 1; + xfer[0].delay_usecs = 5; + spi_message_add_tail(&xfer[0], &msg); + spi_sync(gf_dev->spi, &msg); + + kfree(xfer); + if (xfer != NULL) + xfer = NULL; + + return 0; +} + +static int gfspi_spi_transfer_raw(struct gf_device *gf_dev, u8 *tx_buf, + u8 *rx_buf, u32 len) +{ + struct spi_message msg; + struct spi_transfer xfer; + + spi_message_init(&msg); + memset(&xfer, 0, sizeof(struct spi_transfer)); + + xfer.tx_buf = tx_buf; + xfer.rx_buf = rx_buf; + xfer.len = len; + spi_message_add_tail(&xfer, &msg); + spi_sync(gf_dev->spi, &msg); + + return 0; +} + +int gfspi_ioctl_transfer_raw_cmd(struct gf_device *gf_dev, + unsigned long arg, unsigned int bufsiz) +{ + struct gf_ioc_transfer_raw ioc_xraw; + int retval = 0; +#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY + struct gf_ioc_transfer_raw_32 ioc_xraw_32; + u64 read_buf_64; + u64 write_buf_64; +#endif + + do { + u8 *tx_buf; + u8 *rx_buf; + uint32_t len; + +#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY + if (copy_from_user(&ioc_xraw_32, (void __user *)arg, + sizeof(struct gf_ioc_transfer_raw_32))) +#else + if (copy_from_user(&ioc_xraw, (void __user *)arg, + sizeof(struct gf_ioc_transfer_raw))) +#endif + { + pr_err("%s: Failed to copy gf_ioc_transfer_raw from user to kernel\n", + __func__); + retval = -EFAULT; + break; + } + +#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY + read_buf_64 = (u64)ioc_xraw_32.read_buf; + write_buf_64 = (u64)ioc_xraw_32.write_buf; + ioc_xraw.read_buf = (u8 *)read_buf_64; + ioc_xraw.write_buf = (u8 *)write_buf_64; + ioc_xraw.high_time = ioc_xraw_32.high_time; + ioc_xraw.len = ioc_xraw_32.len; + ioc_xraw.low_time = ioc_xraw_32.low_time; +#endif + + if ((ioc_xraw.len > bufsiz) || (ioc_xraw.len == 0)) { + pr_err("%s: request transfer length larger than maximum buffer\n", + __func__); + pr_err("%s: buf max %x, buf_len %x\n", __func__, bufsiz, ioc_xraw.len); + retval = -EINVAL; + break; + } + + if (ioc_xraw.read_buf == NULL || ioc_xraw.write_buf == NULL) { + pr_err("%s: read buf and write buf can not equal to NULL simultaneously.\n", + __func__); + retval = -EINVAL; + break; + } + + /* change speed and set transfer mode */ + gfspi_spi_setup_conf(gf_dev, ioc_xraw.high_time); + + len = ioc_xraw.len; + + tx_buf = kzalloc(len, GFP_KERNEL); + if (tx_buf == NULL) { + pr_err("%s: failed to allocate tx buffer\n", + __func__); + retval = -EMSGSIZE; + break; + } + + rx_buf = kzalloc(len, GFP_KERNEL); + if (rx_buf == NULL) { + kfree(tx_buf); + pr_err("%s: failed to allocate rx buffer\n", + __func__); + retval = -EMSGSIZE; + break; + } + + if (copy_from_user(tx_buf, (void __user *)ioc_xraw.write_buf, + ioc_xraw.len)) { + kfree(tx_buf); + kfree(rx_buf); + pr_err("Failed to copy gf_ioc_transfer from user to kernel\n"); + retval = -EFAULT; + break; + } + + gfspi_spi_transfer_raw(gf_dev, tx_buf, rx_buf, len); + + if (copy_to_user((void __user *)ioc_xraw.read_buf, + rx_buf, ioc_xraw.len)) { + pr_err("Failed to copy gf_ioc_transfer_raw from kernel to user\n"); + retval = -EFAULT; + } + + kfree(tx_buf); + kfree(rx_buf); + } while (0); + + return retval; +} + +int gfspi_ioctl_spi_init_cfg_cmd(struct gf_device *gf_dev, unsigned long arg) +{ + return 0; +} +#endif + +/*GPIO pins reference.*/ +int gfspi_get_gpio_dts_info(struct device *dev, struct gf_device *gf_dev) +{ + struct device_node *np = dev->of_node; + int status = 0; + + /*get pwr resource*/ + gf_dev->pwr_gpio = of_get_named_gpio(np, "goodix,gpio_pwr", 0); + if (!gpio_is_valid(gf_dev->pwr_gpio)) { + pr_err("%s, PWR GPIO is invalid.\n", __func__); + return -1; + } + pr_info("%s, goodix_pwr:%d\n", __func__, gf_dev->pwr_gpio); + status = gpio_request(gf_dev->pwr_gpio, "goodix_pwr"); + if (status < 0) { + pr_err("%s, Failed to request PWR GPIO. rc = %d\n", + __func__, status); + return status; + } + gpio_direction_output(gf_dev->pwr_gpio, 0); + + /*get reset resource*/ + gf_dev->reset_gpio = of_get_named_gpio(np, "goodix,gpio_reset", 0); + if (!gpio_is_valid(gf_dev->reset_gpio)) { + pr_err("%s, RESET GPIO is invalid.\n", __func__); + return -1; + } + pr_info("%s, goodix_reset:%d\n", + __func__, gf_dev->reset_gpio); + status = gpio_request(gf_dev->reset_gpio, "goodix_reset"); + if (status < 0) { + pr_err("%s, Failed to request RESET GPIO. rc = %d\n", + __func__, status); + return status; + } + gpio_direction_output(gf_dev->reset_gpio, 0); + + /*get irq resourece*/ + gf_dev->irq_gpio = of_get_named_gpio(np, "goodix,gpio_irq", 0); + if (!gpio_is_valid(gf_dev->irq_gpio)) { + pr_err("%s, IRQ GPIO is invalid.\n", __func__); + return -1; + } + pr_info("%s, irq_gpio:%d\n", __func__, gf_dev->irq_gpio); + status = gpio_request(gf_dev->irq_gpio, "goodix_irq"); + if (status < 0) { + pr_err("%s, Failed to request IRQ GPIO. rc = %d\n", + __func__, status); + return status; + } + gpio_direction_input(gf_dev->irq_gpio); + + if (of_property_read_u32(np, "goodix,min_cpufreq_limit", + &gf_dev->min_cpufreq_limit)) + gf_dev->min_cpufreq_limit = 0; + + if (of_property_read_string_index(np, "goodix,chip_id", 0, + (const char **)&gf_dev->chipid)) + gf_dev->chipid = "NULL"; + pr_info("%s, Chip ID:%s\n", __func__, gf_dev->chipid); + + gf_dev->p = pinctrl_get_select(dev, "default"); + if (IS_ERR(gf_dev->p)) { + status = -EINVAL; + pr_err("%s: failed pinctrl_get\n", __func__); + goto fail_pinctrl_get; + } + + gf_dev->pins_sleep = pinctrl_lookup_state(gf_dev->p, "sleep"); + if (IS_ERR(gf_dev->pins_sleep)) { + pr_err("%s : could not get pins sleep_state (%li)\n", + __func__, PTR_ERR(gf_dev->pins_sleep)); + status = -EINVAL; + goto fail_pinctrl_get; + } + + gf_dev->pins_idle = pinctrl_lookup_state(gf_dev->p, "idle"); + if (IS_ERR(gf_dev->pins_idle)) { + pr_err("%s : could not get pins idle_state (%li)\n", + __func__, PTR_ERR(gf_dev->pins_idle)); + goto fail_pinctrl_get; + } + + gfspi_pin_control(gf_dev, false); + + if (of_property_read_u32(np, "goodix,orient", &gf_dev->orient)) + gf_dev->orient = 0; + pr_info("%s: orient=%d\n", __func__, gf_dev->orient); + +fail_pinctrl_get: + pinctrl_put(gf_dev->p); + return status; +} + +void gfspi_cleanup_info(struct gf_device *gf_dev) +{ + if (gpio_is_valid(gf_dev->irq_gpio)) { + gpio_free(gf_dev->irq_gpio); + pr_debug("%s, remove irq_gpio.\n", __func__); + } + if (gpio_is_valid(gf_dev->reset_gpio)) { + gpio_free(gf_dev->reset_gpio); + pr_debug("%s, remove reset_gpio.\n", __func__); + } + if (gpio_is_valid(gf_dev->pwr_gpio)) { + gpio_free(gf_dev->pwr_gpio); + pr_debug("%s, remove pwr_gpio.\n", __func__); + } +} + +int gfspi_spi_clk_enable(struct gf_device *gf_dev) +{ + int ret_val = 0; +#ifdef ENABLE_SENSORS_FPRINT_SECURE + if (gf_dev->enabled_clk) { + pr_info("%s already enabled same clock.\n", + __func__); + return ret_val; + } + pr_info("%s ENABLE_SPI_CLOCK %ld\n", + __func__, (long int)gf_dev->spi->max_speed_hz); + wake_lock(&gf_dev->wake_lock); + gf_dev->enabled_clk = true; +#endif + return ret_val; +} + +int gfspi_spi_clk_disable(struct gf_device *gf_dev) +{ + int ret_val = 0; +#ifdef ENABLE_SENSORS_FPRINT_SECURE + if (gf_dev->enabled_clk) { + pr_info("%s DISABLE_SPI_CLOCK\n", __func__); + wake_unlock(&gf_dev->wake_lock); + gf_dev->enabled_clk = false; + pr_info("%s, clk disalbed\n", __func__); + } +#endif + return ret_val; +} + +int gfspi_pin_control(struct gf_device *gf_dev, bool pin_set) +{ + int status = 0; + + if (pin_set) { + if (!IS_ERR(gf_dev->pins_idle)) { + status = pinctrl_select_state(gf_dev->p, + gf_dev->pins_idle); + if (status) + pr_err("%s: can't set pin default state\n", + __func__); + pr_debug("%s idle\n", __func__); + } + } else { + if (!IS_ERR(gf_dev->pins_sleep)) { + status = pinctrl_select_state(gf_dev->p, + gf_dev->pins_sleep); + if (status) + pr_err("%s: can't set pin sleep state\n", + __func__); + pr_debug("%s sleep\n", __func__); + } + } + return status; +} diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index cd84934774cc..27d1c5f5fc0f 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -28,4 +28,9 @@ config FPGA_MGR_ZYNQ_FPGA endif # FPGA +config POGO_FPGA + tristate "fpga for pogo keyboard" + help + FPGA manager driver support for pogo fpga. + endmenu diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 8d83fc6b1613..d708a242d6fc 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o # FPGA Manager Drivers obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o +obj-$(CONFIG_POGO_FPGA) += pogo_fpga_dev.o diff --git a/drivers/fpga/pogo_fpga_dev.c b/drivers/fpga/pogo_fpga_dev.c new file mode 100644 index 000000000000..aab947e7569b --- /dev/null +++ b/drivers/fpga/pogo_fpga_dev.c @@ -0,0 +1,739 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define POGO_FPGA_NAME "pogo_expander" + +/* + * This supports access to SPI devices using normal userspace I/O calls. + * Note that while traditional UNIX/POSIX I/O semantics are half duplex, + * and often mask message boundaries, full SPI support requires full duplex + * transfers. There are several kinds of internal message boundaries to + * handle chipselect management and other protocol options. + * + * SPI has a character major number assigned. We allocate minor numbers + * dynamically using a bitmask. You must use hotplug tools, such as udev + * (or mdev with busybox) to create and destroy the /dev/spidevB.C device + * nodes, since there is no fixed association of minor numbers with any + * particular SPI bus or device. + */ +#define SPIDEV_MAJOR 154 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ +static DECLARE_BITMAP(minors, N_SPI_MINORS); + +/* Bit masks for spi_device.mode management. Note that incorrect + * settings for some settings can cause *lots* of trouble for other + * devices on a shared bus: + * + * - CS_HIGH ... this device will be active when it shouldn't be + * - 3WIRE ... when active, it won't behave as it should + * - NO_CS ... there will be no explicit message boundaries; this + * is completely incompatible with the shared bus model + * - READY ... transfers may proceed when they shouldn't. + * + * REVISIT should changing those flags be privileged? + */ +#define SPI_MODE_MASK \ + (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ + | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \ + | SPI_NO_CS | SPI_READY) + +static struct pogo_fpga_platform_data pogo_fpga_pdata = +{ + .fpga_cdone = 8, + .fpga_pogo_ldo_en = 31, + .fpga_gpio_reset = 85, + .fpga_gpio_crst_b = GPIO_CRST_B, + .fpga_gpio_spi_cs = GPIO_CS, +}; + +static struct class *pogo_fpga_class; +static uint8_t __maybe_unused tx_header_100_dummy_clocks[13] = { 0 }; // 100 clocks ~= 13 dummy bytes transfered//msd cmtd + +struct pogo_fpga *g_pogo_fpga; + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + + +unsigned bufsiz = 9000; +module_param(bufsiz, uint, S_IRUGO); +MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); + +extern void fpga_rstn_control(void) +{ + pr_info("%s: high->low->high\n", __func__); + + gpio_set_value(g_pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_HIGH); + gpio_set_value(g_pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_LOW); + mdelay(1); + gpio_set_value(g_pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_HIGH); + +} + +extern void fpga_rstn_high(void) +{ + gpio_set_value(g_pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_HIGH); +} + +extern bool is_fw_dl_completed(void) +{ + int cdone = 0; + cdone = gpio_get_value(g_pogo_fpga->pdata->fpga_cdone); + + return cdone ? true : false; +} + +int pogo_fpga_rx_data( + struct pogo_fpga *pogo_fpga, + char *txbuf, + char *rxbuf, + int len) +{ + int err; + u8 mode; + struct spi_device *spi; + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = txbuf, + .rx_buf = rxbuf, + .len = len, + .cs_change = 0, + .speed_hz=SPI_CLK_FREQ, + }; + + mode = 0; //spi mode3 is set in probe funcion but here we set for spi mode0//if we want spi_mode_3 we can commnet these 3 lines + spi = spi_dev_get(pogo_fpga->spi_client); + spi->mode=mode; + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_spi_cs, 0); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + err = spi_sync(pogo_fpga->spi_client, &msg); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_spi_cs, 1); + + return err; +} + +int pogo_fpga_tx_data( + struct pogo_fpga *pogo_fpga, + char *txbuf, + int len) +{ + int err; + u8 mode; + struct spi_device *spi; + struct spi_message msg; + + struct spi_transfer xfer = { + .tx_buf = txbuf, + .rx_buf = NULL, + .len = len, + .cs_change = 0, + .speed_hz=SPI_CLK_FREQ, + }; + + mode = 0;//spi mode3 is set in probe funcion but here we set for spi mode0//if we want spi_mode_3 we can commnet these 3 lines + spi = spi_dev_get(pogo_fpga->spi_client); + spi->mode=mode; + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_spi_cs, 0); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + err = spi_sync(pogo_fpga->spi_client, &msg); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_spi_cs, 1); + + return err; +} + +static struct attribute *pogo_fpga_attributes[] = +{ + NULL +}; + +static const struct attribute_group pogo_fpga_group_attr = +{ + .attrs = pogo_fpga_attributes, +}; + +int pogo_fpga_config(struct pogo_fpga *pogo_fpga,char *bitstream) +{ + int rc = 0; + struct spi_device *spi_client = pogo_fpga->spi_client; + uint8_t config_fw[5]; + uint8_t *image_data = NULL; + + const struct firmware *fw = NULL; + const uint8_t *sensorHubImage = NULL; + size_t imageSize = 0; + + struct pinctrl *cs_pinctrl; + + pr_info("[FPGA] %s ++ \n", __func__); + pogo_fpga->fw_file_name = bitstream;//assigning bitstream which we got from userapp + + if (pogo_fpga->fw_file_name != NULL) { + dev_err(&spi_client->dev,"requesting Firmware %s",pogo_fpga->fw_file_name); + + pr_info("[FPGA] %s request_firmware ++ \n", __func__); + rc = request_firmware(&fw,pogo_fpga->fw_file_name,&spi_client->dev); + pr_info("[FPGA] %s request firmware -- \n", __func__); + if (rc) { + dev_err(&spi_client->dev, + "qot load microcode data from %s error %d", + pogo_fpga->fw_file_name, rc); + pr_info("[FPGA]%s : firmware load fail\n",__func__); + return rc; + } + dev_err(&spi_client->dev, "Firmware read size %d",(int)(fw->size)); + sensorHubImage = fw->data; + imageSize = fw->size; + } + + cs_pinctrl = devm_pinctrl_get_select(&spi_client->dev, "fpga_cdone"); + if (IS_ERR(cs_pinctrl)) { + pr_err("[FPGA]: cdone pin select error \n"); + } + + cs_pinctrl = devm_pinctrl_get_select(&spi_client->dev, "fpga_pogo_ldo_en"); + if (IS_ERR(cs_pinctrl)) { + pr_err("[FPGA]: pogo_ldo_en pin select error \n"); + } + + if (!pogo_fpga->is_gpio_allocated) { + rc = gpio_request(pogo_fpga->pdata->fpga_cdone, "FPGA_CDONE"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_cdone, rc); + return rc; + } + + rc = gpio_direction_input(pogo_fpga->pdata->fpga_cdone); + if (rc) { + pr_err("%s: unable to set direction input gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_cdone, rc); + return rc; + } + pr_info("[FPGA],%s : CDONE %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_cdone)); + + rc = gpio_request(pogo_fpga->pdata->fpga_pogo_ldo_en, "POGO_LDO_EN"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_pogo_ldo_en, rc); + return rc; + } + + rc = gpio_direction_output(pogo_fpga->pdata->fpga_pogo_ldo_en, 1); + if (rc) { + pr_err("%s: unable to set direction output gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_pogo_ldo_en, rc); + return rc; + } + pr_info("[FPGA],%s : POGO_LDO_EN %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_pogo_ldo_en)); + + rc = gpio_request(pogo_fpga->pdata->fpga_gpio_reset, "FPGA_RESET"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_gpio_reset, rc); + return rc; + } + + rc = gpio_direction_output(pogo_fpga->pdata->fpga_gpio_reset, 1); + if (rc) { + pr_err("%s: unable to set direction output gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_gpio_reset, rc); + return rc; + } + pr_info("[FPGA],%s : reset %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_reset)); + + rc = gpio_request(pogo_fpga->pdata->fpga_gpio_crst_b, "FPGA_CRST"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_gpio_crst_b, rc); + return rc; + } + + rc = gpio_direction_output(pogo_fpga->pdata->fpga_gpio_crst_b, 1); + if (rc) { + pr_err("%s: unable to set direction output gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_gpio_crst_b, rc); + return rc; + } + pr_info("[FPGA],%s : crst_b %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_crst_b)); + + rc = gpio_request(pogo_fpga->pdata->fpga_gpio_spi_cs, "FPGA_CS"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_gpio_spi_cs, rc); + return rc; + } + + rc = gpio_direction_output(pogo_fpga->pdata->fpga_gpio_spi_cs, 1); + if (rc) { + pr_err("%s: unable to direction output gpio %d (%d)\n", + __func__, pogo_fpga->pdata->fpga_gpio_spi_cs, rc); + return rc; + } + pr_info("[FPGA],%s : cs %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_spi_cs)); + + pogo_fpga->is_gpio_allocated = 1; + } + + pr_info("[FPGA]%s : after gpio allocate\n",__func__); + + if (pogo_fpga->fw_file_name != NULL) { + /* + * Extra bytes appended to the data to generate extra 100 clocks after transfering actual data + */ + image_data = (uint8_t *)kzalloc(imageSize + sizeof(tx_header_100_dummy_clocks), GFP_KERNEL); + if (image_data == NULL) { + return -ENOMEM; + } + + memcpy(image_data, sensorHubImage, imageSize); + memset(image_data + imageSize, 0, sizeof(tx_header_100_dummy_clocks)); /* 100 clocks ~= 13 dummy bytes transfered */ + + gpio_set_value(pogo_fpga->pdata->fpga_gpio_spi_cs, 0); + pr_info("[FPGA],%s : cs %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_spi_cs)); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_crst_b, 0); + pr_info("[FPGA],%s : crst_b %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_crst_b)); + udelay (1000); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_crst_b, 1); + pr_info("[FPGA],%s : crst_b %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_crst_b)); + udelay (1000); /* Wait for min 1000 microsec to clear internal configuration memory */ + + pogo_fpga_tx_data(pogo_fpga, image_data, imageSize + sizeof(tx_header_100_dummy_clocks)); + gpio_set_value (pogo_fpga->pdata->fpga_gpio_spi_cs, 1); + pr_info("[FPGA],%s : cs %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_spi_cs)); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_reset, 0); + pr_info("[FPGA],%s : reset %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_reset)); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_reset, 1); + pr_info("[FPGA],%s : reset %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_reset)); + + /*delay 1ms : to settle down FPGA*/ + mdelay(1); + + /*Reset Sequence is added*/ + config_fw[0] = 0x00; + config_fw[1] = 0x00; + config_fw[2] = 0xFF; + config_fw[3] = 0xFF; + config_fw[4] = 0x80; + pogo_fpga_tx_data(pogo_fpga, config_fw, sizeof(config_fw)); + memset(config_fw, 0, sizeof(config_fw)); + + config_fw[0] = 0x08; + config_fw[1] = 0x00; + config_fw[2] = 0xFF; + config_fw[3] = 0xFF; + config_fw[4] = 0x80; + pogo_fpga_tx_data(pogo_fpga, config_fw, sizeof(config_fw)); + memset(config_fw, 0, sizeof(config_fw)); + + config_fw[0] = 0x10; + config_fw[1] = 0x00; + config_fw[2] = 0xFF; + config_fw[3] = 0xFF; + config_fw[4] = 0x80; + pogo_fpga_tx_data(pogo_fpga, config_fw, sizeof(config_fw)); + memset(config_fw, 0, sizeof(config_fw)); + + kfree (image_data); + release_firmware(fw); + udelay (1000); /* Wait for min 1000 microsec to clear internal configuration memory */ + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_LOW); + pr_info("[FPGA],%s : reset %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_reset)); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_HIGH); + pr_info("[FPGA],%s : reset %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_reset)); + + mdelay(10); + + gpio_set_value (pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_LOW); + pr_info("[FPGA],%s : reset %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_gpio_reset)); + } + + pogo_fpga->is_configured = 1; + + return rc; +} + +int pogo_fpga_read_fw_version(struct pogo_fpga *pogo_fpga) +{ + int ret = 0; +#if 0 + struct spi_list_s *i2c_cmd; + + i2c_cmd = pogo_fpga->spi_cmd[0]; + + mutex_lock(&i2c_cmd->device_lock); + i2c_cmd->cmds->ret = 0; + + memset(&i2c_cmd->cmds->data_tx0, 0x00, 300); + memset(&i2c_cmd->cmds->data_tx1, 0x00, 20); + memset(&i2c_cmd->cmds->data_tx2, 0x00, 20); + memset(&i2c_cmd->cmds->data_tx3, 0x00, 20); + memset(&i2c_cmd->cmds->data_rx0, 0x00, 2040); + memset(&i2c_cmd->cmds->data_rx1, 0x00, 10); + + i2c_cmd->cmds->data_tx0[0] = 0x87; + i2c_cmd->cmds->tx_length0 = 1; + i2c_cmd->cmds->rx_length0 = 1; + i2c_cmd->cmds->op_code = DEVICE_I2C_RD; +// fpga_work(pogo_fpga, i2c_cmd); + + if(i2c_cmd->cmds->ret !=0){ + ret = i2c_cmd->cmds->ret; //SPI return value check.. + mutex_unlock(&i2c_cmd->device_lock); + pr_err("%s(%d): SPI Error :%d\n", __func__, __LINE__, ret); + return ret; + } + + if(i2c_cmd->cmds->data_rx0[1] != FPGA_FW_VERSION ) { + pr_err("%s(%d): FPGA_FW_VERSION is wrong. original value : 0x%x , wrong value : 0x%x \n", __func__, __LINE__, FPGA_FW_VERSION, i2c_cmd->cmds->data_rx0[1]); + ret = FW_VERSION_MISMATCH; + } else + pr_info("[FPGA] FPGA_FW_VERSION is 0x%x\n", i2c_cmd->cmds->data_rx0[1]); + + mutex_unlock(&i2c_cmd->device_lock); +#endif + return ret; +} + +static int pogo_fpga_parse_dt(struct pogo_fpga *pogo_fpga) +{ + struct device_node *np = pogo_fpga->spi_client->dev.of_node; +// int ret; + + pogo_fpga->pdata->fpga_cdone = of_get_named_gpio(np, "fpga,gpio_cdone", 0); + if (!gpio_is_valid(pogo_fpga->pdata->fpga_cdone)) { + pr_err("%s:%d, fpga_cdone not specified\n", __func__, __LINE__); + return -1; + } + pr_info("[FPGA] %s: CDONE : %d \n", __func__, pogo_fpga->pdata->fpga_cdone); + + pogo_fpga->pdata->fpga_pogo_ldo_en = of_get_named_gpio(np, "fpga,pogo_ldo_en", 0); + if (!gpio_is_valid(pogo_fpga->pdata->fpga_pogo_ldo_en)) { + pr_err("%s:%d, pogo_ldo_en not specified\n", __func__, __LINE__); + return -1; + } + pr_info("[FPGA] %s: LDO_EN : %d \n", __func__, pogo_fpga->pdata->fpga_pogo_ldo_en); + + pogo_fpga->pdata->fpga_gpio_reset = of_get_named_gpio(np, "fpga,gpio_reset", 0); + if (!gpio_is_valid(pogo_fpga->pdata->fpga_gpio_reset)) { + pr_err("%s:%d, reset gpio not specified\n", __func__, __LINE__); + return -1; + } + pr_info("[FPGA] %s: reset : %d \n", __func__, pogo_fpga->pdata->fpga_gpio_reset); + + pogo_fpga->pdata->fpga_gpio_crst_b = of_get_named_gpio(np, "fpga,gpio_crst_b", 0); + if (!gpio_is_valid(pogo_fpga->pdata->fpga_gpio_crst_b)) { + pr_err("%s:%d, reset gpio not specified\n", __func__, __LINE__); + return -1; + } + pr_info("[FPGA] %s: creset_b : %d \n", __func__, pogo_fpga->pdata->fpga_gpio_crst_b); + + pogo_fpga->pdata->fpga_gpio_spi_cs = of_get_named_gpio(np, "fpga,gpio_cs", 0); + if (!gpio_is_valid(pogo_fpga->pdata->fpga_gpio_spi_cs)) { + pr_err("%s:%d, cs gpio not specified\n", __func__, __LINE__); + return -1; + } + pr_info("[FPGA] %s: cs : %d \n", __func__, pogo_fpga->pdata->fpga_gpio_spi_cs); + + return 0; +} + +static int pogo_fpga_probe(struct spi_device *spi_client) +{ + unsigned long minor; + int err = 0; +// int retry_count = 0; + + struct pogo_fpga *pogo_fpga = NULL; + + pr_info("[FPGA] %s ++ \n", __func__); + + dev_err(&spi_client->dev, "pogo_fpga_probe!!!!!!!!!!!\n"); + + pogo_fpga = kzalloc(sizeof(struct pogo_fpga), GFP_KERNEL); + if (!pogo_fpga) { + dev_dbg(&spi_client->dev, "unable to allocate memory\n"); + err = -ENOMEM; + goto exit; + } + + pogo_fpga->spi_client = spi_client; + + spi_client->dev.platform_data = &pogo_fpga_pdata; /* MDN Addition */ + pogo_fpga->pdata = spi_client->dev.platform_data; + if (!pogo_fpga->pdata) { + dev_dbg(&spi_client->dev, "No platform data - aborting\n"); + err = -EINVAL; + goto exit; + } + + err = pogo_fpga_parse_dt(pogo_fpga); + if(err) { + goto mem_cleanup; + } + + if(pogo_fpga->vdd != NULL) { + err = regulator_enable(pogo_fpga->vdd); + if (err) { + printk(KERN_ERR"enable ldo failed, rc=%d\n", err); + goto mem_cleanup; + } + } + + pogo_fpga->spi_bus_num = spi_client->master->bus_num; + + /* Configure the SPI bus */ + /* not setting SPI_CS_HIGH SPI_NO_CS SPI_LSB_FIRST SPI_3WIRE SPI_READY */ + /* so it is MSB out first, CS active low, not 3 wire mode, no SPI ready support */ + +// spi_client->mode = (SPI_MODE_3); //spi mode we can set here but now we set mode 0 on pogo_fpga_tx_data_msd() and pogo_fpga_rx_data_msd funcs..msd + spi_client->mode = SPI_MODE_0; //spi mode we can set here but now we set mode 0 on + + spi_client->bits_per_word = 8; + spi_setup(spi_client); + + spi_set_drvdata(spi_client, pogo_fpga); + + g_pogo_fpga = pogo_fpga; + + mutex_init(&pogo_fpga->mutex); + + err = sysfs_create_group(&spi_client->dev.kobj, &pogo_fpga_group_attr); + if (err) + goto err_remove_files; + + spin_lock_init(&pogo_fpga->spi_lock); + mutex_init(&pogo_fpga->buf_lock); + + INIT_LIST_HEAD(&pogo_fpga->device_entry); + + BUILD_BUG_ON(N_SPI_MINORS > 256); + + err = 0; + pogo_fpga_class = class_create(THIS_MODULE, "fpga_class"); + if (IS_ERR(pogo_fpga_class)) { + dev_err(&spi_client->dev, "class_create spidev failed!\n"); + err = PTR_ERR(pogo_fpga_class); + goto err_remove_spidev_chrdev; + } + + err = 0; + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + + pogo_fpga->devt = MKDEV(SPIDEV_MAJOR, minor); + dev = device_create(pogo_fpga_class, &spi_client->dev, pogo_fpga->devt, + pogo_fpga, "fpga_spi"); + + if (IS_ERR(dev)) { + err = PTR_ERR(dev); + dev_err(&spi_client->dev, "device_create failed!\n"); + goto err_remove_class; + } + } else { + dev_err(&spi_client->dev, "no minor number available!\n"); + err = -ENODEV; + goto err_remove_class; + } + + set_bit(minor, minors); + list_add(&pogo_fpga->device_entry, &device_list); + + mutex_unlock(&device_list_lock); + +//retry_config: + + err = pogo_fpga_config(pogo_fpga, "fpga/pogo_fpga.fw"); + + if (err) { + dev_err(&spi_client->dev, "unable to configure_iCE\n"); + goto err_remove_files; + } +#if 0 + err = pogo_fpga_read_fw_version(pogo_fpga); + + if(err == FW_VERSION_MISMATCH) { + if ( retry_count <= 3) { + retry_count ++; + goto retry_config; + } else { + dev_err(&spi_client->dev, "retry_count for fw_config was over 3times.\n"); + goto err_remove_files; + } + } +#endif + if (err) { + goto err_remove_files; + } + + pr_info("[FPGA],%s : CDONE %d\n", __func__, gpio_get_value (pogo_fpga->pdata->fpga_cdone)); + pr_info("[FPGA] %s -- \n", __func__); + + udelay(1000); + goto exit; + +err_remove_class: + mutex_unlock(&device_list_lock); + class_destroy(pogo_fpga_class); + +err_remove_spidev_chrdev: + unregister_chrdev(SPIDEV_MAJOR, POGO_FPGA_NAME); + +err_remove_files: + sysfs_remove_group(&spi_client->dev.kobj, &pogo_fpga_group_attr); + +mem_cleanup: + if (pogo_fpga) { + kfree(pogo_fpga); + pogo_fpga = NULL; + } + +exit: + return err; +} + +static int pogo_fpga_remove(struct spi_device *spi_client) +{ + struct pogo_fpga *pogo_fpga = spi_get_drvdata(spi_client); + + sysfs_remove_group(&spi_client->dev.kobj, &pogo_fpga_group_attr); + unregister_chrdev(SPIDEV_MAJOR, POGO_FPGA_NAME); + list_del(&pogo_fpga->device_entry); + device_destroy(pogo_fpga_class, pogo_fpga->devt); + clear_bit(MINOR(pogo_fpga->devt), minors); + class_destroy(pogo_fpga_class); + unregister_chrdev(SPIDEV_MAJOR, POGO_FPGA_NAME); + spi_set_drvdata(spi_client, NULL); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + if (pogo_fpga->users == 0) + kfree(pogo_fpga); + mutex_unlock(&device_list_lock); + + return 0; +} + +static int pogo_fpga_resume(struct device *dev) +{ + struct pogo_fpga *pogo_fpga = dev_get_drvdata(dev); + uint8_t fpga_resume[5] = {0x38, 0x00, 0xff, 0xff, 0x01}; + int ret; + + pr_info("[FPGA],%s\n", __func__); + + ret = pogo_fpga_tx_data(pogo_fpga, fpga_resume, sizeof(fpga_resume)); + if (ret) { + pr_info("[FPGA],%s resume state: %d\n", __func__, ret); + } + return 0; +} + +static int pogo_fpga_suspend(struct device *dev) +{ + struct pogo_fpga *pogo_fpga = dev_get_drvdata(dev); + uint8_t fpga_suspend[5] = {0x38, 0x00, 0xff, 0xff, 0x00}; + int ret; + + pr_info("[FPGA],%s\n", __func__); + + ret = pogo_fpga_tx_data(pogo_fpga, fpga_suspend, sizeof(fpga_suspend)); + if (ret) { + pr_info("[FPGA],%s suspend state: %d\n", __func__, ret); + } + + gpio_set_value(g_pogo_fpga->pdata->fpga_gpio_reset, GPIO_LEVEL_LOW); + return 0; +} + +static SIMPLE_DEV_PM_OPS(pogo_fpga_pm_ops, pogo_fpga_suspend, + pogo_fpga_resume); + +static struct of_device_id pogo_fpga_of_table[] = +{ + { .compatible = "pogo_fpga" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pogo_fpga_of_table); + +static struct spi_driver pogo_fpga_driver = +{ + .driver = { + .name = "pogo_fpga", + .of_match_table = pogo_fpga_of_table, + .pm = &pogo_fpga_pm_ops, + }, + .probe = pogo_fpga_probe, + .remove = pogo_fpga_remove, +}; + +static int __init pogo_fpga_init(void) +{ + int ret = 0; + + pr_info("[FPGA] %s \n", __func__); + ret = spi_register_driver(&pogo_fpga_driver); + if(ret < 0) { + pr_info("[FPGA] spi register failed: %d\n", ret); + return ret; + } + pr_info("[FPGA] %s done\n", __func__); + return ret; +} + +static void __exit pogo_fpga_exit(void) +{ + spi_unregister_driver(&pogo_fpga_driver); +} + +module_init(pogo_fpga_init); +module_exit(pogo_fpga_exit); + +MODULE_DESCRIPTION("FPGA I2C Expander Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5060064acd45..d1b06cf28880 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -822,6 +822,12 @@ config GPIO_TS4900 Say yes here to enabled the GPIO driver for Technologic's FPGA core. Series supported include TS-4100, TS-4900, TS-7970 and TS-7990. +config GPIO_PCAL6524 + bool "PCAL6524 GPIO expander" + depends on I2C + help + GPIO driver for PCAL6524 I2C-based GPIO expander. + endmenu menu "MFD GPIO expanders" @@ -1233,4 +1239,21 @@ config GPIO_VIPERBOARD endmenu +config SEC_GPIO_DVS + tristate "setting Samsung GPIO debugging and verification system" + help + To verify gpio configurations of devices, set this feature. + This feature should be enabled for user-debug mode. + + +config SENSORS_FP_SPI_GPIO_START + depends on SENSORS_FINGERPRINT + int "Fingerprint spi gpio number start" + default 0 + +config SENSORS_FP_SPI_GPIO_END + depends on SENSORS_FINGERPRINT + int "Fingerprint spi gpio number end" + default 0 + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index beba6635cd2c..27575d59b51d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -2,6 +2,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG +ccflags-y := $(KBUILD_FP_SENSOR_CFLAGS) + obj-$(CONFIG_GPIOLIB) += devres.o obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o @@ -143,3 +145,6 @@ obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o obj-$(CONFIG_GPIO_ZX) += gpio-zx.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o obj-$(CONFIG_MSM_SMP2P) += gpio-msm-smp2p.o +obj-$(CONFIG_GPIO_PCAL6524) += gpio-pcal6524.o +obj-$(CONFIG_SEC_PM_DEBUG) += sec-pinmux.o +obj-$(CONFIG_SEC_GPIO_DVS) += secgpio_dvs.o diff --git a/drivers/gpio/gpio-pcal6524.c b/drivers/gpio/gpio-pcal6524.c new file mode 100644 index 000000000000..f9525d1c3d53 --- /dev/null +++ b/drivers/gpio/gpio-pcal6524.c @@ -0,0 +1,1085 @@ +/* + * pcal6524.c - 16 bit I/O port + * + * Copyright (C) 2005 Ben Gardner + * Copyright (C) 2007 Marvell International Ltd. + * Copyright (C) 2013 NXP Semiconductors + * + * Derived from drivers/i2c/chips/pca953x.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_OF) +#include +#include +#endif +#include + +struct pcal6524_chip { + struct i2c_client *client; + struct gpio_chip gpio_chip; + struct dentry *dentry; + struct mutex lock; + struct pcal6524_platform_data *pdata; + unsigned gpio_start; + + uint8_t reg_output[3]; + uint8_t reg_polarity[3]; + uint8_t reg_config[3]; + uint8_t reg_drive0[2]; + uint8_t reg_drive1[2]; + uint8_t reg_drive2[2]; + uint8_t reg_inputlatch[3]; + uint8_t reg_enpullupdown[3]; + uint8_t reg_selpullupdown[3]; +// uint8_t reg_intmask[3]; + uint8_t reg_outputconfig; +}; + +static struct pcal6524_chip *g_dev = NULL; + +/* read the 8-bit register from the PCAL6524 + reg: register address + val: the value read back from the PCAL6524 +*/ +static int pcal6524_read_reg(struct pcal6524_chip *chip, int reg, uint8_t *val) +{ + int ret = i2c_smbus_read_byte_data(chip->client, reg); + + if (ret < 0) { + dev_err(&chip->client->dev, + "failed reading register %d\n", ret); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +/* write a 8-bit value to the PCAL6524 + reg: register address + val: the value read back from the PCAL6524 +*/ +static int pcal6524_write_reg(struct pcal6524_chip *chip, int reg, uint8_t val) +{ + int ret = i2c_smbus_write_byte_data(chip->client, reg, val); + + if (ret < 0) { + dev_err(&chip->client->dev, + "failed writing register %d\n", ret); + return ret; + } + + return 0; +} + +/* +static void print_pcal6524_gpio_state(struct pcal6524_chip *dev) +{ + struct pcal6524_chip *chip = dev; + struct pcal6524_chip chip_state; + int i, drv_str; + int quot = 0, rest = 0; + uint8_t read_input[3]; + char buf[255]; + unsigned int size; + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + pcal6524_read_reg(chip, PCAL6524_INPUT + i, &read_input[i]); + pcal6524_read_reg(chip, PCAL6524_DAT_OUT + i, &chip_state.reg_output[i]); + pcal6524_read_reg(chip, PCAL6524_CONFIG + i, &chip_state.reg_config[i]); + if (i < 2) { + pcal6524_read_reg(chip, PCAL6524_DRIVE0 + i, &chip_state.reg_drive0[i]); + pcal6524_read_reg(chip, PCAL6524_DRIVE1 + i, &chip_state.reg_drive1[i]); + pcal6524_read_reg(chip, PCAL6524_DRIVE2 + i, &chip_state.reg_drive2[i]); + } + pcal6524_read_reg(chip, PCAL6524_EN_PULLUPDOWN + i, &chip_state.reg_enpullupdown[i]); + pcal6524_read_reg(chip, PCAL6524_SEL_PULLUPDOWN + i, &chip_state.reg_selpullupdown[i]); + } + + for (i = 0; i <= PCAL6524_MAX_GPIO; i++) { + memset(buf, 0, sizeof(char) * 255); + size = 0; + size += sprintf(&buf[size], "Expander[3%02d]", i); + quot = i / 8; + rest = i % 8; + + if ((chip_state.reg_config[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " IN"); + else { + if ((chip_state.reg_output[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " OUT_HIGH"); + else + size += sprintf(&buf[size], " OUT_LOW"); + } + + if ((chip_state.reg_enpullupdown[quot] >> rest) & 0x1) { + if ((chip_state.reg_selpullupdown[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " PULL_UP"); + else + size += sprintf(&buf[size], " PULL_DOWN"); + } else + size += sprintf(&buf[size], " PULL_NONE"); + + if (quot == 2) { + if (rest > 3) + drv_str = (chip_state.reg_drive2[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive2[0] >> ((rest)*2)) & 0x3; + } else if (quot == 1) { + if (rest > 3) + drv_str = (chip_state.reg_drive1[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive1[0] >> ((rest)*2)) & 0x3; + } else { + if (rest > 3) + drv_str = (chip_state.reg_drive0[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive0[0] >> ((rest)*2)) & 0x3; + } + + switch(drv_str) { + case GPIO_CFG_6_25MA: + size += sprintf(&buf[size], " DRV_6.25mA"); + break; + case GPIO_CFG_12_5MA: + size += sprintf(&buf[size], " DRV_12.5mA"); + break; + case GPIO_CFG_18_75MA: + size += sprintf(&buf[size], " DRV_18.75mA"); + break; + case GPIO_CFG_25MA: + size += sprintf(&buf[size], " DRV_25mA"); + break; + } + + if ((read_input[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " VAL_HIGH\n"); + else + size += sprintf(&buf[size], " VAL_LOW\n"); + + pr_info("%s : %s\n", __func__, buf); + } + + return; +} +*/ +void pcal6524_sync_structure_with_register(struct pcal6524_chip *dev) +{ + struct pcal6524_chip *chip = dev; + int i; + + pr_info("%s \n", __func__); + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + pcal6524_read_reg(chip, PCAL6524_DAT_OUT + i, &chip->reg_output[i]); + pcal6524_read_reg(chip, PCAL6524_CONFIG + i, &chip->reg_config[i]); + if (i < 2) { + pcal6524_read_reg(chip, PCAL6524_DRIVE0 + i, &chip->reg_drive0[i]); + pcal6524_read_reg(chip, PCAL6524_DRIVE1 + i, &chip->reg_drive1[i]); + pcal6524_read_reg(chip, PCAL6524_DRIVE2 + i, &chip->reg_drive2[i]); + } + pcal6524_read_reg(chip, PCAL6524_EN_PULLUPDOWN + i, &chip->reg_enpullupdown[i]); + pcal6524_read_reg(chip, PCAL6524_SEL_PULLUPDOWN + i, &chip->reg_selpullupdown[i]); + } + return; +} +/* read a port pin value (INPUT register) from the PCAL6524 + off: bit number (0..23) + return: bit value 0 or 1 +*/ +static int pcal6524_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + int ret; + int quot = off / 8; + int rest = off % 8; + uint8_t reg_val; + + struct pcal6524_chip *chip + = container_of(gc, struct pcal6524_chip, gpio_chip); + + mutex_lock(&chip->lock); + ret = pcal6524_read_reg(chip, PCAL6524_INPUT + quot, ®_val); + if (ret < 0) { + /* NOTE: diagnostic already emitted; that's all we should + * do unless gpio_*_value_cansleep() calls become different + * from their nonsleeping siblings (and report faults). + */ + mutex_unlock(&chip->lock); + return ret; + } + + mutex_unlock(&chip->lock); + return (reg_val & (1u << rest)) ? 1 : 0; +} + +/* write a port pin value (INPUT register) from the PCAL6524 + off: bit number (0..23) + val: 0 or 1 + return: none +*/ +static void pcal6524_gpio_set_value(struct gpio_chip *gc, + unsigned off, int val) +{ + int quot = off / 8; + int rest = off % 8; + struct pcal6524_chip *chip + = container_of(gc, struct pcal6524_chip, gpio_chip); + + mutex_lock(&chip->lock); + if (val) + chip->reg_output[quot] |= (1u << rest); + else + chip->reg_output[quot] &= ~(1u << rest); + + pcal6524_write_reg(chip, PCAL6524_DAT_OUT + quot, chip->reg_output[quot]); + pr_info("%s : off =%d, val=%d\n",__func__ ,off , val); + mutex_unlock(&chip->lock); +} + +/* set the CONFIGURATION register of a port pin as an input + off: bit number (0..23) +*/ +static int pcal6524_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + int ret; + int quot = off / 8; + int rest = off % 8; + + struct pcal6524_chip *chip + = container_of(gc, struct pcal6524_chip, gpio_chip); + + pr_info("%s(off=[%d])\n", __func__, off); + mutex_lock(&chip->lock); + /* input set bit 1 */ + chip->reg_config[quot] |= (1u << rest); + ret = pcal6524_write_reg(chip, PCAL6524_CONFIG + quot, chip->reg_config[quot]); + mutex_unlock(&chip->lock); + return ret; +} + +/* set the DIRECTION (CONFIGURATION register) of a port pin as an output + off: bit number (0..23) + val = 1 or 0 + return: 0 if successful +*/ +static int pcal6524_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + int ret; + int quot = off / 8; + int rest = off % 8; + + struct pcal6524_chip *chip + = container_of(gc, struct pcal6524_chip, gpio_chip); + + pr_info("%s(off=[%d], val=[%d])\n", __func__, off, val); + mutex_lock(&chip->lock); + /* set output level */ + if (val) + chip->reg_output[quot] |= (1u << rest); + else + chip->reg_output[quot] &= ~(1u << rest); + ret = pcal6524_write_reg(chip, PCAL6524_DAT_OUT + quot, chip->reg_output[quot]); + + /* then direction */ + /* output set bit 0 */ + chip->reg_config[quot] &= ~(1u << rest); + ret |= pcal6524_write_reg(chip, PCAL6524_CONFIG + quot, chip->reg_config[quot]); + mutex_unlock(&chip->lock); + + return ret; +} + +static int pcal6524_gpio_request(struct gpio_chip *gc, unsigned off) +{ + struct pcal6524_chip *chip + = container_of(gc, struct pcal6524_chip, gpio_chip); + + pr_info("%s gpio %d \n", __func__, off); + if (off >= chip->gpio_chip.ngpio) { + pr_err("[%s] offset over max = [%d]\n", __func__, off); + return 1; + } + /* to do*/ + return 0; +} + +static void pcal6524_gpio_free(struct gpio_chip *gc, unsigned off) +{ + struct pcal6524_chip *chip + = container_of(gc, struct pcal6524_chip, gpio_chip); + + pr_info("%s gpio %d \n", __func__, off); + if (off >= chip->gpio_chip.ngpio) { + pr_err("[%s] offset over max = [%d]\n", __func__, off); + } + /* to do*/ +} + +static int pcal6524_gpio_setup(struct pcal6524_chip *dev) +{ + int ret, i; + pr_info("[%s] GPIO Expander Init setting\n", __func__); + + dev->reg_outputconfig = 0x00; + ret = pcal6524_write_reg(dev, PCAL6524_OUTPUT_CONFIG, dev->reg_outputconfig); /* push-pull */ + if (ret < 0) { + pr_err("failed set output config\n"); + return ret; + } + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + ret = pcal6524_write_reg(dev, PCAL6524_DAT_OUT + i, dev->reg_output[i]); /* 1 : output high, 0 : output low */ + if (ret < 0) { + pr_err("failed set data out\n"); + return ret; + } + } + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + ret = pcal6524_write_reg(dev, PCAL6524_CONFIG + i, dev->reg_config[i]); /* 1 : input, 0 : output */ + if (ret < 0) { + pr_err("failed set config\n"); + return ret; + } + } + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + dev->reg_polarity[i] = 0x00; + ret = pcal6524_write_reg(dev, PCAL6524_POLARITY + i, dev->reg_polarity[i]); + if (ret < 0) { + pr_err("failed set polarity\n"); + return ret; + } + } + + for (i = 0; i < 2; i++) { + dev->reg_drive0[i] = 0x00; + ret = pcal6524_write_reg(dev, PCAL6524_DRIVE0 + i, dev->reg_drive0[i]); /* drive 0.25x */ + if (ret < 0) { + pr_err("failed set drive0\n"); + return ret; + } + } + for (i = 0; i < 2; i++) { + dev->reg_drive1[i] = 0x00; + ret = pcal6524_write_reg(dev, PCAL6524_DRIVE1 + i, dev->reg_drive1[i]); /* drive 0.25x */ + if (ret < 0) { + pr_err("failed set drive1\n"); + return ret; + } + } + for (i = 0; i < 2; i++) { + dev->reg_drive2[i] = 0x00; + ret = pcal6524_write_reg(dev, PCAL6524_DRIVE2 + i, dev->reg_drive2[i]); /* drive 0.25x */ + if (ret < 0) { + pr_err("failed set drive1\n"); + return ret; + } + } + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + dev->reg_inputlatch[i] = 0x00; + ret = pcal6524_write_reg(dev, PCAL6524_INPUT_LATCH + i, dev->reg_inputlatch[i]); /* not use latch */ + if (ret < 0) { + pr_err("failed set input latch\n"); + return ret; + } + } + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + ret = pcal6524_write_reg(dev, PCAL6524_SEL_PULLUPDOWN + i, dev->reg_selpullupdown[i]); /* 1 : pull-up, 0 : pull-down */ + if (ret < 0) { + pr_err("failed set select pull\n"); + return ret; + } + } + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + ret = pcal6524_write_reg(dev, PCAL6524_EN_PULLUPDOWN + i, dev->reg_enpullupdown[i]); /* 1 : enable, 0 : disable */ + if (ret < 0) { + pr_err("failed set enable pullupdown\n"); + return ret; + } + } +#if 0 + dev->reg_intmask = 0xFFFF; + ret = pcal6524_write_reg(dev, PCAL6524_INT_MASK, + dev->reg_intmask); /* not use int */ + if (ret < 0) { + pr_err("failed set int mask\n"); + return ret; + } + ret = pcal6524_read_reg(dev, PCAL6524_INT_MASK, + &read_val); + if (ret < 0) { + pr_err("failed read int mask\n"); + return ret; + } +#endif + + return 0; +} + +#ifdef CONFIG_OF +static int pcal6524_parse_dt(struct device *dev, + struct pcal6524_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + struct pinctrl *reset_pinctrl; + int ret, i, j; + u32 pull_reg[3]; + + ret = of_property_read_u32(np, "pcal6524,gpio_start", &pdata->gpio_start); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,gpio_start\n", __func__); + return ret; + } + + ret = of_property_read_u32(np, "pcal6524,ngpio", &pdata->ngpio); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,ngpio\n", __func__); + return ret; + } + pdata->reset_gpio = of_get_named_gpio(np, "pcal6524,reset-gpio", 0); + /* Get pinctrl if target uses pinctrl */ + reset_pinctrl = devm_pinctrl_get_select(dev, "expander_reset_setting"); + if (IS_ERR(reset_pinctrl)) { + if (PTR_ERR(reset_pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + pr_debug("Target does not use pinctrl\n"); + reset_pinctrl = NULL; + } + + ret = of_property_read_u32(np, "pcal6524,support_initialize", (u32 *)&pdata->support_init); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,support_init\n", __func__); + pdata->support_init = 0; + } + + pdata->vdd = devm_regulator_get(dev, "pcal6524,vdd"); + if (IS_ERR(pdata->vdd)) { + pr_err("%s: cannot get pcal6524,vdd\n", __func__); + ret = -ENOMEM; + return ret; + } else if (!regulator_get_voltage(pdata->vdd)) { + ret = regulator_set_voltage(pdata->vdd, 1800000, 1800000); + if (ret < 0) { + pr_err("regulator set voltage failed, %d\n", + ret); + return ret; + } + } + + if (pdata->support_init) { + ret = of_property_read_u32(np, "pcal6524,config", (u32 *)&pdata->init_config); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,support_init\n", __func__); + pdata->init_config= 0x000000; + } + ret = of_property_read_u32(np, "pcal6524,data_out", (u32 *)&pdata->init_data_out); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,support_init\n", __func__); + pdata->init_data_out = 0x000000; + } + ret = of_property_read_u32(np, "pcal6524,pull_reg_p0", &pull_reg[0]); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,pull_reg_p0\n", __func__); + } + ret = of_property_read_u32(np, "pcal6524,pull_reg_p1", &pull_reg[1]); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,pull_reg_p1\n", __func__); + } + ret = of_property_read_u32(np, "pcal6524,pull_reg_p2", &pull_reg[2]); + if (ret < 0) { + pr_err("[%s]: Unable to read pcal6524,pull_reg_p2\n", __func__); + } + pr_info("[%s] Pull reg P0[0x%04x] P1[0x%04x] P2[0x%04x]\n", __func__, pull_reg[0], pull_reg[1], pull_reg[2]); + pdata->init_en_pull = 0x000000; + pdata->init_sel_pull = 0x000000; + for (j = 0; j < 3; j++){ + for (i = 0; i < 8; i++) { + if (((pull_reg[j]>>(i*2))&0x3) == NO_PULL) { + pdata->init_en_pull &= ~(1 << (i + (8 * j))); + pdata->init_sel_pull &= ~(1 << (i + (8 * j))); + } + else if (((pull_reg[j]>>(i*2))&0x3) == PULL_DOWN) { + pdata->init_en_pull |= (1 << (i + (8 * j))); + pdata->init_sel_pull &= ~(1 << (i + (8 * j))); + } + else if (((pull_reg[j]>>(i*2))&0x3) == PULL_UP) { + pdata->init_en_pull |= (1 << (i + (8 * j))); + pdata->init_sel_pull |= (1 << (i + (8 * j))); + } + } + } + } else { + pdata->init_config = 0x000000; + pdata->init_data_out = 0x000000; + pdata->init_en_pull = 0x000000; + pdata->init_sel_pull = 0x000000; + } + + pr_info("[%s] initialize reg 0x%06x 0x%06x 0x%06x 0x%06x\n", __func__, + pdata->init_config, pdata->init_data_out, + pdata->init_en_pull, pdata->init_sel_pull); + dev->platform_data = pdata; + pr_info("[%s]gpio_start=[%d]ngpio=[%d]reset-gpio=[%d]\n", + __func__, pdata->gpio_start, pdata->ngpio, + pdata->reset_gpio); + return 0; +} +#endif + +static void pcal6524_power_ctrl(struct pcal6524_platform_data *pdata, char enable) +{ + int ret = 0; + struct regulator *reg_power = pdata->vdd; + + if (enable) { + if (regulator_is_enabled(reg_power)) + pr_err("%s: power regulator(1.8V) is enabled\n", __func__); + else + ret = regulator_enable(reg_power); + if (ret) { + pr_err("%s: power regulator enable failed, rc=%d\n", + __func__, ret); + return; + } + pr_info("%s: gpio expander 1.8V on is finished.\n", __func__); + } else { + if (regulator_is_enabled(reg_power)) + ret = regulator_disable(reg_power); + else + pr_err("%s: power regulator(1.8V) is disabled\n", __func__); + if (ret) { + pr_err("%s: disable power regulator failed, rc=%d\n", + __func__, ret); + return; + } + pr_info("%s: gpio expander 1.8V off is finished.\n", __func__); + } + pr_err("[pcal6524 gpio expander] %s enable(%d)\n", __func__, enable); + return; +} + +static int pcal6524_reset_chip(struct pcal6524_platform_data *pdata) +{ + int retval; + int reset_gpio = pdata->reset_gpio; + + if (gpio_is_valid(reset_gpio)) { + retval = gpio_request(reset_gpio, "pcal6524_reset_gpio"); + if (retval) { + pr_err("[%s]: unable to request gpio [%d]\n", + __func__, reset_gpio); + return retval; + } + + retval = gpio_direction_output(reset_gpio, 1); + if (retval) { + pr_err("[%s]: unable to set direction for gpio [%d]\n", + __func__, reset_gpio); + gpio_free(reset_gpio); + return retval; + } + + usleep_range(100, 100); + gpio_set_value(reset_gpio, 0); + usleep_range(100, 100); + gpio_set_value(reset_gpio, 1); + pr_info("[%s]: gpio expander reset.\n", __func__); + + gpio_free(reset_gpio); + return 0; + } else { + pr_err("[%s]: gpio_is_valid fail\n", __func__); + return -EIO; + } + return 0; +} + +static ssize_t store_pcal6524_gpio_inout(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int retval, off, val, gpio_pcal6524; + char in_out, msg[13]; + struct pcal6524_chip *data = dev_get_drvdata(dev); + + retval = sscanf(buf, "%1c %3d %1d", &in_out, &off, &val); + if (retval == 0) { + dev_err(&data->client->dev, "[%s] fail to pcal6524 out.\n", __func__); + return count; + } + if (!(in_out == 'i' || in_out == 'o')) { + pr_err("[%s] wrong in_out value [%c]\n", __func__, in_out); + return count; + } + if ((off < 0) || (off > PCAL6524_MAX_GPIO)) { + pr_err("[%s] wrong offset value [%d]\n", __func__, off); + return count; + } + if (!(val == 0 || val == 1)) { + pr_err("[%s] wrong val value [%d]\n", __func__, val); + return count; + } + + gpio_pcal6524 = data->gpio_start + off; + snprintf(msg, sizeof(msg)/sizeof(char), "exp-gpio%d\n", off); + if (gpio_is_valid(gpio_pcal6524)) { + retval = gpio_request(gpio_pcal6524, msg); + if (retval) { + pr_err("[%s] unable to request gpio=[%d] err=[%d]\n", + __func__, gpio_pcal6524, retval); + return count; + } + + if (in_out == 'i') { + retval = gpio_direction_input(gpio_pcal6524); + val = gpio_get_value(gpio_pcal6524); + } + else + retval = gpio_direction_output(gpio_pcal6524, val); + + if (retval) + pr_err("%s: unable to set direction for gpio [%d]\n", + __func__, gpio_pcal6524); + + gpio_free(gpio_pcal6524); + } + + pr_info("pcal6524 mode set to dir[%c], offset[%d], val[%d]\n", in_out, off, val); + + return count; +} + +static ssize_t show_pcal6524_gpio_state(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pcal6524_chip *chip = dev_get_drvdata(dev); + struct pcal6524_chip chip_state; + int i, drv_str; + int quot = 0, rest = 0; + uint8_t read_input[3]; + size_t size = 0; + + if (chip == NULL) { + pr_info("%s: driver data is NULL", __func__); + return size; + } + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + pcal6524_read_reg(chip, PCAL6524_INPUT + i, &read_input[i]); + pcal6524_read_reg(chip, PCAL6524_DAT_OUT + i, &chip_state.reg_output[i]); + pcal6524_read_reg(chip, PCAL6524_CONFIG + i, &chip_state.reg_config[i]); + if (i < 2) { + pcal6524_read_reg(chip, PCAL6524_DRIVE0 + i, &chip_state.reg_drive0[i]); + pcal6524_read_reg(chip, PCAL6524_DRIVE1 + i, &chip_state.reg_drive1[i]); + pcal6524_read_reg(chip, PCAL6524_DRIVE2 + i, &chip_state.reg_drive2[i]); + } + pcal6524_read_reg(chip, PCAL6524_EN_PULLUPDOWN + i, &chip_state.reg_enpullupdown[i]); + pcal6524_read_reg(chip, PCAL6524_SEL_PULLUPDOWN + i, &chip_state.reg_selpullupdown[i]); + } + + for (i = 0; i <= PCAL6524_MAX_GPIO; i++) { + size += sprintf(&buf[size], "Expander[3%02d]", i); + quot = i / 8; + rest = i % 8; + + if ((chip_state.reg_config[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " IN"); + else { + if ((chip_state.reg_output[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " OUT_HIGH"); + else + size += sprintf(&buf[size], " OUT_LOW"); + } + + if ((chip_state.reg_enpullupdown[quot] >> rest) & 0x1) { + if ((chip_state.reg_selpullupdown[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " PULL_UP"); + else + size += sprintf(&buf[size], " PULL_DOWN"); + } else + size += sprintf(&buf[size], " PULL_NONE"); + + if (quot == 2) { + if (rest > 3) + drv_str = (chip_state.reg_drive2[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive2[0] >> ((rest)*2)) & 0x3; + } else if (quot == 1) { + if (rest > 3) + drv_str = (chip_state.reg_drive1[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive1[0] >> ((rest)*2)) & 0x3; + } else { + if (rest > 3) + drv_str = (chip_state.reg_drive0[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive0[0] >> ((rest)*2)) & 0x3; + } + + switch(drv_str) { + case GPIO_CFG_6_25MA: + size += sprintf(&buf[size], " DRV_6.25mA"); + break; + case GPIO_CFG_12_5MA: + size += sprintf(&buf[size], " DRV_12.5mA"); + break; + case GPIO_CFG_18_75MA: + size += sprintf(&buf[size], " DRV_18.75mA"); + break; + case GPIO_CFG_25MA: + size += sprintf(&buf[size], " DRV_25mA"); + break; + } + + if ((read_input[quot] >> rest) & 0x1) + size += sprintf(&buf[size], " VAL_HIGH\n"); + else + size += sprintf(&buf[size], " VAL_LOW\n"); + } + + return size; +} + +static DEVICE_ATTR(expgpio, 0644, + show_pcal6524_gpio_state, store_pcal6524_gpio_inout); + +static int expander_show(struct seq_file *s, void *unused) +{ + struct pcal6524_chip chip_state; + int i, drv_str; + int quot = 0, rest = 0; + uint8_t read_input[3]; + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + pcal6524_read_reg(g_dev, PCAL6524_INPUT + i, &read_input[i]); + pcal6524_read_reg(g_dev, PCAL6524_DAT_OUT + i, &chip_state.reg_output[i]); + pcal6524_read_reg(g_dev, PCAL6524_CONFIG + i, &chip_state.reg_config[i]); + if (i < 2) { + pcal6524_read_reg(g_dev, PCAL6524_DRIVE0 + i, &chip_state.reg_drive0[i]); + pcal6524_read_reg(g_dev, PCAL6524_DRIVE1 + i, &chip_state.reg_drive1[i]); + pcal6524_read_reg(g_dev, PCAL6524_DRIVE2 + i, &chip_state.reg_drive2[i]); + } + pcal6524_read_reg(g_dev, PCAL6524_EN_PULLUPDOWN + i, &chip_state.reg_enpullupdown[i]); + pcal6524_read_reg(g_dev, PCAL6524_SEL_PULLUPDOWN + i, &chip_state.reg_selpullupdown[i]); + } + + for (i = 0; i <= PCAL6524_MAX_GPIO; i++) { + seq_printf(s, "Expander[3%02d]", i); + quot = i / 8; + rest = i % 8; + + if ((chip_state.reg_config[quot] >> rest) & 0x1) + seq_printf(s, " IN"); + else { + if ((chip_state.reg_output[quot] >> rest) & 0x1) + seq_printf(s, " OUT_HIGH"); + else + seq_printf(s, " OUT_LOW"); + } + + if ((chip_state.reg_enpullupdown[quot] >> rest) & 0x1) { + if ((chip_state.reg_selpullupdown[quot] >> rest) & 0x1) + seq_printf(s, " PULL_UP"); + else + seq_printf(s, " PULL_DOWN"); + } else + seq_printf(s, " PULL_NONE"); + + if (quot == 2) { + if (rest > 3) + drv_str = (chip_state.reg_drive2[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive2[0] >> ((rest)*2)) & 0x3; + } else if (quot == 1) { + if (rest > 3) + drv_str = (chip_state.reg_drive1[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive1[0] >> ((rest)*2)) & 0x3; + } else { + if (rest > 3) + drv_str = (chip_state.reg_drive0[1] >> ((rest-4)*2)) & 0x3; + else + drv_str = (chip_state.reg_drive0[0] >> ((rest)*2)) & 0x3; + } + + switch(drv_str) { + case GPIO_CFG_6_25MA: + seq_printf(s, " DRV_6.25mA"); + break; + case GPIO_CFG_12_5MA: + seq_printf(s, " DRV_12.5mA"); + break; + case GPIO_CFG_18_75MA: + seq_printf(s, " DRV_18.75mA"); + break; + case GPIO_CFG_25MA: + seq_printf(s, " DRV_25mA"); + break; + } + + if ((read_input[quot] >> rest) & 0x1) + seq_printf(s, " VAL_HIGH\n"); + else + seq_printf(s, " VAL_LOW\n"); + } + + return 0; +} +static int expander_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, expander_show, NULL); +} + +static const struct file_operations expander_operations = { + .open = expander_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pcal6524_gpio_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *np = client->dev.of_node; + struct pcal6524_platform_data *pdata = NULL; + struct pcal6524_chip *dev; + struct gpio_chip *gc; + struct dentry *debugfs_file; + int ret, i; + int retry; + + pr_info("[%s]\n", __func__); + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } +#ifdef CONFIG_OF + if (np) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct pcal6524_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + ret = pcal6524_parse_dt(&client->dev, pdata); + if (ret) { + pr_err("[%s] pcal6524 parse dt failed\n", __func__); + return ret; + } + + } else { + pdata = client->dev.platform_data; + pr_info("GPIO Expender failed to align dtsi %s", + __func__); + } +#else + pdata = client->dev.platform_data; +#endif + if (pdata == NULL) { + dev_err(&client->dev, "missing platform data\n"); + return -ENODEV; + } + pcal6524_power_ctrl(pdata, POWER_ON); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&client->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + dev->client = client; + dev->pdata = pdata; + dev->gpio_start = pdata->gpio_start; + + gc = &dev->gpio_chip; + gc->direction_input = pcal6524_gpio_direction_input; + gc->direction_output = pcal6524_gpio_direction_output; + gc->get = pcal6524_gpio_get_value; + gc->set = pcal6524_gpio_set_value; + gc->request = pcal6524_gpio_request; + gc->free = pcal6524_gpio_free; + gc->can_sleep = 0; + /*Require dev to use the of_gpio api*/ + gc->parent = &client->dev; + + gc->base = pdata->gpio_start; + gc->ngpio = pdata->ngpio; + gc->label = client->name; + gc->owner = THIS_MODULE; + + mutex_init(&dev->lock); + + for (i = 0; i < PCAL6524_PORT_CNT; i++) { + dev->reg_config[i] = (pdata->init_config >> (8*i)) & 0xFF; + dev->reg_output[i] = (pdata->init_data_out >> (8*i)) & 0xFF; + dev->reg_enpullupdown[i] = (pdata->init_en_pull >> (8*i)) & 0xFF; + dev->reg_selpullupdown[i] = (pdata->init_sel_pull >> (8*i)) & 0xFF; + } + + retry = 0; + if (0) { + while(0) { // do not reset + ret = pcal6524_reset_chip(pdata); + if (ret) { + pr_err("[%s]reset control fail\n", __func__); + } else { + ret = pcal6524_gpio_setup(dev); + if (ret) { + dev_err(&client->dev, + "expander setup i2c retry [%d]\n", retry); + } else { + pr_info("[%s]Expander setup success [%d]\n", + __func__, retry); + break; + } + } + if (retry++ > 5) { + dev_err(&client->dev, + "Failed to expander retry[%d]\n", retry); + panic("pcal6524 i2c fail, check HW!\n"); + goto err; + + } + usleep_range(100, 200); + } + }else { + pcal6524_sync_structure_with_register(dev); + } + + ret = gpiochip_add(&dev->gpio_chip); + if (ret) + goto err; + + dev_info(&client->dev, "gpios %d..%d on a %s\n", + gc->base, gc->base + gc->ngpio - 1, + client->name); + + pcal6524_dev = sec_device_create(0, dev, "expander"); + if (IS_ERR(pcal6524_dev)) { + dev_err(&client->dev, + "Failed to create device for expander\n"); + ret = -ENODEV; + goto err; + } + + ret = sysfs_create_file(&pcal6524_dev->kobj, &dev_attr_expgpio.attr); + if (ret) { + dev_err(&client->dev, + "Failed to create sysfs group for expander\n"); + goto err_destroy; + } + + dev->dentry = debugfs_create_dir("expander", NULL); + if (IS_ERR_OR_NULL(dev->dentry)) { + dev_err(&client->dev, + "Failed to create debugfs dir for expander\n"); + goto err_debug_dir; + + } + debugfs_file = debugfs_create_file("gpio", S_IFREG | S_IRUGO, + dev->dentry, NULL, &expander_operations); + if (IS_ERR_OR_NULL(debugfs_file)) { + dev_err(&client->dev, + "Failed to create debugfs file for gpio\n"); + goto err_debug_file; + } + + i2c_set_clientdata(client, dev); + g_dev = dev; + + return 0; + +err_debug_file: + debugfs_remove_recursive(dev->dentry); +err_debug_dir: + sysfs_remove_file(&pcal6524_dev->kobj, &dev_attr_expgpio.attr); +err_destroy: + sec_device_destroy(pcal6524_dev->devt); +err: + pcal6524_power_ctrl(pdata, POWER_OFF); + mutex_destroy(&dev->lock); + kfree(dev); + return ret; +} + +static int pcal6524_gpio_remove(struct i2c_client *client) +{ + struct pcal6524_chip *dev = i2c_get_clientdata(client); + + pcal6524_power_ctrl(dev->pdata, POWER_OFF); + gpiochip_remove(&dev->gpio_chip); + if (!IS_ERR_OR_NULL(dev->dentry)) + debugfs_remove_recursive(dev->dentry); + sysfs_remove_file(&pcal6524_dev->kobj, &dev_attr_expgpio.attr); + mutex_destroy(&dev->lock); + kfree(dev); + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id pcal6524_dt_ids[] = { + { .compatible = "pcal6524,gpio-expander",}, + { /* sentinel */ }, +}; +#endif +static const struct i2c_device_id pcal6524_gpio_id[] = { + {DRV_NAME, 0, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcal6524_gpio_id); + +static struct i2c_driver pcal6524_gpio_driver = { + .driver = { + .name = DRV_NAME, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(pcal6524_dt_ids), +#endif + }, + .probe = pcal6524_gpio_probe, + .remove = pcal6524_gpio_remove, + .id_table = pcal6524_gpio_id, +}; + +static int __init pcal6524_gpio_init(void) +{ + return i2c_add_driver(&pcal6524_gpio_driver); +} + +subsys_initcall(pcal6524_gpio_init); + +static void __exit pcal6524_gpio_exit(void) +{ + i2c_del_driver(&pcal6524_gpio_driver); +} + +module_exit(pcal6524_gpio_exit); + +MODULE_DESCRIPTION("GPIO expander driver for PCAL6524"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpio/sec-pinmux.c b/drivers/gpio/sec-pinmux.c new file mode 100755 index 000000000000..c4662cd1c310 --- /dev/null +++ b/drivers/gpio/sec-pinmux.c @@ -0,0 +1,480 @@ +/* Copyright (c) 2010,2013-2014, 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 +#include +#include +#include +#include +#ifdef CONFIG_SEC_PM_DEBUG +#include +#include +#endif +#ifdef CONFIG_SEC_GPIO_DVS +#include +#include +#include +#endif + +static DEFINE_SPINLOCK(gpiomux_lock); + +/****************************************************************************** + * Define value in accordance with the specification of each BB vendor. + ******************************************************************************/ +#define AP_MAX_GPIO_NUM 149 +#define AP_GPIO_COUNT 121 + +/* GP PIN TYPE REG MASKS */ +#define GPIO_PULL_SHFT 0 +#define GPIO_PULL_MASK 0x3 +#define GPIO_DIR_SHFT 9 +#define GPIO_DIR_MASK 1 +#define GPIO_FUNC_SHFT 2 +#define GPIO_FUNC_MASK 0xF + +/* config translations */ +#define GPIO_NO_PULL 0 +#define GPIO_PULL_DOWN 1 +#define GPIO_PULL_UP 3 + +/* GP pin type register offsets */ +#ifdef CONFIG_ARM64 +#define GPIO_CFG_REG(base, pin) \ + (void __iomem *)(base + 0x0 + 0x1000 * (ulong)(pin)) +#define GPIO_INOUT_REG(base, pin) \ + (void __iomem *)(base + 0x4 + 0x1000 * (ulong)(pin)) +#else +#define GPIO_CFG_REG(base, pin) \ + (void __iomem *)(base + 0x0 + 0x1000 * (pin)) +#define GPIO_INOUT_REG(base, pin) \ + (void __iomem *)(base + 0x4 + 0x1000 * (pin)) +#endif + +#define GET_RESULT_GPIO(a, b, c) \ + ((a<<4 & 0xF0) | (b<<1 & 0xE) | (c & 0x1)) + +#define GET_GPIO_IO(value) \ + (unsigned char)((0xF0 & (value)) >> 4) +#define GET_GPIO_PUPD(value) \ + (unsigned char)((0xE & (value)) >> 1) +#define GET_GPIO_LH(value) \ + (unsigned char)(0x1 & (value)) + +static unsigned int gpio_table[AP_GPIO_COUNT]; + +struct gpio_range { + int start; + int end; +}; + +struct gpio_range skip_gpios_array[] = { +#if defined(CONFIG_ARCH_SDM670) + {31, 37}, + {58, 64}, + {69, 74}, + {85, 88}, + {102, 104}, + {128, 129} +#else + {-1, -1} +#endif +}; + +static void create_gpio_table(void) +{ + int array_cnt = ARRAY_SIZE(skip_gpios_array); + int array_idx, gpio; + unsigned int index = 0; + int skip; + + pr_debug("%s: start\n", __func__); + for(gpio=0; gpio<=AP_MAX_GPIO_NUM; gpio++) { + skip = 0; + for(array_idx=0; array_idx= skip_gpios_array[array_idx].start + && gpio <= skip_gpios_array[array_idx].end) { + skip = 1; + break; + } + } + if (!skip) { + gpio_table[index] = gpio; + index++; + } + } + pr_debug("%s: done\n", __func__); +} + +#ifdef CONFIG_SEC_GPIO_DVS +/****************************************************************/ +/* Pre-defined variables. (DO NOT CHANGE THIS!!) */ +static unsigned char checkgpiomap_result[GDVS_PHONE_STATUS_MAX][AP_GPIO_COUNT]; +static struct gpiomap_result gpiomap_result = { + .init = checkgpiomap_result[PHONE_INIT], + .sleep = checkgpiomap_result[PHONE_SLEEP] +}; +/****************************************************************/ +#ifdef SECGPIO_SLEEP_DEBUGGING +static struct sleepdebug_gpiotable sleepdebug_table; +#endif + +static void msm_check_gpio_status(unsigned char phonestate) +{ + struct gpiomux_setting val; + struct gpio_chip *gp = gpio_to_chip(0); + u32 gpio, vgpio; + u8 temp_io = 0, temp_pdpu = 0, temp_lh = 0; + pr_info("[dvs_%s] state : %s\n", __func__, + (phonestate == PHONE_INIT) ? "init" : "sleep"); + + for (vgpio = 0; vgpio < AP_GPIO_COUNT; vgpio++) { + gpio = gpio_table[vgpio]; + msm_gp_get_cfg(gp, gpio, &val); + temp_lh = msm_gp_get_value(gp, gpio, val.dir); + + if (val.func == GPIOMUX_FUNC_GPIO) { + if (val.dir == GPIOMUX_IN) + temp_io = 0x01; /* GPIO_IN */ + else if (val.dir == GPIOMUX_OUT_HIGH || + val.dir == GPIOMUX_OUT_LOW) + temp_io = 0x02; /* GPIO_OUT */ + else + temp_io = 0xF; /* not alloc. */ + } else + temp_io = 0x0; /* FUNC */ + + switch(val.pull) { + case GPIOMUX_PULL_NONE: + temp_pdpu = 0x00; + break; + case GPIOMUX_PULL_DOWN: + temp_pdpu = 0x01; + break; + case GPIOMUX_PULL_UP: + temp_pdpu = 0x02; + break; + case GPIOMUX_PULL_KEEPER: + temp_pdpu = 0x03; + break; + default: + temp_pdpu = 0x07; + break; + } + + checkgpiomap_result[phonestate][vgpio] = + GET_RESULT_GPIO(temp_io, temp_pdpu, temp_lh); + } + + pr_info("[dvs_%s]-\n", __func__); + + return; +} + +#ifdef SECGPIO_SLEEP_DEBUGGING +/****************************************************************/ +/* Define this function in accordance with the specification of each BB vendor */ +void setgpio_for_sleepdebug(int gpionum, uint16_t io_pupd_lh) +{ + unsigned char temp_io, temp_pupd, temp_lh; + unsigned int temp_data; + struct gpio_chip *gp = gpio_to_chip(0); + + pr_info("[dvs_%s] gpionum=%d, io_pupd_lh=0x%x\n", + __func__, gpionum, io_pupd_lh); + + temp_io = GET_GPIO_IO(io_pupd_lh); + temp_pupd = GET_GPIO_PUPD(io_pupd_lh); + temp_lh = GET_GPIO_LH(io_pupd_lh); + + pr_info("[dvs_%s] io=%d, pupd=%d, lh=%d\n", + __func__, temp_io, temp_pupd, temp_lh); + + /* in case of 'INPUT', set PD/PU */ + if (temp_io == GDVS_IO_IN) { + /* 0x0:NP, 0x1:PD, 0x2:PU */ + if (temp_pupd == GDVS_PUPD_NP) + temp_data = GPIO_DVS_CFG_PULL_NONE; + else if (temp_pupd == GDVS_PUPD_PD) + temp_data = GPIO_DVS_CFG_PULL_DOWN; + else if (temp_pupd == GDVS_PUPD_PU) + temp_data = GPIO_DVS_CFG_PULL_UP; + else /* It should be not runned */ + temp_data = GPIO_DVS_CFG_PULL_NONE; + + msm_set_gpio_status(gp, gpionum, temp_data, 0); + } + /* in case of 'OUTPUT', set L/H */ + + else if (temp_io == GDVS_IO_OUT) { + pr_info("[dvs_%s] %d gpio set %d\n", + __func__, gpionum, temp_lh); + temp_data = GPIO_DVS_CFG_OUTPUT; + msm_set_gpio_status(gp, gpionum, temp_data, temp_lh); + } + else + { + pr_info("[dvs_%s] %d gpio set %d NOT VALID\n", + __func__, gpionum, temp_lh); + } +} +/****************************************************************/ + +/****************************************************************/ +/* Define this function in accordance with the specification of each BB vendor */ +static void undo_sleepgpio(void) +{ + int i; + + pr_info("[dvs_%s] ++\n", __func__); + + for (i = 0; i < sleepdebug_table.gpio_count; i++) { + int gpio_num; + gpio_num = sleepdebug_table.gpioinfo[i].gpio_num; + /* + * << Caution >> + * If it's necessary, + * change the following function to another appropriate one + * or delete it + */ + setgpio_for_sleepdebug(gpio_num, gpiomap_result.sleep[gpio_num]); + } + + pr_info("[dvs_%s] --\n", __func__); + return; +} +/****************************************************************/ +#endif + +/********************* Fixed Code Area !***************************/ +#ifdef SECGPIO_SLEEP_DEBUGGING +static void set_sleepgpio(void) +{ + int i; + uint16_t set_data; + + pr_info("[dvs_%s] ++, cnt=%d\n", + __func__, sleepdebug_table.gpio_count); + + for (i = 0; i < sleepdebug_table.gpio_count; i++) { + int gpio_num; + pr_info("[dvs_%s][%d] gpio_num(%d), io(%d), pupd(%d), lh(%d)\n", + __func__, + i, sleepdebug_table.gpioinfo[i].gpio_num, + sleepdebug_table.gpioinfo[i].io, + sleepdebug_table.gpioinfo[i].pupd, + sleepdebug_table.gpioinfo[i].lh); + + gpio_num = sleepdebug_table.gpioinfo[i].gpio_num; + + // to prevent a human error caused by "don't care" value + if( sleepdebug_table.gpioinfo[i].io == GDVS_IO_IN) + sleepdebug_table.gpioinfo[i].lh = + GET_GPIO_LH(gpiomap_result.sleep[gpio_num]); + else if( sleepdebug_table.gpioinfo[i].io == GDVS_IO_OUT) + sleepdebug_table.gpioinfo[i].pupd = + GET_GPIO_PUPD(gpiomap_result.sleep[gpio_num]); + + set_data = GET_RESULT_GPIO( + sleepdebug_table.gpioinfo[i].io, + sleepdebug_table.gpioinfo[i].pupd, + sleepdebug_table.gpioinfo[i].lh); + + setgpio_for_sleepdebug(gpio_num, set_data); + } + pr_info("[dvs_%s] --\n", __func__); + return; +} +#endif + +/****************************************************************/ +/* Define appropriate variable in accordance with + the specification of each BB vendor */ +static struct gpio_dvs msm_gpio_dvs = { + .result = &gpiomap_result, + .check_gpio_status = msm_check_gpio_status, + .count = AP_GPIO_COUNT, + .check_init = false, + .check_sleep = false, +#ifdef SECGPIO_SLEEP_DEBUGGING + .sdebugtable = &sleepdebug_table, + .set_sleepgpio = set_sleepgpio, + .undo_sleepgpio = undo_sleepgpio, +#endif + .gpio_tbl = gpio_table +}; +/****************************************************************/ +#endif + +#ifdef CONFIG_SEC_PM_DEBUG +static const char * const gpiomux_drv_str[] = { + "DRV_2mA", + "DRV_4mA", + "DRV_6mA", + "DRV_8mA", + "DRV_10mA", + "DRV_12mA", + "DRV_14mA", + "DRV_16mA", +}; + +static const char * const gpiomux_func_str[] = { + "GPIO", + "Func_1", + "Func_2", + "Func_3", + "Func_4", + "Func_5", + "Func_6", + "Func_7", + "Func_8", + "Func_9", + "Func_a", + "Func_b", + "Func_c", + "Func_d", + "Func_e", + "Func_f", +}; + +static const char * const gpiomux_pull_str[] = { + "PULL_NONE", + "PULL_DOWN", + "PULL_KEEPER", + "PULL_UP", +}; + +static const char * const gpiomux_dir_str[] = { + "IN", + "OUT_HIGH", + "OUT_LOW", +}; + +static const char * const gpiomux_val_str[] = { + "VAL_LOW", + "VAL_HIGH", +}; + +static void gpiomux_debug_print(struct seq_file *m) +{ + unsigned long flags; + struct gpiomux_setting set; + struct gpio_chip *gp = gpio_to_chip(0); + unsigned val = 0; + unsigned gpio, vgpio; + + spin_lock_irqsave(&gpiomux_lock, flags); + + for (vgpio = 0; vgpio < AP_GPIO_COUNT; ++vgpio) { + gpio = gpio_table[vgpio]; +#ifdef ENABLE_SENSORS_FPRINT_SECURE + if (gpio >= CONFIG_SENSORS_FP_SPI_GPIO_START + && gpio <= CONFIG_SENSORS_FP_SPI_GPIO_END) + continue; +#endif +#ifdef CONFIG_ESE_SECURE + if (gpio >= CONFIG_ESE_SPI_GPIO_START + && gpio <= CONFIG_ESE_SPI_GPIO_END) + continue; +#endif + msm_gp_get_cfg(gp, gpio, &set); + val = msm_gp_get_value(gp, gpio, set.dir); + + if (IS_ERR_OR_NULL(m)) { + pr_info("GPIO[%u] \t%s \t%s \t%s \t%s \t%s\n", + gpio, + gpiomux_func_str[set.func], + gpiomux_dir_str[set.dir], + gpiomux_pull_str[set.pull], + gpiomux_drv_str[set.drv], + gpiomux_val_str[val]); + } else { + seq_printf(m, "GPIO[%u] \t%s \t%s \t%s \t%s \t%s\n", + gpio, + gpiomux_func_str[set.func], + gpiomux_dir_str[set.dir], + gpiomux_pull_str[set.pull], + gpiomux_drv_str[set.drv], + gpiomux_val_str[val]); + } + } + + spin_unlock_irqrestore(&gpiomux_lock, flags); +} + +void msm_gpio_print_enabled(void) +{ + gpiomux_debug_print(NULL); +} + +static int gpiomux_debug_showall(struct seq_file *m, void *unused) +{ + gpiomux_debug_print(m); + return 0; +} + +static int gpiomux_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, gpiomux_debug_showall, inode->i_private); +} + +static const struct file_operations gpiomux_operations = { + .open = gpiomux_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init msm_gpiomux_debug_init(void) +{ + (void) debugfs_create_file("gpiomux", S_IFREG | S_IRUGO, + NULL, NULL, &gpiomux_operations); + return 0; +} +late_initcall(msm_gpiomux_debug_init); +#endif + +static int __init msm_gpiomux_init(void) +{ + return 0; +} +late_initcall(msm_gpiomux_init); + +#ifdef CONFIG_SEC_GPIO_DVS +static struct platform_device secgpio_dvs_device = { + .name = "secgpio_dvs", + .id = -1, + /**************************************************************** + * Designate appropriate variable pointer + * in accordance with the specification of each BB vendor. + ***************************************************************/ + .dev.platform_data = &msm_gpio_dvs, +}; + +static struct platform_device *secgpio_dvs_devices[] __initdata = { + &secgpio_dvs_device, +}; + +static int __init secgpio_dvs_device_init(void) +{ + create_gpio_table(); + + return platform_add_devices( + secgpio_dvs_devices, ARRAY_SIZE(secgpio_dvs_devices)); +} +#else +static int __init secgpio_dvs_device_init(void) +{ + create_gpio_table(); + + return 0; +} +#endif +arch_initcall(secgpio_dvs_device_init); diff --git a/drivers/gpio/secgpio_dvs.c b/drivers/gpio/secgpio_dvs.c new file mode 100755 index 000000000000..273cf6e175de --- /dev/null +++ b/drivers/gpio/secgpio_dvs.c @@ -0,0 +1,522 @@ +/* + * Samsung Mobile VE Group. + * + * drivers/gpio/secgpio_dvs.c + * + * Drivers for samsung gpio debugging & verification. + * + * Copyright (C) 2013, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/*sys fs*/ +struct class *secgpio_dvs_class; +EXPORT_SYMBOL(secgpio_dvs_class); + +struct device *secgpio_dotest; +EXPORT_SYMBOL(secgpio_dotest); + +/* extern GPIOMAP_RESULT GpioMap_result; */ +static struct gpio_dvs *gdvs_info; + +static ssize_t checked_secgpio_file_read( + struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t checked_sleep_secgpio_file_read( + struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t checked_secgpio_init_read_details( + struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t checked_secgpio_sleep_read_details( + struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t secgpio_ctrl_file_write( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size); +static ssize_t secgpio_checked_sleepgpio_read( + struct device *dev, struct device_attribute *attr, char *buf); +#ifdef SECGPIO_SLEEP_DEBUGGING +static ssize_t secgpio_sleep_debug_write( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size); +#endif + +static DEVICE_ATTR(gpioinit_check, 0664, + checked_secgpio_file_read, NULL); +static DEVICE_ATTR(gpiosleep_check, 0664, + checked_sleep_secgpio_file_read, NULL); +static DEVICE_ATTR(check_init_detail, 0664, + checked_secgpio_init_read_details, NULL); +static DEVICE_ATTR(check_sleep_detail, 0664, + checked_secgpio_sleep_read_details, NULL); +static DEVICE_ATTR(secgpio_ctrl, 0664 , NULL, secgpio_ctrl_file_write); +static DEVICE_ATTR(checked_sleepGPIO, 0664, + secgpio_checked_sleepgpio_read, NULL); +#ifdef SECGPIO_SLEEP_DEBUGGING +static DEVICE_ATTR(gpio_sleep_debug, 0220 , NULL, secgpio_sleep_debug_write); +#endif + +static struct attribute *secgpio_dvs_attributes[] = { + &dev_attr_gpioinit_check.attr, + &dev_attr_gpiosleep_check.attr, + &dev_attr_check_init_detail.attr, + &dev_attr_check_sleep_detail.attr, + &dev_attr_secgpio_ctrl.attr, + &dev_attr_checked_sleepGPIO.attr, +#ifdef SECGPIO_SLEEP_DEBUGGING + &dev_attr_gpio_sleep_debug.attr, +#endif + NULL, +}; + +static struct attribute_group secgpio_dvs_attr_group = { + .attrs = secgpio_dvs_attributes, +}; + +static int atoi(const char *str) +{ + int result = 0; + int count = 0; + if (str == NULL) + return -EIO; + while (str[count] != 0 /* NULL */ + && str[count] >= '0' && str[count] <= '9') { + result = result * 10 + str[count] - '0'; + ++count; + } + return result; +} + +static char * +strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + /* if (s == NULL && (s = *last) == NULL) + return NULL; */ + if (s == NULL) { + s = *last; + if (s == NULL) + return NULL; + } + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return NULL; + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + sc = *spanp++; + if (sc == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return tok; + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +static char * +strtok(char *s, const char *delim) +{ + static char *last; + + return strtok_r(s, delim, &last); +} + +static ssize_t checked_secgpio_file_read( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + char temp_buf[20]; + struct gpio_dvs *gdvs = dev_get_drvdata(dev); + + for (i = 0; i < gdvs->count; i++) { + memset(temp_buf, 0, sizeof(char)*20); + snprintf(temp_buf, 20, "%x ", gdvs->result->init[i]); + strlcat(buf, temp_buf, PAGE_SIZE); + } + + return strlen(buf); +} + +static ssize_t checked_sleep_secgpio_file_read( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + char temp_buf[20]; + struct gpio_dvs *gdvs = dev_get_drvdata(dev); + + for (i = 0; i < gdvs->count; i++) { + memset(temp_buf, 0, sizeof(char)*20); + snprintf(temp_buf, 20, "%x ", gdvs->result->sleep[i]); + strlcat(buf, temp_buf, PAGE_SIZE); + } + + return strlen(buf); +} + +static ssize_t checked_secgpio_init_read_details( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + char temp_buf[20]; + struct gpio_dvs *gdvs = dev_get_drvdata(dev); + + for (i = 0; i < gdvs->count; i++) { + memset(temp_buf, 0, sizeof(char)*20); + snprintf(temp_buf, 20, "GI[%d] - %x\n ", + gdvs->gpio_tbl[i], gdvs->result->init[i]); + strlcat(buf, temp_buf, PAGE_SIZE); + } + + return strlen(buf); +} +static ssize_t checked_secgpio_sleep_read_details( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + char temp_buf[20]; + struct gpio_dvs *gdvs = dev_get_drvdata(dev); + + for (i = 0; i < gdvs->count; i++) { + memset(temp_buf, 0, sizeof(char)*20); + snprintf(temp_buf, 20, "GS[%d] - %x\n ", + gdvs->gpio_tbl[i], gdvs->result->sleep[i]); + strlcat(buf, temp_buf, PAGE_SIZE); + } + + return strlen(buf); + +} + +#ifdef SECGPIO_SLEEP_DEBUGGING +static int sleepdata_cur_pos; +static char sdata_phase = SLDATA_START; +static ssize_t secgpio_sleep_debug_write( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + int gpio_ctrl[3] = {0,}; + char temp_buf[50]; + struct gpio_dvs *gdvs = dev_get_drvdata(dev); + + pr_info("[secgpio_dvs][%s] buf = %s\n", __func__, buf); + + memset(temp_buf, 0, 50); + strlcpy(temp_buf, buf, 50); + + /* Data Format example + * Start Data : Cxxx (xxx : gpio count, ex : C12) + * Data : Dxxx,y,z (xxx : gpio_num, y : In/Out, z : State H/L, ex : D112,2,0) + * refer to the standard document for value + * End Data : D + */ + if (temp_buf[0] == 'C') { /* sleep data start */ + int count = 0; + count = atoi(&temp_buf[1]); + + if (count <= gdvs->count) { + /* data Initialize */ + gdvs->sdebugtable->active = false; + sleepdata_cur_pos = 0; + gdvs->sdebugtable->gpio_count = 0; + if (gdvs->sdebugtable->gpioinfo != NULL) { + kfree(gdvs->sdebugtable->gpioinfo); + gdvs->sdebugtable->gpioinfo = NULL; + } + + gdvs->sdebugtable->gpio_count = count; + pr_info("[secgpio_dvs] count = %d\n", count); + + /* gpioinfo must be released later!! */ + gdvs->sdebugtable->gpioinfo = + kzalloc(sizeof(struct sleepdebug_gpioinfo) * count, GFP_KERNEL); + if (gdvs->sdebugtable->gpioinfo == NULL) + pr_err("[secgpio_dvs][%s] fail to memalloc!!\n", __func__); + else + sdata_phase = SLDATA_DATA; + } else { + pr_err("[secgpio_dvs] gpio count error!!(%d)\n", count); + sdata_phase = SLDATA_START; + } + } + /* sleep data */ + else if (sdata_phase == SLDATA_DATA && temp_buf[0] == 'D') { + char *token = NULL; + char comp[] = ","; + int num = 0; + token = strtok(&temp_buf[1], comp); + + for (num = 0; num < 3; num++) { + if (token != NULL) { + pr_info("[secgpio_dvs] GPIO Control TOKEN = %s\n", + token); + gpio_ctrl[num] = atoi(token); + token = strtok(NULL, comp); + } + pr_info("[secgpio_dvs] GPIO Control[%d] = %d\n", + num, gpio_ctrl[num]); + } + + /* + gpio_ctrl[0] = GPIO NUMBER, gpio_ctrl[1] = IN/OUT, + gpio_ctrl[2] = L/H (OUT), PD/PU (IN) + */ + gdvs->sdebugtable->gpioinfo[sleepdata_cur_pos].gpio_num = gpio_ctrl[0]; + gdvs->sdebugtable->gpioinfo[sleepdata_cur_pos].io = gpio_ctrl[1]; + if (gpio_ctrl[1] == 1) /* IN */ + gdvs->sdebugtable->gpioinfo[sleepdata_cur_pos].pupd = gpio_ctrl[2]; + else if (gpio_ctrl[1] == 2) /* OUT */ + gdvs->sdebugtable->gpioinfo[sleepdata_cur_pos].lh = gpio_ctrl[2]; + else /* It should not be runned */ + gdvs->sdebugtable->gpioinfo[sleepdata_cur_pos].pupd = gpio_ctrl[2]; + + sleepdata_cur_pos++; + if (sleepdata_cur_pos == gdvs->sdebugtable->gpio_count) + sdata_phase = SLDATA_END; + } + /* sleep data end */ + else if (sdata_phase == SLDATA_END && temp_buf[0] == 'E') { + pr_info("[secgpio_dvs] GPIO debug On!!\n"); + gdvs->sdebugtable->active = true; + sdata_phase = SLDATA_START; + } + else { + pr_err("[secgpio_dvs][%s] Phase Error!!(%d)\n", __func__, sdata_phase); + } + + return size; +} +#endif + +static ssize_t secgpio_ctrl_file_write( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + char *token = NULL; + char comp[] = ","; + int gpio_ctrl[3] = {0,}, num = 0; + char temp_buf[50]; + + pr_info("[secgpio_dvs] GPIO onoff buf = %s\n", buf); + + memset(temp_buf, 0, 50); + strlcpy(temp_buf, buf, 50); + token = strtok(temp_buf, comp); + + for (num = 0; num < 3; num++) { + if (token != NULL) { + pr_info("[secgpio_dvs] GPIO Control TOKEN = %s\n", + token); + gpio_ctrl[num] = atoi(token); + token = strtok(NULL, comp); + } + pr_info("[secgpio_dvs] GPIO Control[%d] = %d\n", + num, gpio_ctrl[num]); + } + /* + gpio_ctrl[0] = IN/OUT, gpio_ctrl[1] = GPIO NUMBER, + gpio_ctrl[2] = L/H + */ + if (gpio_ctrl[0] == 1) { + gpio_request(gpio_ctrl[1], "gpio_output_test_on"); + gpio_direction_input(gpio_ctrl[1]); + gpio_free(gpio_ctrl[1]); + } else { + gpio_request(gpio_ctrl[1], "gpio_output_test_on"); + gpio_direction_output(gpio_ctrl[1], 1); + gpio_free(gpio_ctrl[1]); + + if (gpio_ctrl[2] == 1) { + gpio_request(gpio_ctrl[1], "gpio_output_test_on"); + gpio_set_value(gpio_ctrl[1], 1); + gpio_free(gpio_ctrl[1]); + } else if (gpio_ctrl[2] == 0) { + gpio_request(gpio_ctrl[1], "gpio_output_test_off"); + gpio_set_value(gpio_ctrl[1], 0); + gpio_free(gpio_ctrl[1]); + } + } + return size; + +} + +static ssize_t secgpio_checked_sleepgpio_read( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct gpio_dvs *gdvs = dev_get_drvdata(dev); + + if (gdvs->check_sleep) + return snprintf(buf, PAGE_SIZE, "1"); + else + return snprintf(buf, PAGE_SIZE, "0"); +} + +void gpio_dvs_check_initgpio(void) +{ + if (gdvs_info && gdvs_info->check_gpio_status) + gdvs_info->check_gpio_status(PHONE_INIT); +} + +void gpio_dvs_check_sleepgpio(void) +{ + if (unlikely(!gdvs_info->check_sleep)) { + gdvs_info->check_gpio_status(PHONE_SLEEP); + gdvs_info->check_sleep = true; + } +} + +#ifdef SECGPIO_SLEEP_DEBUGGING +void gpio_dvs_set_sleepgpio(void) +{ + + if (unlikely(gdvs_info->sdebugtable->active)) { + /* setting function */ + gdvs_info->set_sleepgpio(); + } + + return; +} + +void gpio_dvs_undo_sleepgpio(void) +{ + + if (unlikely(gdvs_info->sdebugtable->active)) { + /* undoing function */ + gdvs_info->undo_sleepgpio(); + /* data initialize */ + gdvs_info->sdebugtable->active = false; + sleepdata_cur_pos = 0; + gdvs_info->sdebugtable->gpio_count = 0; + if (gdvs_info->sdebugtable->gpioinfo != NULL) { + kfree(gdvs_info->sdebugtable->gpioinfo); + gdvs_info->sdebugtable->gpioinfo = NULL; + } + } +} +#endif + +static int secgpio_dvs_probe(struct platform_device *pdev) +{ + int ret = 0; + struct class *secgpio_dvs_class; + struct device *secgpio_dotest; + struct gpio_dvs *gdvs = dev_get_platdata(&pdev->dev); + + pr_info("[secgpio_dvs] %s has been created!!!\n", __func__); + + secgpio_dvs_class = class_create(THIS_MODULE, "secgpio_check"); + if (IS_ERR(secgpio_dvs_class)) { + ret = PTR_ERR(secgpio_dvs_class); + pr_err("Failed to create class(secgpio_check_all)"); + goto fail_out; + } + + secgpio_dotest = device_create(secgpio_dvs_class, + NULL, 0, NULL, "secgpio_check_all"); + if (IS_ERR(secgpio_dotest)) { + ret = PTR_ERR(secgpio_dotest); + pr_err("Failed to create device(secgpio_check_all)"); + goto fail1; + } + dev_set_drvdata(secgpio_dotest, gdvs); + gdvs_info = gdvs; + + ret = sysfs_create_group(&secgpio_dotest->kobj, + &secgpio_dvs_attr_group); + if (ret) { + pr_err("Failed to create sysfs group"); + goto fail2; + } + + return ret; + +fail2: + device_destroy(secgpio_dvs_class,0); +fail1: + class_destroy(secgpio_dvs_class); +fail_out: + if (ret) + pr_err(" (err = %d)!\n", ret); + return ret; + +} + +static int secgpio_dvs_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver secgpio_dvs = { + .probe = secgpio_dvs_probe, + .remove = secgpio_dvs_remove, + .driver = { + .name = "secgpio_dvs", + .owner = THIS_MODULE, + }, +}; + +static int __init secgpio_dvs_init(void) +{ + int ret; + ret = platform_driver_register(&secgpio_dvs); + pr_info("[secgpio_dvs] secgpio_dvs_init has been initialized!!!\n"); + return ret; +} + +static void __exit secgpio_dvs_exit(void) +{ + platform_driver_unregister(&secgpio_dvs); +} + +module_init(secgpio_dvs_init); +module_exit(secgpio_dvs_exit); + +MODULE_DESCRIPTION("Samsung GPIO debugging and verification"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index ec1ed94b2390..ec1031aac9c7 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -25,6 +25,8 @@ * */ +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ + #include #include #include @@ -38,12 +40,56 @@ #include "drm_crtc_helper_internal.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include +#define DP_ENUM_STR(x) #x + +#define IOCTL_MAGIC 't' +#define IOCTL_MAXNR 30 +#define IOCTL_DP_AUXCMD_TYPE _IO(IOCTL_MAGIC, 0) + +static inline char *auxdev_ioctl_cmd_to_string(u32 cmd) +{ + switch (cmd) { + case IOCTL_DP_AUXCMD_TYPE: + return DP_ENUM_STR(IOCTL_DP_AUXCMD_TYPE); + default: + return "unknown"; + } +} + +enum dp_auxcmd_type { + DP_AUXCMD_NATIVE, + DP_AUXCMD_I2C, +}; + +static inline char *auxcmd_type_to_string(u32 cmd_type) +{ + switch (cmd_type) { + case DP_AUXCMD_NATIVE: + return DP_ENUM_STR(DP_AUXCMD_NATIVE); + case DP_AUXCMD_I2C: + return DP_ENUM_STR(DP_AUXCMD_I2C); + default: + return "unknown"; + } +} + +struct ioctl_auxdev_info { + int size; /* unused */ + enum dp_auxcmd_type cmd_type; +}; +#endif + struct drm_dp_aux_dev { unsigned index; struct drm_dp_aux *aux; struct device *dev; struct kref refcount; atomic_t usecount; +#ifdef CONFIG_SEC_DISPLAYPORT + enum dp_auxcmd_type cmd_type; +#endif }; #define DRM_AUX_MINORS 256 @@ -87,6 +133,9 @@ static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) return ERR_PTR(index); } aux_dev->index = index; +#ifdef CONFIG_SEC_DISPLAYPORT + aux_dev->cmd_type = DP_AUXCMD_NATIVE; +#endif return aux_dev; } @@ -158,7 +207,11 @@ static ssize_t auxdev_read(struct file *file, char __user *buf, size_t count, } while (bytes_pending > 0) { +#ifdef CONFIG_SEC_DISPLAYPORT + uint8_t localbuf[512]; +#else uint8_t localbuf[DP_AUX_MAX_PAYLOAD_BYTES]; +#endif ssize_t todo = min_t(size_t, bytes_pending, sizeof(localbuf)); if (signal_pending(current)) { @@ -167,7 +220,14 @@ static ssize_t auxdev_read(struct file *file, char __user *buf, size_t count, goto out; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (aux_dev->cmd_type == DP_AUXCMD_NATIVE) + res = drm_dp_dpcd_read(aux_dev->aux, *offset, localbuf, todo); + else + res = drm_dp_i2c_read(aux_dev->aux, localbuf, todo); +#else res = drm_dp_dpcd_read(aux_dev->aux, *offset, localbuf, todo); +#endif if (res <= 0) { res = num_bytes_processed ? num_bytes_processed : res; goto out; @@ -207,7 +267,11 @@ static ssize_t auxdev_write(struct file *file, const char __user *buf, } while (bytes_pending > 0) { +#ifdef CONFIG_SEC_DISPLAYPORT + uint8_t localbuf[512]; +#else uint8_t localbuf[DP_AUX_MAX_PAYLOAD_BYTES]; +#endif ssize_t todo = min_t(size_t, bytes_pending, sizeof(localbuf)); if (signal_pending(current)) { @@ -223,7 +287,14 @@ static ssize_t auxdev_write(struct file *file, const char __user *buf, goto out; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (aux_dev->cmd_type == DP_AUXCMD_NATIVE) + res = drm_dp_dpcd_write(aux_dev->aux, *offset, localbuf, todo); + else + res = drm_dp_i2c_write(aux_dev->aux, localbuf, todo); +#else res = drm_dp_dpcd_write(aux_dev->aux, *offset, localbuf, todo); +#endif if (res <= 0) { res = num_bytes_processed ? num_bytes_processed : res; goto out; @@ -248,6 +319,40 @@ static int auxdev_release(struct inode *inode, struct file *file) return 0; } +#ifdef CONFIG_SEC_DISPLAYPORT +static long auxdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + s32 res = 0, rc = 0; + u32 size = 0; + struct ioctl_auxdev_info info; + struct drm_dp_aux_dev *aux_dev = file->private_data; + + if (_IOC_TYPE(cmd) != IOCTL_MAGIC) + return -EINVAL; + if (_IOC_NR(cmd) >= IOCTL_MAXNR) + return -EINVAL; + + size = sizeof(struct ioctl_auxdev_info); + pr_info("cmd: %s\n", auxdev_ioctl_cmd_to_string(cmd)); + + switch (cmd) { + case IOCTL_DP_AUXCMD_TYPE: + rc = copy_from_user((void *)&info, (void *)arg, size); + if (rc) { + pr_debug("error at copy_from_user, rc(%d)\n", rc); + break; + } + aux_dev->cmd_type = info.cmd_type; + pr_info("auxcmd_type: %s\n", auxcmd_type_to_string(aux_dev->cmd_type)); + break; + default: + break; + } + + return res; +} +#endif + static const struct file_operations auxdev_fops = { .owner = THIS_MODULE, .llseek = auxdev_llseek, @@ -255,6 +360,12 @@ static const struct file_operations auxdev_fops = { .write = auxdev_write, .open = auxdev_open, .release = auxdev_release, +#ifdef CONFIG_SEC_DISPLAYPORT + .unlocked_ioctl = auxdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = auxdev_ioctl, +#endif +#endif }; #define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 8164c6f6b9de..a54b94961cf8 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -168,6 +168,11 @@ EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); #define AUX_RETRY_INTERVAL 500 /* us */ +#ifdef CONFIG_SEC_DISPLAYPORT +extern int secdp_get_hpd_status(void); +extern bool secdp_get_cable_status(void); +#endif + /** * DOC: dp helpers * @@ -195,6 +200,14 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, mutex_lock(&aux->hw_mutex); +#ifdef CONFIG_SEC_DISPLAYPORT + if (/*!secdp_get_hpd_status() ||*/ !secdp_get_cable_status()) { + pr_info("[drm-dp] %s: cable is out-1\n", __func__); + ret = -EIO; + goto unlock; + } +#endif + /* * The specification doesn't give any recommendation on how often to * retry native transactions. We used to retry 7 times like for @@ -219,6 +232,13 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, } else ret = -EIO; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (/*!secdp_get_hpd_status() ||*/ !secdp_get_cable_status()) { + pr_info("[drm-dp] %s: cable is out-2\n", __func__); + ret = -EIO; + goto unlock; + } +#endif /* * We want the error we return to be the error we received on @@ -300,6 +320,48 @@ ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, } EXPORT_SYMBOL(drm_dp_dpcd_write); +#ifdef CONFIG_SEC_DISPLAYPORT +static int drm_dp_i2c_access(struct drm_dp_aux *aux, int mode, + void *buffer, size_t size) +{ + + struct i2c_adapter *adapter = &aux->ddc; + int ret, retries = 3; + + do { + struct i2c_msg msgs = { + .addr = 0x50, + .flags = mode == DP_AUX_I2C_READ ? I2C_M_RD : 0, + .len = size, + .buf = buffer, + }; + + ret = i2c_transfer(adapter, &msgs, 1); + + if (ret == -ENXIO) { + DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n", + adapter->name); + break; + } + } while (ret != 1 && --retries); + + return (ret == 1) ? size : ret; +} + +ssize_t drm_dp_i2c_read(struct drm_dp_aux *aux, void *buffer, size_t size) +{ + return drm_dp_i2c_access(aux, DP_AUX_I2C_READ, buffer, + size); +} +EXPORT_SYMBOL(drm_dp_i2c_read); + +ssize_t drm_dp_i2c_write(struct drm_dp_aux *aux, void *buffer, size_t size) +{ + return drm_dp_i2c_access(aux, DP_AUX_I2C_WRITE, buffer, + size); +} +EXPORT_SYMBOL(drm_dp_i2c_write); +#endif /** * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) * @aux: DisplayPort AUX channel diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index cdc319d1237c..454502208758 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -36,6 +36,9 @@ #include #include #include +#ifdef CONFIG_SEC_DISPLAYPORT +#include +#endif #define version_greater(edid, maj, min) \ (((edid)->version > (maj)) || \ @@ -1503,6 +1506,14 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) } } while (ret != xfers && --retries); +#ifdef CONFIG_SEC_DISPLAYPORT + if (len == EDID_LENGTH) { + print_hex_dump(KERN_DEBUG, "secdp_EDID: ", DUMP_PREFIX_NONE, 16, 1, + buf, len, false); + secdp_logger_hex_dump(buf, "EDID:", len); + } +#endif + return ret == xfers ? 0 : -1; } @@ -3725,6 +3736,13 @@ struct edid *edid) const u8 *cea = drm_find_cea_extension(edid); const u8 *db = NULL; +#ifdef CONFIG_SEC_DISPLAYPORT + if (connector->hdr_supported) { + pr_debug("prev: hdr_supported has set -> clear!\n"); + connector->hdr_supported = false; + } +#endif + if (cea && cea_revision(cea) >= 3) { int i, start, end; @@ -3754,6 +3772,16 @@ struct edid *edid) } } } + +#ifdef CONFIG_SEC_DISPLAYPORT + if (connector) { + pr_debug("[drm-dp] %s: HDR electro-optical <%d>, hdr_supported <%d>\n", + __func__, connector->hdr_eotf, connector->hdr_supported); + + if (connector->hdr_supported) + connector->hdr_supported = false; + } +#endif } static u8 * diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index d9a5762ad633..2f9de44c547c 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -561,8 +561,13 @@ EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size); * Return: The number of bytes transmitted on success or a negative error code * on failure. */ +#if defined(CONFIG_DISPLAY_SAMSUNG) +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, void *payload, + size_t size) +#else ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload, size_t size) +#endif { struct mipi_dsi_msg msg = { .channel = dsi->channel, @@ -606,8 +611,13 @@ EXPORT_SYMBOL(mipi_dsi_generic_write); * Return: The number of bytes successfully read or a negative error code on * failure. */ +#if defined(CONFIG_DISPLAY_SAMSUNG) +ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, void *params, + size_t num_params, void *data, size_t size) +#else ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params, size_t num_params, void *data, size_t size) +#endif { struct mipi_dsi_msg msg = { .channel = dsi->channel, @@ -650,8 +660,13 @@ EXPORT_SYMBOL(mipi_dsi_generic_read); * Return: The number of bytes successfully transmitted or a negative error * code on failure. */ +#if defined(CONFIG_DISPLAY_SAMSUNG) +ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, + void *data, size_t len) +#else ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, const void *data, size_t len) +#endif { struct mipi_dsi_msg msg = { .channel = dsi->channel, diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index ba71ce8466a6..39bc3b639ddc 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -1,3 +1,4 @@ +source "drivers/gpu/drm/msm/samsung/Kconfig" config DRM_MSM tristate "MSM DRM" @@ -149,3 +150,21 @@ config DRM_SDE_RSC avoids the display core power collapse. A client can also register for display core power collapse events on rsc. +config SEC_DISPLAYPORT + bool "SEC DISPLAYPORT feature" + depends on DRM_MSM + help + Samsung specific displayport 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. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index c81832ac9bd9..7224a97dc19c 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -3,6 +3,7 @@ ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi ccflags-y += -Idrivers/gpu/drm/msm/sde ccflags-y += -Idrivers/media/platform/msm/sde/rotator +ccflags-$(CONFIG_DISPLAY_SAMSUNG) += -Idrivers/gpu/drm/msm/samsung ccflags-y += -Idrivers/gpu/drm/msm/hdmi msm_drm-y := \ @@ -71,6 +72,14 @@ msm_drm-$(CONFIG_DRM_MSM_MDP5) += mdp/mdp_format.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \ +# sec displayport +msm_drm-$(CONFIG_SEC_DISPLAYPORT) += dp/secdp_sysfs.o \ + dp/secdp_logger.o \ + dp/secdp_unit_test.o \ + dp/secdp_aux_control.o \ + +msm_drm-$(CONFIG_SEC_DISPLAYPORT_BIGDATA) += dp/displayport_bigdata.o + msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \ sde_rsc_hw.o \ @@ -187,3 +196,7 @@ msm_drm-$(CONFIG_DRM_MSM) += \ msm_debugfs.o obj-$(CONFIG_DRM_MSM) += msm_drm.o + +ifeq ($(CONFIG_DISPLAY_SAMSUNG),y) +obj-y += samsung/ +endif diff --git a/drivers/gpu/drm/msm/dp/displayport_bigdata.c b/drivers/gpu/drm/msm/dp/displayport_bigdata.c new file mode 100644 index 000000000000..9976b8cb8ea5 --- /dev/null +++ b/drivers/gpu/drm/msm/dp/displayport_bigdata.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2017 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 +#include +#include +#include + +#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); + +static 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); +} + +static 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; +} + +static CLASS_ATTR(dp_error_info, 0664, secdp_bigdata_show, + secdp_bigdata_store); + +void secdp_bigdata_init(struct class *dp_class) +{ + int ret; + + ret = class_create_file(dp_class, &class_attr_dp_error_info); + if (ret) + pr_err("failed to create attr_dp_bigdata(%d)\n", ret); + + 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); + + if (item == BD_SINK_NAME) { + int i; + char t; + + for (i = 0; i < item_to_column[item].str_max_len; i++) { + t = ((char *)item_to_column[item].data)[i]; + + if (t == '\0') + break; + + switch (t) { + case '0'...'9': + case 'a'...'z': + case 'A'...'Z': + case '-': + case '_': + break; + default: + ((char *)item_to_column[item].data)[i] = ' '; + break; + } + } + } + } +} + +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; + } + } + 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(); + +} diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c index ea2e72ae817a..b3cd6fff310c 100644 --- a/drivers/gpu/drm/msm/dp/dp_audio.c +++ b/drivers/gpu/drm/msm/dp/dp_audio.c @@ -19,6 +19,13 @@ #include +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif +#endif + #include "dp_catalog.h" #include "dp_audio.h" #include "dp_panel.h" @@ -409,6 +416,11 @@ static int dp_audio_info_setup(struct platform_device *pdev, goto end; } +#ifdef CONFIG_SEC_DISPLAYPORT_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->dp_audio.ops_lock); audio->channels = params->num_of_channels; @@ -527,9 +539,11 @@ static int dp_audio_ack_done(struct platform_device *pdev, u32 ack) ack_hpd = ack & AUDIO_ACK_CONNECT; - pr_debug("acknowledging audio (%d)\n", ack_hpd); + pr_debug("acknowledging audio (%d), engine_on (%d)\n", ack_hpd, audio->engine_on); +#ifndef CONFIG_SEC_DISPLAYPORT if (!audio->engine_on) +#endif complete_all(&audio->hpd_comp); end: return rc; @@ -618,6 +632,9 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state) goto end; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (state == EXT_DISPLAY_CABLE_DISCONNECT) { +#endif reinit_completion(&audio->hpd_comp); rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 5); if (!rc) { @@ -625,7 +642,9 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state) rc = -ETIMEDOUT; goto end; } - +#ifdef CONFIG_SEC_DISPLAYPORT + } +#endif pr_debug("success\n"); end: return rc; @@ -642,6 +661,12 @@ static int dp_audio_on(struct dp_audio *dp_audio) return -EINVAL; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + return -EINVAL; + } +#endif audio = container_of(dp_audio, struct dp_audio_private, dp_audio); if (IS_ERR(audio)) { pr_err("invalid input\n"); @@ -684,6 +709,13 @@ static int dp_audio_off(struct dp_audio *dp_audio) audio = container_of(dp_audio, struct dp_audio_private, dp_audio); ext = &audio->ext_audio_data; +#ifdef CONFIG_SEC_DISPLAYPORT + if (!audio->session_on) { + pr_info("dp audio already off\n"); + return rc; + } +#endif + work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work); if (work_pending) pr_debug("pending notification work completed\n"); diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c index e60131bfae7c..643e4372e204 100644 --- a/drivers/gpu/drm/msm/dp/dp_aux.c +++ b/drivers/gpu/drm/msm/dp/dp_aux.c @@ -17,6 +17,13 @@ #include #include "dp_aux.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif +#include "secdp.h" +#include "secdp_aux_control.h" +#endif #define DP_AUX_ENUM_STR(x) #x @@ -240,6 +247,13 @@ static void dp_aux_native_handler(struct dp_aux_private *aux) aux->catalog->clear_hw_interrupts(aux->catalog); } +#ifdef CONFIG_SEC_DISPLAYPORT_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); } @@ -269,6 +283,13 @@ static void dp_aux_i2c_handler(struct dp_aux_private *aux) } } +#ifdef CONFIG_SEC_DISPLAYPORT_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); } @@ -449,9 +470,16 @@ static int dp_aux_transfer_ready(struct dp_aux_private *aux, goto error; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_fw_update_status()) { + dp_aux_update_offset_and_segment(aux, msg); + dp_aux_transfer_helper(aux, msg, send_seg); + } +#else dp_aux_update_offset_and_segment(aux, msg); dp_aux_transfer_helper(aux, msg, send_seg); +#endif aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ); @@ -549,6 +577,12 @@ 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)) { +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + goto unlock_exit; + } +#endif aux->retry_cnt++; if (!(aux->retry_cnt % retry_count)) aux->catalog->update_aux_cfg(aux->catalog, @@ -628,6 +662,10 @@ static void dp_aux_deinit(struct dp_aux *dp_aux) drm_dp_cec_unregister_connector(&aux->drm_aux); } +#ifdef CONFIG_SEC_DISPLAYPORT +static struct drm_dp_aux *g_drm_dp_aux; +#endif + static int dp_aux_register(struct dp_aux *dp_aux) { struct dp_aux_private *aux; @@ -650,6 +688,9 @@ static int dp_aux_register(struct dp_aux *dp_aux) goto exit; } dp_aux->drm_aux = &aux->drm_aux; +#ifdef CONFIG_SEC_DISPLAYPORT + g_drm_dp_aux = dp_aux->drm_aux; +#endif exit: return ret; } @@ -665,8 +706,45 @@ static void dp_aux_deregister(struct dp_aux *dp_aux) aux = container_of(dp_aux, struct dp_aux_private, dp_aux); drm_dp_aux_unregister(&aux->drm_aux); +#ifdef CONFIG_SEC_DISPLAYPORT + g_drm_dp_aux = NULL; +#endif } +#ifdef CONFIG_SEC_DISPLAYPORT +static ssize_t secdp_i2c_write(void *buffer, size_t size) +{ + if (!g_drm_dp_aux) + return -EIO; + + return drm_dp_i2c_write(g_drm_dp_aux, buffer, size); +} + +static ssize_t secdp_i2c_read(void *buffer, size_t size) +{ + if (!g_drm_dp_aux) + return -EIO; + + return drm_dp_i2c_read(g_drm_dp_aux, buffer, size); +} + +static ssize_t secdp_dpcd_write(unsigned int offset, void *buffer, size_t size) +{ + if (!g_drm_dp_aux) + return -EIO; + + return drm_dp_dpcd_write(g_drm_dp_aux, offset, buffer, size); +} + +static ssize_t secdp_dpcd_read(unsigned int offset, void *buffer, size_t size) +{ + if (!g_drm_dp_aux) + return -EIO; + + return drm_dp_dpcd_read(g_drm_dp_aux, offset, buffer, size); +} +#endif + static void dp_aux_dpcd_updated(struct dp_aux *dp_aux) { struct dp_aux_private *aux; @@ -742,6 +820,11 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog, dp_aux->dpcd_updated = dp_aux_dpcd_updated; dp_aux->set_sim_mode = dp_aux_set_sim_mode; +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_aux_dev_init(secdp_i2c_write, secdp_i2c_read, + secdp_dpcd_write, secdp_dpcd_read, secdp_get_hpd_status); +#endif + return dp_aux; error: return ERR_PTR(rc); diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index 94392d3290ca..b65b59a70ab3 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -20,6 +20,9 @@ #include "dp_catalog.h" #include "dp_reg.h" #include "msm_kms.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#endif #define DP_GET_MSB(x) (x >> 8) #define DP_GET_LSB(x) (x & 0xff) @@ -54,7 +57,9 @@ parser->get_io_buf(parser, #x); \ } -static u8 const vm_pre_emphasis[4][4] = { +#ifdef SECDP_CALIBRATE_VXPX +/* table for calibraton - DO NOT USE IN MARKET BINARY, sdm845 P-OS default */ +static u8 vm_pre_emphasis[4][4] = { {0x00, 0x0B, 0x14, 0xFF}, /* pe0, 0 db */ {0x00, 0x0B, 0x12, 0xFF}, /* pe1, 3.5 db */ {0x00, 0x0B, 0xFF, 0xFF}, /* pe2, 6.0 db */ @@ -62,12 +67,44 @@ static u8 const vm_pre_emphasis[4][4] = { }; /* voltage swing, 0.2v and 1.0v are not support */ -static u8 const vm_voltage_swing[4][4] = { +static u8 vm_voltage_swing[4][4] = { {0x07, 0x0F, 0x16, 0xFF}, /* sw0, 0.4v */ {0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6 v */ {0x19, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ {0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ }; +#else +/* sdm845 O-OS default */ +static u8 const vm_pre_emphasis[4][4] = { + {0x00, 0x0B, 0x12, 0xFF}, /* pe0, 0 db */ + {0x00, 0x0A, 0x12, 0xFF}, /* pe1, 3.5 db */ + {0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */ + {0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ +}; + +/* voltage swing, 0.2v and 1.0v are not support */ +static u8 const vm_voltage_swing[4][4] = { + {0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v */ + {0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */ + {0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ + {0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ +}; + +/* re-calibration for new FPCB. it's applied since hwid 05 */ +static u8 const vm_pre_emphasis5[4][4] = { + {0x0A, 0x0A, 0x0A, 0xFF}, /* pe0, 0 db */ + {0x0A, 0x0A, 0x0A, 0xFF}, /* pe1, 3.5 db */ + {0x0A, 0x0A, 0xFF, 0xFF}, /* pe2, 6.0 db */ + {0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ +}; + +static u8 const vm_voltage_swing5[4][4] = { + {0x1D, 0x1D, 0x1D, 0xFF}, /* sw0, 0.4v */ + {0x1D, 0x1D, 0x1D, 0xFF}, /* sw1, 0.6 v */ + {0x1D, 0x1D, 0xFF, 0xFF}, /* sw1, 0.8 v */ + {0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ +}; +#endif struct dp_catalog_io { struct dp_io_data *dp_ahb; @@ -100,6 +137,13 @@ static u32 dp_read(struct dp_catalog_private *catalog, { u32 data = 0; +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_clk_status(DP_CORE_PM)) { + pr_debug("core_clks_on: off\n"); + return 0; + } +#endif + if (!strcmp(catalog->exe_mode, "hw") || !strcmp(catalog->exe_mode, "all")) { data = readl_relaxed(io_data->io.base + offset); @@ -114,6 +158,13 @@ static u32 dp_read(struct dp_catalog_private *catalog, static void dp_write(struct dp_catalog_private *catalog, struct dp_io_data *io_data, u32 offset, u32 data) { +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_clk_status(DP_CORE_PM)) { + pr_debug("core_clks_on: off\n"); + return; + } +#endif + if (!strcmp(catalog->exe_mode, "hw") || !strcmp(catalog->exe_mode, "all")) writel_relaxed(data, io_data->io.base + offset); @@ -202,7 +253,15 @@ static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read) if (read) { data = dp_read(catalog, io_data, DP_AUX_TRANS_CTRL); +#ifdef CONFIG_SEC_DISPLAYPORT + /* 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(catalog, io_data, DP_AUX_TRANS_CTRL, data); } else { dp_write(catalog, io_data, DP_AUX_TRANS_CTRL, 0); @@ -226,6 +285,10 @@ static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux) io_data = catalog->io.dp_phy; data = dp_read(catalog, io_data, DP_PHY_AUX_INTERRUPT_STATUS); +#ifdef CONFIG_SEC_DISPLAYPORT + if (data) + pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data); +#endif dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f); wmb(); /* make sure 0x1f is written before next write */ @@ -301,6 +364,13 @@ static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux, return; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + return; + } +#endif + catalog = dp_catalog_get_priv(aux); io_data = catalog->io.dp_phy; @@ -1115,6 +1185,8 @@ static void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog_ctrl *ctrl, return; } + pr_debug("+++, flip:%d, ln_cnt:%d\n", flipped, ln_cnt); + catalog = dp_catalog_get_priv(ctrl); io_data = catalog->io.dp_phy; @@ -1144,6 +1216,14 @@ static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl, value0 = vm_voltage_swing[v_level][p_level]; value1 = vm_pre_emphasis[v_level][p_level]; +#if defined(CONFIG_SEC_DISPLAYPORT) && !defined(SECDP_CALIBRATE_VXPX) + pr_debug("aux_redrv: %u, rev_hw: %u\n", + catalog->parser->aux_redrv, catalog->parser->rev_hw); + if (catalog->parser->aux_redrv && catalog->parser->rev_hw >= 5) { + value0 = vm_voltage_swing5[v_level][p_level]; + value1 = vm_pre_emphasis5[v_level][p_level]; + } +#endif /* program default setting first */ @@ -1177,6 +1257,90 @@ static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl, } } +#ifdef SECDP_CALIBRATE_VXPX +void secdp_catalog_vx_show(void) +{ + u8 value0, value1, value2, value3; + int i; + + for (i = 0; i < 4; i++) { + value0 = vm_voltage_swing[i][0]; + value1 = vm_voltage_swing[i][1]; + value2 = vm_voltage_swing[i][2]; + value3 = vm_voltage_swing[i][3]; + pr_info("%02x,%02x,%02x,%02x\n", value0, value1, value2, value3); + } +} + +int secdp_catalog_vx_store(int *val, int size) +{ + int rc = 0; + + vm_voltage_swing[0][0] = val[0]; + vm_voltage_swing[0][1] = val[1]; + vm_voltage_swing[0][2] = val[2]; + vm_voltage_swing[0][3] = val[3]; + + vm_voltage_swing[1][0] = val[4]; + vm_voltage_swing[1][1] = val[5]; + vm_voltage_swing[1][2] = val[6]; + vm_voltage_swing[1][3] = val[7]; + + vm_voltage_swing[2][0] = val[8]; + vm_voltage_swing[2][1] = val[9]; + vm_voltage_swing[2][2] = val[10]; + vm_voltage_swing[2][3] = val[11]; + + vm_voltage_swing[3][0] = val[12]; + vm_voltage_swing[3][1] = val[13]; + vm_voltage_swing[3][2] = val[14]; + vm_voltage_swing[3][3] = val[15]; + + return rc; +} + +void secdp_catalog_px_show(void) +{ + u8 value0, value1, value2, value3; + int i; + + for (i = 0; i < 4; i++) { + value0 = vm_pre_emphasis[i][0]; + value1 = vm_pre_emphasis[i][1]; + value2 = vm_pre_emphasis[i][2]; + value3 = vm_pre_emphasis[i][3]; + pr_info("%02x,%02x,%02x,%02x\n", value0, value1, value2, value3); + } +} + +int secdp_catalog_px_store(int *val, int size) +{ + int rc = 0; + + vm_pre_emphasis[0][0] = val[0]; + vm_pre_emphasis[0][1] = val[1]; + vm_pre_emphasis[0][2] = val[2]; + vm_pre_emphasis[0][3] = val[3]; + + vm_pre_emphasis[1][0] = val[4]; + vm_pre_emphasis[1][1] = val[5]; + vm_pre_emphasis[1][2] = val[6]; + vm_pre_emphasis[1][3] = val[7]; + + vm_pre_emphasis[2][0] = val[8]; + vm_pre_emphasis[2][1] = val[9]; + vm_pre_emphasis[2][2] = val[10]; + vm_pre_emphasis[2][3] = val[11]; + + vm_pre_emphasis[3][0] = val[12]; + vm_pre_emphasis[3][1] = val[13]; + vm_pre_emphasis[3][2] = val[14]; + vm_pre_emphasis[3][3] = val[15]; + + return rc; +} +#endif + static void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog_ctrl *ctrl, u32 pattern) { diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 365fefc81d08..191a03cd72f1 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -20,6 +20,12 @@ #include "msm_kms.h" #include "dp_ctrl.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif +#include "secdp.h" +#endif #define DP_KHZ_TO_HZ 1000 @@ -67,6 +73,9 @@ struct dp_ctrl_private { struct dp_power *power; struct dp_parser *parser; struct dp_catalog_ctrl *catalog; +#ifdef CONFIG_SEC_DISPLAYPORT + bool link_train_status; +#endif struct completion idle_comp; struct completion video_comp; @@ -959,6 +968,10 @@ static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl) pr_debug("new bw code=0x%x\n", ctrl->link->link_params.bw_code); +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_save_item(BD_CUR_LINK_RATE, ctrl->link->link_params.bw_code); +#endif + return ret; } @@ -1041,6 +1054,15 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl) u8 encoding = 0x1; struct drm_dp_link link_info = {0}; +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + return -EIO; + } + + ctrl->link_train_status = false; +#endif + ctrl->link->phy_params.p_level = 0; ctrl->link->phy_params.v_level = 0; @@ -1081,11 +1103,22 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl) pr_info("link training #2 successful\n"); end: +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_cable_status()) { + pr_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); +#ifdef CONFIG_SEC_DISPLAYPORT + if (!ret) + ctrl->link_train_status = true; +#endif return ret; } @@ -1110,8 +1143,24 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train) ctrl->catalog->reset(ctrl->catalog); ret = dp_ctrl_link_train(ctrl); - if (ret) + if (ret) { +#ifdef CONFIG_SEC_DISPLAYPORT +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_inc_error_cnt(ERR_LINK_TRAIN); +#endif + if (!secdp_get_cable_status()) { + ret = 0; + goto send_video; + } +#ifndef SECDP_AUDIO_CTS + if (ctrl->link->link_params.bw_code == DP_LINK_BW_1_62) { + ret = 0; + goto send_video; + } +#endif +#endif goto end; + } send_video: /* @@ -1219,10 +1268,29 @@ static void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl) pr_debug("Host deinitialized successfully\n"); } +#ifdef CONFIG_SEC_DISPLAYPORT +static bool dp_ctrl_get_link_train_status(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + + if (!dp_ctrl) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + pr_info("link_train_status: %s\n", ctrl->link_train_status ? "success": "failure"); + return ctrl->link_train_status; +} +#endif + static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) { int ret = 0; struct dp_ctrl_private *ctrl; +#ifdef CONFIG_SEC_DISPLAYPORT + bool clk_on = false; +#endif if (!dp_ctrl) { pr_err("Invalid input data\n"); @@ -1246,6 +1314,16 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) ctrl->pixel_rate = ctrl->panel->get_pixel_clk(ctrl->panel); do { +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + if (clk_on) + dp_ctrl_disable_mainlink_clocks(ctrl); + ret = -EIO; + break; + } +#endif + if (ret == -EAGAIN) { /* try with lower link rate */ dp_ctrl_link_rate_down_shift(ctrl); @@ -1262,10 +1340,16 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) * link maintenance. */ dp_ctrl_disable_mainlink_clocks(ctrl); +#ifdef CONFIG_SEC_DISPLAYPORT + clk_on = false; +#endif ret = dp_ctrl_enable_mainlink_clocks(ctrl); if (ret) continue; +#ifdef CONFIG_SEC_DISPLAYPORT + clk_on = true; +#endif dp_ctrl_configure_source_params(ctrl); @@ -1389,6 +1473,74 @@ static void dp_ctrl_reset(struct dp_ctrl *dp_ctrl) ctrl->catalog->reset(ctrl->catalog); } +#ifdef SECDP_OPTIMAL_LINK_RATE +/* DP testbox list */ +static char secdp_tbox[][14] = { + "UNIGRAF TE", + "UFG DPR-120", + "UCD-400 DP", + "AGILENT ATR", + "UFG DP SINK", +}; + +/** 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_ctrl_private *ctrl) +{ + struct dp_panel *panel; + int i, rc; + bool ret = false; + + if (!ctrl || !ctrl->panel) + goto end; + + panel = ctrl->panel; + + for (i = 0; i < dim(secdp_tbox); i++) { + rc = strncmp(panel->monitor_name, secdp_tbox[i], + strlen(panel->monitor_name)); + if (!rc) { + pr_info("<%s> detected!\n", panel->monitor_name); + ret = true; + goto end; + } + } + + pr_info("real sink <%s>\n", panel->monitor_name); +end: + return ret; +} + +static u32 secdp_dp_gen_link_clk(struct dp_panel *dp_panel) +{ + u32 calc_link_rate = 540000; /* default HBR2 */ + u32 min_link_rate; + + if (!dp_panel) + goto end; + + min_link_rate = dp_panel->get_min_req_link_rate(dp_panel); + + if (min_link_rate == 0) + pr_info("timing not found, set default\n"); + else 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; + else + pr_err("too big!, set default\n"); + + pr_info("min_link_rate <%u>, calc_link_rate <%u>\n", + min_link_rate, calc_link_rate); +end: + return calc_link_rate; +} +#endif + static int dp_ctrl_on(struct dp_ctrl *dp_ctrl) { int rc = 0; @@ -1414,6 +1566,10 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl) if (!ctrl->panel->pinfo.pixel_clk_khz) ctrl->pixel_rate = phy_cts_pixel_clk_khz; } else { +#ifdef SECDP_OPTIMAL_LINK_RATE + if (!secdp_check_tbox(ctrl)) + rate = secdp_dp_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 = @@ -1446,12 +1602,27 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl) if (!rc) break; +#ifdef CONFIG_SEC_DISPLAYPORT + if (/*!secdp_get_hpd_status() ||*/ !secdp_get_cable_status()) { + pr_info("hpd_low or cable lost\n"); + link_train_max_retries = 2; + } +#endif + /* try with lower link rate */ dp_ctrl_link_rate_down_shift(ctrl); ctrl->catalog->mainlink_ctrl(ctrl->catalog, false); dp_ctrl_disable_mainlink_clocks(ctrl); + +#ifdef CONFIG_SEC_DISPLAYPORT + if (/*!secdp_get_hpd_status() ||*/ !secdp_get_cable_status()) { + pr_info("hpd_low or cable lost, skip to enable clk\n"); + link_train_max_retries = 2; + } +#endif + /* hw recommended delay before re-enabling clocks */ msleep(20); @@ -1551,6 +1722,9 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) dp_ctrl->reset = dp_ctrl_reset; dp_ctrl->link_maintenance = dp_ctrl_link_maintenance; dp_ctrl->process_phy_test_request = dp_ctrl_process_phy_test_request; +#ifdef CONFIG_SEC_DISPLAYPORT + dp_ctrl->get_link_train_status = dp_ctrl_get_link_train_status; +#endif return dp_ctrl; error: diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index 31d8f071b1f1..40bdc5bfd46b 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -34,6 +34,9 @@ struct dp_ctrl { bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl); void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl); int (*link_maintenance)(struct dp_ctrl *dp_ctrl); +#ifdef CONFIG_SEC_DISPLAYPORT + bool (*get_link_train_status)(struct dp_ctrl *dp_ctrl); +#endif }; struct dp_ctrl_in { diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 97de1b803f55..0776ec556214 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -25,6 +25,10 @@ #include "sde_connector.h" #include "dp_display.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#endif + #define DEBUG_NAME "drm_dp" struct dp_debug_private { diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 331d6ade2454..5c48f4a6a958 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -39,9 +39,46 @@ #include "sde_hdcp.h" #include "dp_debug.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif +#include "secdp.h" +#include "secdp_sysfs.h" +#define CCIC_DP_NOTI_REG_DELAY 30000 + +struct secdp_event { + struct dp_display_private *dp; + u32 id; +}; + +#define SECDP_EVENT_Q_MAX 4 + +struct secdp_event_data { + wait_queue_head_t event_q; + u32 pndx; + u32 gndx; + struct secdp_event event_list[SECDP_EVENT_Q_MAX]; + spinlock_t event_lock; +}; + +static void secdp_handle_attention(struct dp_display_private *dp); +#endif + static struct dp_display *g_dp_display; #define HPD_STRING_SIZE 30 +#ifdef CONFIG_SEC_DISPLAYPORT +static struct switch_dev switch_secdp = { + .name = "secdp", +}; + +static struct switch_dev switch_secdp_msg = { + .name = "secdp_msg", +}; +#endif + struct dp_hdcp { void *data; struct sde_hdcp_ops *ops; @@ -80,6 +117,21 @@ struct dp_display_private { struct dp_hdcp hdcp; +#ifdef CONFIG_SEC_DISPLAYPORT + struct completion dp_off_comp; + struct completion dp_on_comp; + + struct secdp_sysfs *sysfs; + struct secdp_misc sec; + + /* for ccic event handler */ + struct secdp_event_data dp_event; + struct task_struct *ev_thread; + struct list_head attention_head; + struct mutex attention_lock; + struct workqueue_struct *workq; + atomic_t notification_status; +#endif struct dp_usbpd_cb usbpd_cb; struct dp_display_mode mode; struct dp_display dp_display; @@ -99,8 +151,196 @@ static const struct of_device_id dp_dt_match[] = { {} }; + static void dp_display_update_hdcp_info(struct dp_display_private *dp); +#ifdef CONFIG_SEC_DISPLAYPORT +struct dp_display_private *g_secdp_priv; + +void secdp_send_poor_connection_event(void) +{ + struct dp_display_private *dp = g_secdp_priv; + + pr_info("poor connection!"); + + switch_set_state(&switch_secdp_msg, 1); + switch_set_state(&switch_secdp_msg, 0); + + dp->sec.dex.dex_node_status = dp->sec.dex.prev = dp->sec.dex.curr = DEX_DISABLED; +} + +bool secdp_get_power_status(void) +{ + if (!g_secdp_priv) + return false; + + return g_secdp_priv->power_on; +} + +/* check if dp cable has connected or not */ +bool secdp_get_cable_status(void) +{ + if (!g_secdp_priv) + return false; + + return g_secdp_priv->sec.cable_connected; +} + +/* check if hpd high has come or not */ +int secdp_get_hpd_status(void) +{ + if (!g_secdp_priv) + return 0; + + return g_secdp_priv->sec.hpd; +} + +bool secdp_get_poor_connection_status(void) +{ + struct dp_display_private *dp; + + if (!g_secdp_priv) + return false; + + dp = g_secdp_priv; + return dp->link->poor_connection; +} + +bool secdp_get_link_train_status(void) +{ + struct dp_display_private *dp; + + if (!g_secdp_priv) + return false; + + dp = g_secdp_priv; + return dp->ctrl->get_link_train_status(dp->ctrl); +} + +int secdp_read_branch_revision(struct dp_display_private *dp) +{ + int rlen = 0; + struct drm_dp_aux *drm_aux; + char *fw_ver; + + if (!dp || !dp->aux || !dp->aux->drm_aux) { + pr_err("invalid param\n"); + goto end; + } + + drm_aux = dp->aux->drm_aux; + fw_ver = dp->sec.dex.fw_ver; + + rlen = drm_dp_dpcd_read(drm_aux, DPCD_BRANCH_HW_REVISION, fw_ver, + LEN_BRANCH_REVISION); + if (rlen < 3) { + pr_err("read fail, rlen(%d)\n", rlen); + goto end; + } + + pr_info("branch revision: HW(0x%X), SW(0x%X, 0x%X)\n", + fw_ver[0], fw_ver[1], fw_ver[2]); + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_save_item(BD_ADAPTER_HWID, fw_ver[0]); + secdp_bigdata_save_item(BD_ADAPTER_FWVER, (fw_ver[1] << 8) | fw_ver[2]); +#endif + +end: + return rlen; +} + +void secdp_clear_branch_info(struct dp_display_private *dp) +{ + int i; + char *fw_ver; + + if (!dp) + goto end; + + fw_ver = dp->sec.dex.fw_ver; + + for (i = 0; i < LEN_BRANCH_REVISION; i++) + fw_ver[i] = 0; + +end: + return; +} + +static enum dex_support_res_t secdp_check_adapter_type(uint64_t ven_id, uint64_t prod_id) +{ + enum dex_support_res_t type = DEX_RES_DFT; /* default resolution */ + + pr_info("ven_id(0x%04x), prod_id(0x%04x)\n", (uint)ven_id, (uint)prod_id); + +#ifdef NOT_SUPPORT_DEX_RES_CHANGE + return DEX_RES_NOT_SUPPORT; +#endif + + if (ven_id == SAMSUNG_VENDOR_ID) { + switch (prod_id) { + case 0xa029: /* PAD */ + case 0xa020: /* Station */ + case 0xa02a: + case 0xa02b: + case 0xa02c: + case 0xa02d: + case 0xa02e: + case 0xa02f: + case 0xa030: + case 0xa031: + case 0xa032: + case 0xa033: + type = DEX_RES_MAX; + break; + default: + pr_info("it's SS dongle but UNKNOWN\n"); + break; + } + } else { + pr_info("it's NOT SS dongle\n"); + } + + return type; +} + +#ifndef SECDP_HDCP_DISABLE +static enum dex_adapter_t secdp_get_adapter(struct dp_display_private *dp) +{ + enum dex_adapter_t dongle = DEX_ADAPTER_NONE; + + if (dp->sec.vid != SAMSUNG_VENDOR_ID) { + pr_info("not samsung accessory\n"); + goto end; + } + + switch (dp->sec.pid) { + case DEXDOCK_PRODUCT_ID: + dongle = DEX_ADAPTER_DEXSTN; + break; + case HG950_PRODUCT_ID: + dongle = DEX_ADAPTER_HG950; + break; + case MPA2_PRODUCT_ID: + dongle = DEX_ADAPTER_MPA2; + break; + case DEXPAD_PRODUCT_ID: + dongle = DEX_ADAPTER_DEXPAD; + break; + case DEXCABLE_PRODUCT_ID: + dongle = DEX_ADAPTER_DEXCBL; + break; + default: + break; + } + +end: + pr_info("%s\n", secdp_dex_adapter_to_string(dongle)); + return dongle; +} +#endif +#endif + static bool dp_display_framework_ready(struct dp_display_private *dp) { return dp->dp_display.post_open ? false : true; @@ -136,6 +376,10 @@ static irqreturn_t dp_display_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_SEC_DISPLAYPORT +static void dp_display_handle_maintenance_req(struct dp_display_private *dp); +#endif + static void dp_display_hdcp_cb_work(struct work_struct *work) { struct dp_display_private *dp; @@ -167,10 +411,18 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) switch (dp->link->hdcp_status.hdcp_state) { case HDCP_STATE_AUTHENTICATING: +#ifdef CONFIG_SEC_DISPLAYPORT + if (dp->link->hdcp_status.hdcp_version < HDCP_VERSION_2P2) + secdp_reset_link_status(dp->link); +#endif if (dp->hdcp.ops && dp->hdcp.ops->authenticate) rc = dp->hdcp.ops->authenticate(dp->hdcp.data); break; case HDCP_STATE_AUTH_FAIL: +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_inc_error_cnt(ERR_HDCP_AUTH); +#endif + if (dp->power_on) { if (ops && ops->reauthenticate) { rc = ops->reauthenticate(dp->hdcp.data); @@ -257,6 +509,11 @@ static void dp_display_update_hdcp_info(struct dp_display_private *dp) if (hdcp2_present || hdcp1_present) { dp->hdcp.data = fd; dp->hdcp.ops = ops; + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_save_item(BD_HDCP_VER, + dp->link->hdcp_status.hdcp_version == HDCP_VERSION_2P2 ? "hdcp2" : "hdcp1"); +#endif } else { dp->hdcp.data = NULL; dp->hdcp.ops = NULL; @@ -359,6 +616,13 @@ static int dp_display_bind(struct device *dev, struct device *master, dp->dp_display.drm_dev = drm; dp->priv = drm->dev_private; + +#ifdef CONFIG_SEC_DISPLAYPORT + rc = secdp_init(pdev); + if (rc) + pr_err("secdp_init failed\n"); +#endif + end: return rc; } @@ -380,6 +644,10 @@ static void dp_display_unbind(struct device *dev, struct device *master, return; } +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_deinit(pdev); +#endif + (void)dp->power->power_client_deinit(dp->power); (void)dp->aux->drm_aux_deregister(dp->aux); dp_display_deinitialize_hdcp(dp); @@ -417,6 +685,11 @@ static void dp_display_send_hpd_event(struct dp_display_private *dp) return; } +#ifdef CONFIG_SEC_DISPLAYPORT + switch_set_state(&switch_secdp, (int)dp->dp_display.is_connected); + pr_info("secdp displayport uevent: %d\n", dp->dp_display.is_connected); + msleep(100); +#endif connector->status = connector->funcs->detect(connector, false); dev = dp->dp_display.connector->dev; @@ -475,6 +748,13 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp, { int ret = 0; +#ifdef CONFIG_SEC_DISPLAYPORT + if (hpd && !secdp_get_cable_status()) { + pr_info("cable is out\n"); + return -EIO; + } +#endif + dp->dp_display.is_connected = hpd; if (!dp_display_framework_ready(dp)) { @@ -491,9 +771,28 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp, reinit_completion(&dp->notification_comp); dp_display_send_hpd_event(dp); +#ifdef CONFIG_SEC_DISPLAYPORT + if (!hpd && !dp->power_on) { + pr_info("DP is already off, no wait\n"); + return 0; + } + + atomic_set(&dp->notification_status, 1); +#endif + if (!wait_for_completion_timeout(&dp->notification_comp, +#ifndef CONFIG_SEC_DISPLAYPORT HZ * 5)) { +#else + HZ * 15)) { +#endif pr_warn("%s timeout\n", hpd ? "connect" : "disconnect"); +#ifndef CONFIG_SEC_DISPLAYPORT + /* cancel any pending request */ + dp->ctrl->abort(dp->ctrl); + /* once timeout happens, this makes hdcp failure. not necessary */ + dp->aux->abort(dp->aux); +#endif ret = -EINVAL; } @@ -507,6 +806,13 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) int rc = 0; struct edid *edid; +#ifdef CONFIG_SEC_DISPLAYPORT + if (dp->dp_display.is_connected) { + pr_debug("it's already handled\n"); + return rc; + } +#endif + dp->aux->init(dp->aux, dp->parser->aux_cfg); if (dp->debug->psm_enabled) { @@ -519,6 +825,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) rc = dp->panel->read_sink_caps(dp->panel, dp->dp_display.connector, dp->usbpd->multi_func); +#ifndef CONFIG_SEC_DISPLAYPORT if (rc) { /* * ETIMEDOUT --> cable may have been removed @@ -529,13 +836,41 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) else goto notify; } +#else + if (rc) { + if (!secdp_get_hpd_status() || !secdp_get_cable_status() || rc == -EIO) { + pr_info("hpd_low or cable_lost or AUX failure\n"); +#ifndef CONFIG_SEC_FACTORY + dp->link->poor_connection = true; +#endif + goto end; + } + + pr_info("fall through failsafe\n"); + goto notify; + } + + dp->sec.dex.prev = secdp_check_dex_mode(); + pr_info("dex.setting_ui: %d, dex.curr: %d\n", dp->sec.dex.setting_ui, dp->sec.dex.curr); + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + if (dp->sec.dex.prev) + secdp_bigdata_save_item(BD_DP_MODE, "DEX"); + else + secdp_bigdata_save_item(BD_DP_MODE, "MIRROR"); +#endif + + secdp_read_branch_revision(dp); +#endif edid = dp->panel->edid_ctrl->edid; dp->audio_supported = drm_detect_monitor_audio(edid); dp->link->process_request(dp->link); + mutex_lock(&dp->audio->ops_lock); dp->panel->handle_sink_request(dp->panel); + mutex_unlock(&dp->audio->ops_lock); dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz; dp->dp_display.yuv_support = dp->parser->yuv_support; @@ -543,6 +878,15 @@ notify: dp_display_send_hpd_notification(dp, true); end: +#ifdef CONFIG_SEC_DISPLAYPORT +#ifndef CONFIG_SEC_FACTORY + if (dp->link->poor_connection) { + secdp_send_poor_connection_event(); + pr_info("poor connection! send secdp_msg uevent\n"); + dp->link->poor_connection = 0; + } +#endif +#endif return rc; } @@ -590,6 +934,11 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) return 0; } +#ifdef CONFIG_SEC_DISPLAYPORT + cancel_delayed_work_sync(&dp->sec.hdcp_start_work); + cancel_delayed_work(&dp->sec.link_status_work); +#endif + if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); @@ -598,6 +947,17 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) dp->audio_status = -ENODEV; +#ifdef CONFIG_SEC_DISPLAYPORT + if (!dp->power_on && secdp_get_cable_status()) { + pr_info("DP is already off, skip\n"); + if (dp->dp_display.is_connected) { + pr_info("platform reset? clear!\n"); + dp->dp_display.is_connected = false; + } + dp->panel->video_test = false; + return rc; + } +#endif rc = dp_display_send_hpd_notification(dp, false); dp->panel->video_test = false; @@ -605,6 +965,84 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) return rc; } +#ifdef CONFIG_SEC_DISPLAYPORT +void secdp_dex_do_reconnecting(void) +{ + struct dp_display_private *dp = g_secdp_priv; + int ret = 0; + + if (dp->link->poor_connection) { + pr_info("poor connection, return!\n"); + return; + } + + init_completion(&dp->dp_on_comp); + if (!dp->power_on) { + ret = wait_for_completion_timeout(&dp->dp_on_comp, msecs_to_jiffies(3000)); + if (ret <= 0) { + pr_err("dp_on_comp timedout\n"); + return; + } + } + + mutex_lock(&dp->attention_lock); + pr_info("dex_reconnect hpd low++\n"); + dp->sec.dex.reconnecting = 1; + dp->sec.dex.dex_node_status = 2; + + if (dp->sec.dex.curr == DEX_ENABLED) + dp->sec.dex.curr = DEX_DURING_MODE_CHANGE; + + dp->usbpd->hpd_high = false; + dp_display_host_init(dp); + dp_display_process_hpd_low(dp); + + pr_info("dex_reconnect hpd low--\n"); + mutex_unlock(&dp->attention_lock); + + msleep(400); + + mutex_lock(&dp->attention_lock); + if (!dp->power_on && dp->sec.dex.reconnecting) { + pr_info("dex_reconnect hpd high++\n"); + + dp->usbpd->hpd_high = true; + dp_display_host_init(dp); + dp_display_process_hpd_high(dp); + + pr_info("dex_reconnect hpd high--\n"); + } + dp->sec.dex.reconnecting = 0; + mutex_unlock(&dp->attention_lock); +} + +/** check if dex is running now */ +bool secdp_check_dex_mode(void) +{ + struct dp_display_private *dp = g_secdp_priv; + bool mode = false; + + if (dp->sec.dex.res == DEX_RES_NOT_SUPPORT) + goto end; + + if (dp->sec.dex.setting_ui == DEX_DISABLED && dp->sec.dex.curr == DEX_DISABLED) + goto end; + + mode = true; +end: + return mode; +} + +/** get dex resolution. it depends on which dongle/adapter is connected */ +enum dex_support_res_t secdp_get_dex_res(void) +{ + struct dp_display_private *dp = g_secdp_priv; + + return dp->sec.dex.res; +} +#endif + +#ifndef CONFIG_SEC_DISPLAYPORT static int dp_display_usbpd_configure_cb(struct device *dev) { int rc = 0; @@ -633,9 +1071,25 @@ static int dp_display_usbpd_configure_cb(struct device *dev) end: return rc; } +#else +static int secdp_display_usbpd_configure_cb(void) +{ + int rc = 0; + struct dp_display_private *dp = g_secdp_priv; + + dp_display_host_init(dp); + + + return rc; +} +#endif static void dp_display_clean(struct dp_display_private *dp) { +#ifdef CONFIG_SEC_DISPLAYPORT + cancel_delayed_work_sync(&dp->sec.hdcp_start_work); + cancel_delayed_work(&dp->sec.link_status_work); +#endif if (dp_display_is_hdcp_enabled(dp)) { dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE; @@ -673,7 +1127,7 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) return rc; } - +#ifndef CONFIG_SEC_DISPLAYPORT static int dp_display_usbpd_disconnect_cb(struct device *dev) { int rc = 0; @@ -716,19 +1170,91 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) end: return rc; } +#else +static int secdp_display_usbpd_disconnect_cb(void) +{ + int rc = 0; + struct dp_display_private *dp = g_secdp_priv; + + if (!dp) { + pr_err("no driver data found\n"); + rc = -ENODEV; + goto end; + } + + pr_debug("+++, psm(%d)\n", dp->debug->psm_enabled); + + if (atomic_read(&dp->notification_status)) { + reinit_completion(&dp->notification_comp); + pr_info("wait for connection logic++\n"); + if (atomic_read(&dp->notification_status) && + !wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) { + + pr_warn("notification_comp timeout\n"); + } + pr_info("wait for connection logic--\n"); + } + + atomic_set(&dp->notification_status, 0); + + /* + * In case cable/dongle is disconnected during adb shell stop, + * reset psm_enabled flag to false since it is no more needed + */ + if (dp->dp_display.post_open) + dp->debug->psm_enabled = false; + + if (dp->debug->psm_enabled) + dp->link->psm_config(dp->link, &dp->panel->link_info, true); + + /* cancel any pending request */ + atomic_set(&dp->aborted, 1); + dp->ctrl->abort(dp->ctrl); + dp->aux->abort(dp->aux); + + /* wait for idle state */ + flush_workqueue(dp->wq); + + dp_display_handle_disconnect(dp); + atomic_set(&dp->aborted, 0); + +end: + return rc; +} +#endif static void dp_display_handle_maintenance_req(struct dp_display_private *dp) { +#ifdef CONFIG_SEC_DISPLAYPORT + int ret; + + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + return; + } +#endif mutex_lock(&dp->audio->ops_lock); if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status)) dp->audio->off(dp->audio); +#ifndef CONFIG_SEC_DISPLAYPORT dp->ctrl->link_maintenance(dp->ctrl); if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status)) dp->audio_status = dp->audio->on(dp->audio); - +#else + ret = dp->ctrl->link_maintenance(dp->ctrl); + if (!ret && dp->audio_supported) { +/* In case of poor connection, hpd irq is coming continuously in a short time. + * and audio off/on fucntions are called rapidly. + * It can causes dp audio problem. so, added delay. + */ + if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) + msleep(30); + dp->audio->on(dp->audio); + } +#endif mutex_unlock(&dp->audio->ops_lock); } @@ -737,11 +1263,15 @@ static void dp_display_attention_work(struct work_struct *work) struct dp_display_private *dp = container_of(work, struct dp_display_private, attention_work); - if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq) { - if (!dp->hdcp.ops->cp_irq(dp->hdcp.data)) - return; +#ifdef CONFIG_SEC_DISPLAYPORT + if (!secdp_get_hpd_status() || !secdp_get_cable_status()) { + pr_info("hpd_low or cable_lost\n"); + return; } + pr_debug("+++, sink_request: 0x%08x\n", dp->link->sink_request); +#endif + if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) { dp_display_handle_disconnect(dp); @@ -750,6 +1280,11 @@ static void dp_display_attention_work(struct work_struct *work) return; } +#ifdef CONFIG_SEC_DISPLAYPORT + /* add some delay to guarantee hpd event handling in framework side */ + msleep(60); +#endif + queue_work(dp->wq, &dp->connect_work); return; } @@ -765,22 +1300,28 @@ static void dp_display_attention_work(struct work_struct *work) } if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { +#ifdef CONFIG_SEC_DISPLAYPORT + pr_debug("[PHY CTS] cancelling poor connection check!\n"); + cancel_delayed_work(&dp->sec.link_status_work); +#endif dp->ctrl->process_phy_test_request(dp->ctrl); return; } - if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) { - dp_display_handle_maintenance_req(dp); - return; - } - if (dp->link->sink_request & DP_TEST_LINK_TRAINING) { dp->link->send_test_response(dp->link); dp_display_handle_maintenance_req(dp); return; } + + if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) + dp_display_handle_maintenance_req(dp); + + if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq) + dp->hdcp.ops->cp_irq(dp->hdcp.data); } +#ifndef CONFIG_SEC_DISPLAYPORT static int dp_display_usbpd_attention_cb(struct device *dev) { struct dp_display_private *dp; @@ -823,6 +1364,575 @@ static int dp_display_usbpd_attention_cb(struct device *dev) return 0; } +#else +static int secdp_event_thread(void *data) +{ + unsigned long flag; + u32 todo = 0; + + struct secdp_event_data *ev_data; + struct secdp_event *ev; + struct dp_display_private *dp = NULL; + + if (!data) + return -EINVAL; + + ev_data = (struct secdp_event_data *)data; + init_waitqueue_head(&ev_data->event_q); + spin_lock_init(&ev_data->event_lock); + + while (!kthread_should_stop()) { + wait_event(ev_data->event_q, + (ev_data->pndx != ev_data->gndx) || + kthread_should_stop()); + spin_lock_irqsave(&ev_data->event_lock, flag); + ev = &(ev_data->event_list[ev_data->gndx++]); + todo = ev->id; + dp = ev->dp; + ev->id = 0; + ev_data->gndx %= SECDP_EVENT_Q_MAX; + spin_unlock_irqrestore(&ev_data->event_lock, flag); + + pr_debug("todo=%s\n", secdp_ev_event_to_string(todo)); + + switch (todo) { + case EV_USBPD_ATTENTION: + secdp_handle_attention(dp); + break; + default: + pr_err("Unknown event:%d\n", todo); + } + } + + return 0; +} + +static int secdp_event_setup(struct dp_display_private *dp) +{ + + dp->ev_thread = kthread_run(secdp_event_thread, + (void *)&dp->dp_event, "secdp_event"); + if (IS_ERR(dp->ev_thread)) { + pr_err("unable to start event thread\n"); + return PTR_ERR(dp->ev_thread); + } + + dp->workq = create_workqueue("secdp_hpd"); + if (!dp->workq) { + pr_err("error creating workqueue\n"); + return -EPERM; + } + + INIT_LIST_HEAD(&dp->attention_head); + return 0; +} + +static void secdp_event_cleanup(struct dp_display_private *dp) +{ + destroy_workqueue(dp->workq); + + if (dp->ev_thread == current) + return; + + kthread_stop(dp->ev_thread); +} + +static void secdp_send_events(struct dp_display_private *dp, u32 event) +{ + struct secdp_event *ev; + struct secdp_event_data *ev_data = &dp->dp_event; + + pr_debug("event=%s\n", secdp_ev_event_to_string(event)); + + spin_lock(&ev_data->event_lock); + ev = &ev_data->event_list[ev_data->pndx++]; + ev->id = event; + ev->dp = dp; + ev_data->pndx %= SECDP_EVENT_Q_MAX; + wake_up(&ev_data->event_q); + spin_unlock(&ev_data->event_lock); +} + +static void secdp_process_attention(struct dp_display_private *dp, + CC_NOTI_TYPEDEF *noti) +{ + int rc = 0; + + if (!noti || !dp) + goto end; + + pr_debug("sub1(%d), sub2(%d), sub3(%d)\n", noti->sub1, noti->sub2, noti->sub3); + + dp->sec.dex.reconnecting = 0; + + if (noti->id == CCIC_NOTIFY_ID_DP_CONNECT && noti->sub1 == CCIC_NOTIFY_DETACH) { + dp->usbpd->hpd_high = false; + + rc = secdp_display_usbpd_disconnect_cb(); + goto end; + } + + if (noti->id == CCIC_NOTIFY_ID_DP_HPD && + noti->sub1 == CCIC_NOTIFY_HIGH && noti->sub2 == CCIC_NOTIFY_IRQ) { + + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + goto end; + } + + if (dp->link->poor_connection) { + pr_info("poor connection\n"); + goto end; + } + + if (!dp->power_on) { + flush_workqueue(dp->wq); + if (!dp->power_on) { + pr_debug("handle it as hpd high\n"); + + if (dp->dp_display.is_connected) { + pr_info("platform reset!\n"); + secdp_display_usbpd_disconnect_cb(); + msleep(100); + } + goto handle_hpd_high; + } + } + + /* do the same with: dp_display_usbpd_attention_cb */ + dp->link->process_request(dp->link); + queue_work(dp->wq, &dp->attention_work); + + goto end; + } + + if (noti->id == CCIC_NOTIFY_ID_DP_HPD && noti->sub1 == CCIC_NOTIFY_LOW) { + dp->sec.dex.dex_node_status = dp->sec.dex.prev = dp->sec.dex.curr = DEX_DISABLED; + secdp_clear_link_status_update_cnt(dp->link); + +/* Following functions were copied from dp_display_usbpd_disconnect_cb() fucntion. + * Sometimes they make connection status abnormal and it's causing CTS failure + * when running CTS continuously. so, we commented it out. + */ +#if 0 + /* cancel any pending request */ + atomic_set(&dp->aborted, 1); + dp->ctrl->abort(dp->ctrl); + dp->aux->abort(dp->aux); +#endif + + /* wait for idle state */ + cancel_work(&dp->connect_work); + flush_workqueue(dp->wq); + + dp_display_handle_disconnect(dp); + atomic_set(&dp->aborted, 0); + goto end; + } + +handle_hpd_high: + /* handle it as hpd high */ + pr_info("is_connected <%d>\n", dp->dp_display.is_connected); + if (!dp->dp_display.is_connected) { + secdp_clear_link_status_update_cnt(dp->link); + queue_work(dp->wq, &dp->connect_work); + } + +end: + return; +} + +static void secdp_handle_attention(struct dp_display_private *dp) +{ + int i = 0; + + while (!list_empty_careful(&dp->attention_head)) { + struct secdp_attention_node *node; + + pr_debug("+++ processing item %d in the list +++\n", ++i); + + mutex_lock(&dp->attention_lock); + node = list_first_entry(&dp->attention_head, + struct secdp_attention_node, list); + + secdp_process_attention(dp, &node->noti); + +#ifdef CONFIG_SEC_DISPLAYPORT + /* add some delay to guarantee hpd event handling in framework side */ + msleep(60); +#endif + + list_del(&node->list); + mutex_unlock(&dp->attention_lock); + + kzfree(node); + + pr_debug("--- processing item %d in the list ---\n", i); + }; + +} + +static int secdp_ccic_noti_cb(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct dp_display_private *dp = g_secdp_priv; + CC_NOTI_TYPEDEF noti = *(CC_NOTI_TYPEDEF *)data; + struct secdp_attention_node *node; + int rc = 0; + + if (noti.dest != CCIC_NOTIFY_DEV_DP) { + /*pr_debug("not DP, skip\n");*/ + goto end; + } + + switch (noti.id) { + case CCIC_NOTIFY_ID_ATTACH: + pr_info("CCIC_NOTIFY_ID_ATTACH\n"); + break; + + case CCIC_NOTIFY_ID_DP_CONNECT: + pr_info("CCIC_NOTIFY_ID_DP_CONNECT, <%d>\n", noti.sub1); + + switch (noti.sub1) { + case CCIC_NOTIFY_ATTACH: + dp->sec.cable_connected = true; + dp->usbpd->alt_mode_cfg_done = true; + secdp_clear_link_status_update_cnt(dp->link); + dp->usbpd->orientation = secdp_get_plug_orientation(); + dp->sec.vid = noti.sub2; + dp->sec.pid = noti.sub3; + dp->sec.dex.res = + secdp_check_adapter_type(noti.sub2, noti.sub3); +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_connection(); + secdp_bigdata_save_item(BD_ORIENTATION, + dp->usbpd->orientation == ORIENTATION_CC1 ? "CC1" : "CC2"); + secdp_bigdata_save_item(BD_ADT_VID, noti.sub2); + secdp_bigdata_save_item(BD_ADT_PID, noti.sub3); +#endif + secdp_logger_set_max_count(300); + +#ifndef SECDP_USB_CONCURRENCY + /* see dp_display_usbpd_configure_cb() */ + dp_display_host_init(dp); +#endif + + break; + + case CCIC_NOTIFY_DETACH: + if (!secdp_get_cable_status()) { + pr_info("already disconnected\n"); + goto end; + } + + secdp_logger_set_max_count(300); +#ifdef CONFIG_COMBO_REDRIVER + secdp_redriver_onoff(false, 0); +#endif + /* set flags here as soon as disconnected + * resource clear will be made later at "secdp_process_attention" + */ + dp->sec.dex.dex_node_status = dp->sec.dex.prev = dp->sec.dex.curr = DEX_DISABLED; + dp->sec.dex.res = DEX_RES_NOT_SUPPORT; + dp->sec.dex.reconnecting = 0; + dp->sec.vid = -1; + dp->sec.pid = -1; + dp->sec.cable_connected = false; + dp->sec.link_conf = false; + dp->sec.hpd = false; + dp->usbpd->orientation = ORIENTATION_NONE; + secdp_clear_link_status_update_cnt(dp->link); + dp->usbpd->alt_mode_cfg_done = false; + + secdp_clear_branch_info(dp); +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_disconnection(); +#endif + break; + + default: + break; + } + break; + + case CCIC_NOTIFY_ID_DP_LINK_CONF: + pr_info("CCIC_NOTIFY_ID_DP_LINK_CONF, <%c>\n", + noti.sub1 + 'A' - 1); + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + goto end; + } + +#ifdef SECDP_USB_CONCURRENCY + if (noti.sub1 == CCIC_NOTIFY_DP_PIN_B || + noti.sub1 == CCIC_NOTIFY_DP_PIN_D || + noti.sub1 == CCIC_NOTIFY_DP_PIN_F) { + dp->usbpd->multi_func = true; +#if defined(CONFIG_COMBO_REDRIVER) + secdp_redriver_onoff(true, 2); +#endif + } else { + dp->usbpd->multi_func = false; +#if defined(CONFIG_COMBO_REDRIVER) + secdp_redriver_onoff(true, 4); +#endif + } + + pr_info("multi_func: <%d>\n", dp->usbpd->multi_func); + + /* see dp_display_usbpd_configure_cb() */ + dp_display_host_init(dp); +#endif + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_save_item(BD_LINK_CONFIGURE, noti.sub1 + 'A' - 1); +#endif + dp->sec.link_conf = true; + break; + + case CCIC_NOTIFY_ID_DP_HPD: + pr_info("CCIC_NOTIFY_ID_DP_HPD, sub1 <%s>, sub2<%s>\n", + (noti.sub1 == CCIC_NOTIFY_HIGH) ? "high" : + ((noti.sub1 == CCIC_NOTIFY_LOW) ? "low" : "??"), + (noti.sub2 == CCIC_NOTIFY_IRQ) ? "irq" : "??"); + if (!secdp_get_cable_status()) { + pr_info("cable is out\n"); + goto end; + } + + if (noti.sub1 == CCIC_NOTIFY_HIGH) { + secdp_logger_set_max_count(300); + dp->sec.hpd = true; + } else /* if (noti.sub1 == CCIC_NOTIFY_LOW) */ { + dp->sec.hpd = false; + } + + break; + + default: + break; + } + + pr_debug("sec.link_conf(%d), sec.hpd(%d)\n", dp->sec.link_conf, dp->sec.hpd); + if ((dp->sec.link_conf && dp->sec.hpd) || /*hpd high? or hpd_irq?*/ + (noti.id == CCIC_NOTIFY_ID_DP_HPD && noti.sub1 == CCIC_NOTIFY_LOW) || /*hpd low?*/ + (noti.id == CCIC_NOTIFY_ID_DP_CONNECT && noti.sub1 == CCIC_NOTIFY_DETACH)) { /*cable disconnect?*/ + + node = kzalloc(sizeof(*node), GFP_KERNEL); + + node->noti.src = noti.src; + node->noti.dest = noti.dest; + node->noti.id = noti.id; + node->noti.sub1 = noti.sub1; + node->noti.sub2 = noti.sub2; + node->noti.sub3 = noti.sub3; + + mutex_lock(&dp->attention_lock); + list_add_tail(&node->list, &dp->attention_head); + mutex_unlock(&dp->attention_lock); + + secdp_send_events(dp, EV_USBPD_ATTENTION); + + if ((noti.id == CCIC_NOTIFY_ID_DP_CONNECT && noti.sub1 == CCIC_NOTIFY_DETACH) && + (dp->power_on == true || atomic_read(&dp->notification_status))) { + int ret; + + init_completion(&dp->dp_off_comp); + ret = wait_for_completion_timeout(&dp->dp_off_comp, msecs_to_jiffies(10000)); + if (ret <= 0) + pr_err("dp_off_comp timedout\n"); + else + pr_debug("detach complete!\n"); + + atomic_set(&dp->notification_status, 0); + } + } + +end: + return rc; +} + +int secdp_ccic_noti_register_ex(struct secdp_misc *sec, bool retry) +{ + int rc; + + sec->ccic_noti_registered = true; + rc = manager_notifier_register(&sec->ccic_noti_block, + secdp_ccic_noti_cb, MANAGER_NOTIFY_CCIC_DP); + if (!rc) { + pr_debug("success\n"); + goto exit; + } + + sec->ccic_noti_registered = false; + if (!retry) { + pr_debug("no more try\n"); + goto exit; + } + + pr_err("manager_dev is not ready yet, rc(%d)\n", rc); + pr_err("try again in %d[ms]\n", CCIC_DP_NOTI_REG_DELAY); + + schedule_delayed_work(&sec->ccic_noti_reg_work, + msecs_to_jiffies(CCIC_DP_NOTI_REG_DELAY)); + +exit: + return rc; +} + +static void secdp_ccic_noti_register(struct work_struct *work) +{ + int rc; + struct dp_display_private *dp = g_secdp_priv; + struct secdp_misc *sec = &dp->sec; + + mutex_lock(&sec->notifier_lock); + if (sec->ccic_noti_registered) { + pr_info("already registered\n"); + goto exit; + } + + rc = secdp_ccic_noti_register_ex(sec, true); + if (rc) { + pr_err("fail, rc(%d)\n", rc); + goto exit; + } + + pr_debug("success\n"); + sec->ccic_noti_registered = true; + + /* cancel immediately */ + rc = cancel_delayed_work(&sec->ccic_noti_reg_work); + pr_debug("cancel_work, rc(%d)\n", rc); + destroy_delayed_work_on_stack(&sec->ccic_noti_reg_work); + +exit: + mutex_unlock(&sec->notifier_lock); + return; +} + +static void secdp_hdcp_start_work(struct work_struct *work) +{ + struct dp_display_private *dp = g_secdp_priv; + + if (secdp_get_cable_status() && dp->power_on) { + dp_display_update_hdcp_info(dp); + + if (dp_display_is_hdcp_enabled(dp)) { + cancel_delayed_work_sync(&dp->hdcp_cb_work); + + dp->link->hdcp_status.hdcp_state = HDCP_STATE_AUTHENTICATING; + queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2); + } + } +} + +/* This logic is to check poor DP connection. if link train is failed or + * irq hpd is coming more than 4th times in 13 sec, regard it as a poor connection + * and do disconnection of displayport + */ +static void secdp_link_status_work(struct work_struct *work) +{ + struct dp_display_private *dp = g_secdp_priv; + + pr_info("+++ status_update_cnt %d\n", dp->link->status_update_cnt); + + if (secdp_get_cable_status() && dp->power_on) { + + if (!dp->ctrl->get_link_train_status(dp->ctrl) || + dp->link->status_update_cnt > 4) { + + dp->link->poor_connection = true; + dp->link->status_update_cnt = 0; + + /* cancel any pending request */ + dp->ctrl->abort(dp->ctrl); + dp->aux->abort(dp->aux); + + /* wait for idle state */ + flush_workqueue(dp->wq); + + dp_display_handle_disconnect(dp); + + secdp_send_poor_connection_event(); + pr_info("poor connection! send secdp_msg uevent\n"); + } else { + if (!secdp_check_link_stable(dp->link)) { + pr_info("Check poor connection, again\n"); + schedule_delayed_work(&dp->sec.link_status_work, + msecs_to_jiffies(3000)); + } + } + } +} + +int secdp_init(struct platform_device *pdev) +{ + int rc = -1; + struct dp_display_private *dp; + + dp = platform_get_drvdata(pdev); + + INIT_DELAYED_WORK(&dp->sec.hdcp_start_work, secdp_hdcp_start_work); + INIT_DELAYED_WORK(&dp->sec.link_status_work, secdp_link_status_work); + + INIT_DELAYED_WORK(&dp->sec.ccic_noti_reg_work, secdp_ccic_noti_register); + schedule_delayed_work(&dp->sec.ccic_noti_reg_work, + msecs_to_jiffies(CCIC_DP_NOTI_REG_DELAY)); + + rc = secdp_power_request_gpios(dp->power); + if (rc) + pr_err("DRM DP gpio request failed\n"); + + rc = secdp_sysfs_init(); + if (rc) + pr_err("secdp_sysfs_init failed\n"); + + mutex_init(&dp->attention_lock); + mutex_init(&dp->sec.notifier_lock); + + rc = secdp_event_setup(dp); + if (rc) + pr_err("secdp_event_setup failed\n"); + + pr_info("exit, rc(%d)\n", rc); + return rc; +} + +void secdp_deinit(struct platform_device *pdev) +{ + struct dp_display_private *dp; + + dp = platform_get_drvdata(pdev); + + secdp_event_cleanup(dp); +} + +struct dp_panel *secdp_get_panel_info(void) +{ + struct dp_display_private *dp = g_secdp_priv; + struct dp_panel *panel = NULL; + + if (dp) + panel = dp->panel; + + pr_debug("panel\n"); + return panel; +} + +struct drm_connector *secdp_get_connector(void) +{ + struct dp_display *dp_disp = g_dp_display; + struct drm_connector *connector = NULL; + + if (dp_disp) + connector = dp_disp->connector; + + pr_debug("connector\n"); + return connector; +} +#endif + static void dp_display_connect_work(struct work_struct *work) { struct dp_display_private *dp = container_of(work, @@ -838,6 +1948,9 @@ static void dp_display_connect_work(struct work_struct *work) return; } +#ifdef CONFIG_SEC_DISPLAYPORT + dp_display_host_init(dp); +#endif dp_display_process_hpd_high(dp); } @@ -845,6 +1958,9 @@ static void dp_display_deinit_sub_modules(struct dp_display_private *dp) { dp_audio_put(dp->audio); dp_ctrl_put(dp->ctrl); +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_sysfs_put(dp->sysfs); +#endif dp_link_put(dp->link); dp_panel_put(dp->panel); dp_aux_put(dp->aux); @@ -928,9 +2044,23 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_link; } +#ifdef CONFIG_SEC_DISPLAYPORT + dp->sysfs = secdp_sysfs_get(dev, &dp->sec); + if (IS_ERR(dp->sysfs)) { + rc = PTR_ERR(dp->sysfs); + pr_err("failed to initialize sysfs, rc = %d\n", rc); + dp->sysfs = NULL; + goto error_sysfs; + } +#endif + panel_in.aux = dp->aux; panel_in.catalog = &dp->catalog->panel; panel_in.link = dp->link; +#ifdef CONFIG_SEC_DISPLAYPORT + panel_in.connector = dp->dp_display.connector; + panel_in.parser = dp->parser; +#endif dp->panel = dp_panel_get(&panel_in); if (IS_ERR(dp->panel)) { @@ -963,11 +2093,19 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_audio; } +#ifndef CONFIG_SEC_DISPLAYPORT cb->configure = dp_display_usbpd_configure_cb; cb->disconnect = dp_display_usbpd_disconnect_cb; cb->attention = dp_display_usbpd_attention_cb; dp->usbpd = dp_usbpd_get(dev, cb); +#else + cb->configure = secdp_display_usbpd_configure_cb; + cb->disconnect = secdp_display_usbpd_disconnect_cb; + + dp->usbpd = secdp_usbpd_get(dev, cb); +#endif + if (IS_ERR(dp->usbpd)) { rc = PTR_ERR(dp->usbpd); pr_err("failed to initialize usbpd, rc = %d\n", rc); @@ -996,6 +2134,10 @@ error_ctrl: dp_panel_put(dp->panel); error_panel: dp_link_put(dp->link); +#ifdef CONFIG_SEC_DISPLAYPORT +error_sysfs: + secdp_sysfs_put(dp->sysfs); +#endif error_link: dp_aux_put(dp->aux); error_aux: @@ -1103,10 +2245,12 @@ static int dp_display_enable(struct dp_display *dp_display) goto end; } +#ifndef CONFIG_SEC_DISPLAYPORT if (atomic_read(&dp->aborted)) { pr_err("aborted\n"); goto end; } +#endif dp->aux->init(dp->aux, dp->parser->aux_cfg); @@ -1145,10 +2289,12 @@ static int dp_display_post_enable(struct dp_display *dp_display) goto end; } +#ifndef CONFIG_SEC_DISPLAYPORT if (atomic_read(&dp->aborted)) { pr_err("aborted\n"); goto end; } +#endif dp->panel->spd_config(dp->panel); @@ -1158,11 +2304,35 @@ static int dp_display_post_enable(struct dp_display *dp_display) dp->audio_status = dp->audio->on(dp->audio); } +#ifndef CONFIG_SEC_DISPLAYPORT if (dp->hdcp.feature_enabled && 0) { /* bootsplash check */ cancel_delayed_work_sync(&dp->hdcp_cb_work); queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2); } +#else +#ifdef SECDP_HDCP_DISABLE + pr_info("skip hdcp\n"); +#else +{ + unsigned int hdcp_delay = 3500; + if (secdp_get_adapter(dp) == DEX_ADAPTER_MPA2) { +#if 0/*def CONFIG_SEC_GTS4LV_PROJECT*/ + hdcp_delay = 10; +#endif + } + pr_debug("hdcp_delay: %u ms\n", hdcp_delay); + schedule_delayed_work(&dp->sec.hdcp_start_work, msecs_to_jiffies(hdcp_delay)); +} +#endif + +#ifndef CONFIG_SEC_FACTORY + schedule_delayed_work(&dp->sec.link_status_work, + msecs_to_jiffies(13000)); +#else + pr_info("skip checking poor connection\n"); +#endif +#endif dp->panel->setup_hdr(dp->panel, NULL); end: @@ -1170,6 +2340,10 @@ end: dp_display->post_open = NULL; dp->aux->state |= DP_STATE_CTRL_POWERED_ON; +#ifdef CONFIG_SEC_DISPLAYPORT + atomic_set(&dp->notification_status, 0); + complete_all(&dp->dp_on_comp); +#endif complete_all(&dp->notification_comp); mutex_unlock(&dp->session_lock); return 0; @@ -1193,10 +2367,29 @@ static int dp_display_pre_disable(struct dp_display *dp_display) goto end; } +#ifdef CONFIG_SEC_DISPLAYPORT + if (dp->dp_display.is_connected) { + /* send audio disconnect event to audio framework if DP is still connected. + * it can happen in case of platform reset with DP connection. + */ + pr_info("audio is still on DP side -> send audio disconnect event!\n"); + if (dp->audio_supported) + dp->audio->off(dp->audio); + } + + cancel_delayed_work_sync(&dp->sec.hdcp_start_work); + cancel_delayed_work(&dp->sec.link_status_work); +#endif if (dp_display_is_hdcp_enabled(dp)) { dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE; +#ifndef CONFIG_SEC_DISPLAYPORT cancel_delayed_work_sync(&dp->hdcp_cb_work); +#else + pr_info("cable <%d>\n", secdp_get_cable_status()); + cancel_delayed_work(&dp->hdcp_cb_work); + usleep_range(3000, 5000); +#endif if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); } @@ -1263,7 +2456,12 @@ static int dp_display_disable(struct dp_display *dp_display) dp->aux->state = DP_STATE_CTRL_POWERED_OFF; end: complete_all(&dp->notification_comp); +#ifdef CONFIG_SEC_DISPLAYPORT + atomic_set(&dp->notification_status, 0); + complete(&dp->dp_off_comp); +#endif mutex_unlock(&dp->session_lock); + return 0; } @@ -1501,6 +2699,17 @@ static int dp_display_probe(struct platform_device *pdev) } init_completion(&dp->notification_comp); +#ifdef CONFIG_SEC_DISPLAYPORT + init_completion(&dp->dp_off_comp); + init_completion(&dp->dp_on_comp); + rc = switch_dev_register(&switch_secdp); + if (rc) + pr_info("Failed to register secdp switch(%d)\n", rc); + + rc = switch_dev_register(&switch_secdp_msg); + if (rc) + pr_info("Failed to register secdp_msg switch(%d)\n", rc); +#endif dp->pdev = pdev; dp->name = "drm_dp"; @@ -1513,6 +2722,12 @@ static int dp_display_probe(struct platform_device *pdev) goto error; } +#ifdef CONFIG_SEC_DISPLAYPORT + g_secdp_priv = dp; + secdp_logger_init(); + atomic_set(&dp->notification_status, 0); +#endif + platform_set_drvdata(pdev, dp); g_dp_display = &dp->dp_display; @@ -1586,6 +2801,10 @@ static int dp_display_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); devm_kfree(&pdev->dev, dp); +#ifdef CONFIG_SEC_DISPLAYPORT + switch_dev_unregister(&switch_secdp); + switch_dev_unregister(&switch_secdp_msg); +#endif return 0; } diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 442179f15409..fc7bedeaeeeb 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -23,6 +23,9 @@ #include "sde_connector.h" #include "dp_drm.h" #include "dp_debug.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#endif #define to_dp_bridge(x) container_of((x), struct dp_bridge, base) @@ -438,6 +441,9 @@ int dp_connector_get_info(struct msm_display_info *info, void *data) return -EINVAL; } +#ifdef CONFIG_SEC_DISPLAYPORT + memset(info, 0, sizeof(struct msm_display_info)); +#endif info->intf_type = DRM_MODE_CONNECTOR_DisplayPort; info->num_of_h_tiles = 1; @@ -709,6 +715,281 @@ void dp_drm_bridge_deinit(void *data) kfree(bridge); } +#ifdef CONFIG_SEC_DISPLAYPORT +/* Index of max resolution which supported by sink */ +static int g_max_res_index; + +/* Index of max resolution which supported by dex station */ +static int g_dex_max_res_index; +static int g_ignore_ratio = 0; + +void secdp_dex_res_init(void) +{ + g_max_res_index = g_dex_max_res_index = -1; + g_ignore_ratio = 0; +} + +static struct secdp_display_timing secdp_supported_resolution[] = { + { 0, 640, 480, 60, false, DEX_RES_1920X1080}, + { 1, 720, 480, 60, false, DEX_RES_1920X1080}, + { 2, 720, 576, 50, false, DEX_RES_1920X1080}, + + { 3, 1280, 720, 50, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + { 4, 1280, 720, 60, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + + { 5, 1280, 768, 60, false, DEX_RES_1920X1080}, /* CTS 4.4.1.3 */ + { 6, 1280, 800, 60, false, DEX_RES_1920X1080, MON_RATIO_16_10}, /* CTS 18bpp */ + { 7, 1280, 1024, 60, false, DEX_RES_1920X1080}, /* CTS 18bpp */ + { 8, 1360, 768, 60, false, DEX_RES_1920X1080, MON_RATIO_16_9}, /* CTS 4.4.1.3 */ + + { 9, 1366, 768, 60, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + {10, 1600, 900, 60, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + + {20, 1920, 1080, 24, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + {21, 1920, 1080, 25, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + {22, 1920, 1080, 30, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + {23, 1920, 1080, 50, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + {24, 1920, 1080, 60, false, DEX_RES_1920X1080, MON_RATIO_16_9}, + + {25, 1920, 1200, 60, false, DEX_RES_1920X1200, MON_RATIO_16_10}, + + {30, 1920, 1440, 60, false, DEX_RES_NOT_SUPPORT}, /* CTS 400.3.3.1 */ + {40, 2048, 1536, 60, false, DEX_RES_NOT_SUPPORT}, /* CTS 18bpp */ + +#ifdef SECDP_WIDE_21_9_SUPPORT + {60, 2560, 1080, 60, false, DEX_RES_2560X1080, MON_RATIO_21_9}, +#endif + + {61, 2560, 1440, 60, false, DEX_RES_2560X1440, MON_RATIO_16_9}, + {62, 2560, 1600, 60, false, DEX_RES_2560X1600, MON_RATIO_16_10}, + + {70, 1440, 2560, 60, false, DEX_RES_NOT_SUPPORT}, /* TVR test */ + {71, 1440, 2560, 75, false, DEX_RES_NOT_SUPPORT}, /* TVR test */ + +#ifdef SECDP_WIDE_21_9_SUPPORT + {80, 3440, 1440, 50, false, DEX_RES_3440X1440, MON_RATIO_21_9}, + {81, 3440, 1440, 60, false, DEX_RES_3440X1440, MON_RATIO_21_9}, +#ifdef SECDP_HIGH_REFRESH_SUPPORT + {82, 3440, 1440, 100, false, DEX_RES_NOT_SUPPORT, MON_RATIO_21_9}, +#endif +#endif + +#ifdef SECDP_WIDE_32_9_SUPPORT + {100, 3840, 1080, 60, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_9}, +#ifdef SECDP_HIGH_REFRESH_SUPPORT + {101, 3840, 1080, 100, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_9}, + {102, 3840, 1080, 120, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_9}, + {104, 3840, 1080, 144, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_9}, +#endif +#endif + +#ifdef SECDP_WIDE_32_10_SUPPORT + {110, 3840, 1200, 60, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_10}, +#ifdef SECDP_HIGH_REFRESH_SUPPORT + {111, 3840, 1200, 100, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_10}, + {112, 3840, 1200, 120, false, DEX_RES_NOT_SUPPORT, MON_RATIO_32_10}, +#endif +#endif + + {150, 3840, 2160, 24, false, DEX_RES_NOT_SUPPORT, MON_RATIO_16_9}, + {151, 3840, 2160, 25, false, DEX_RES_NOT_SUPPORT, MON_RATIO_16_9}, + {152, 3840, 2160, 30, false, DEX_RES_NOT_SUPPORT, MON_RATIO_16_9}, + {153, 3840, 2160, 50, false, DEX_RES_NOT_SUPPORT, MON_RATIO_16_9}, + {154, 3840, 2160, 60, false, DEX_RES_NOT_SUPPORT, MON_RATIO_16_9}, + + {200, 4096, 2160, 24, false, DEX_RES_NOT_SUPPORT}, + {201, 4096, 2160, 25, false, DEX_RES_NOT_SUPPORT}, + {202, 4096, 2160, 30, false, DEX_RES_NOT_SUPPORT}, + {203, 4096, 2160, 50, false, DEX_RES_NOT_SUPPORT}, + {204, 4096, 2160, 60, false, DEX_RES_NOT_SUPPORT}, +}; + +bool secdp_check_dex_reconnect(void) +{ + pr_info("%d, %d\n", g_max_res_index, g_dex_max_res_index); + if (g_max_res_index == g_dex_max_res_index) + return false; + + return true; +} + +static inline char *secdp_aspect_ratio_to_string(enum mon_aspect_ratio_t ratio) +{ + switch (ratio) { + 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_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"; + } +} + +static enum mon_aspect_ratio_t secdp_get_aspect_ratio(struct drm_display_mode *mode) +{ + enum mon_aspect_ratio_t aspect_ratio = MON_RATIO_NA; + int hdisplay = mode->hdisplay; + int vdisplay = mode->vdisplay; + + if ((hdisplay == 3840 && vdisplay == 2160) || + (hdisplay == 2560 && vdisplay == 1440) || + (hdisplay == 1920 && vdisplay == 1080) || + (hdisplay == 1600 && vdisplay == 900) || + (hdisplay == 1366 && vdisplay == 768) || + (hdisplay == 1280 && vdisplay == 720)) + aspect_ratio = MON_RATIO_16_9; + else if ((hdisplay == 2560 && vdisplay == 1600) || + (hdisplay == 1920 && vdisplay == 1200) || + (hdisplay == 1680 && vdisplay == 1050) || + (hdisplay == 1440 && vdisplay == 900) || + (hdisplay == 1280 && vdisplay == 800)) + aspect_ratio = MON_RATIO_16_10; + else if ((hdisplay == 3440 && vdisplay == 1440) || + (hdisplay == 2560 && vdisplay == 1080)) + aspect_ratio = MON_RATIO_21_9; + else if (hdisplay == 3840 && vdisplay == 1080) + aspect_ratio = MON_RATIO_32_9; + else if (hdisplay == 3840 && vdisplay == 1200) + aspect_ratio = MON_RATIO_32_10; + + return aspect_ratio; +} + +bool secdp_find_supported_resolution(struct dp_panel_info *timing) +{ + struct secdp_display_timing *secdp_timing = secdp_supported_resolution; + int i, res_cnt = ARRAY_SIZE(secdp_supported_resolution); + u32 h_active, v_active, refresh_rate; + bool support = false; + + h_active = timing->h_active; + v_active = timing->v_active; + refresh_rate = timing->refresh_rate; + + for (i = 0; i < res_cnt; i++) { + if (h_active == secdp_timing[i].active_h && + v_active == secdp_timing[i].active_v && + refresh_rate == secdp_timing[i].refresh_rate) { + support = true; + break; + } + } + + return support; +} + +static bool secdp_check_supported_resolution(struct drm_display_mode *mode, struct dp_display *dp_disp) +{ + int i, fps_diff; + int res_cnt = ARRAY_SIZE(secdp_supported_resolution); + bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + static enum mon_aspect_ratio_t prefer_ratio; + + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + prefer_ratio = secdp_get_aspect_ratio(mode); + pr_info("preferred_timing! %dx%d@%dhz, %s\n", + mode->hdisplay, mode->vdisplay, mode->vrefresh, + secdp_aspect_ratio_to_string(prefer_ratio)); + pr_info("max resolution - mirror : %d, dex : %d\n", g_max_res_index, g_dex_max_res_index); + if (g_max_res_index >= 0) { + if (g_dex_max_res_index < 10) /* less than 1600 x 900 */ + g_ignore_ratio = 1; +#ifndef SECDP_USE_PREFERRED + mode->type = mode->type & (unsigned int)(~DRM_MODE_TYPE_PREFERRED); +#endif + } + } + + if (mode->clock > dp_disp->max_pclk_khz) + return false; + if (dp_disp->validate_mode(dp_disp, mode->clock, mode->flags) == MODE_BAD) + return false; + +#if !defined (CONFIG_SEC_FACTORY) && defined (SECDP_MAX_RESOLUTION_4K30) + /*When there is QHD 60hz DP resolution, max DP resolution 4K@30hz change to QHD 60hz.*/ + if (mode->hdisplay == 2560 && mode->vrefresh == 60) { + for (i = 0; i < res_cnt; i++) { + if (secdp_supported_resolution[i].active_h == mode->hdisplay && + secdp_supported_resolution[i].active_v == mode->vdisplay && + secdp_supported_resolution[i].refresh_rate == mode->vrefresh) + break; + } + + if ((mode->vdisplay == 1600) || (mode->vdisplay == 1440 && g_max_res_index != i+1)) { + if (g_max_res_index < secdp_supported_resolution[i].index) + g_max_res_index = secdp_supported_resolution[i].index; + } + } + if (mode->hdisplay == 3840 && mode->vrefresh < 60) { + for (i = 0; i < res_cnt; i++) { + if (secdp_supported_resolution[i].active_h == 2560 && + secdp_supported_resolution[i].active_v == 1440 && + secdp_supported_resolution[i].refresh_rate == 60) + break; + } + + if (g_max_res_index == secdp_supported_resolution[i].index || g_max_res_index == secdp_supported_resolution[i+1].index) + return false; + } +#endif + + for (i = 0; i < res_cnt; i++) { + bool ret = false; + fps_diff = secdp_supported_resolution[i].refresh_rate - drm_mode_vrefresh(mode); + fps_diff = fps_diff < 0 ? fps_diff * (-1) : fps_diff; + + if (fps_diff > 1) + continue; + + if (secdp_supported_resolution[i].interlaced != interlaced) + continue; + + if (secdp_supported_resolution[i].active_h != mode->hdisplay) + continue; + + if (secdp_supported_resolution[i].active_v != mode->vdisplay) + continue; + + /* find max resolution which supported by sink */ + if (g_max_res_index < secdp_supported_resolution[i].index) + g_max_res_index = secdp_supported_resolution[i].index; + + if (secdp_supported_resolution[i].dex_res != DEX_RES_NOT_SUPPORT && + secdp_supported_resolution[i].dex_res <= secdp_get_dex_res()) { + +#if 0/*TEST*/ + if (secdp_supported_resolution[i].dex_res == DEX_RES_3440X1440) { + pr_debug("[TEST] RETURN FALSE 3440x1440!!\n"); + ret = false; + } else if (secdp_supported_resolution[i].dex_res == DEX_RES_2560X1080) { + pr_debug("[TEST] RETURN FALSE 2560x1080!!\n"); + ret = false; + } else +#endif + if (g_ignore_ratio) + ret = true; + else if (secdp_supported_resolution[i].mon_ratio == prefer_ratio) + ret = true; + else + ret = false; + } else + ret = false; + + /* find max resolution which supported by dex station */ + if (ret && g_dex_max_res_index < secdp_supported_resolution[i].index) + g_dex_max_res_index = secdp_supported_resolution[i].index; + + if (secdp_check_dex_mode()) + return ret; + + return true; + } + + return false; +} +#endif + enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode, void *display) @@ -717,6 +998,9 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, struct dp_debug *debug; u32 pclk = 0; u32 rate_ratio = RGB_24BPP_TMDS_CHAR_RATE_RATIO; +#ifdef CONFIG_SEC_DISPLAYPORT + enum drm_mode_status ret = MODE_OK; +#endif if (!mode || !display) { pr_err("invalid params\n"); @@ -736,8 +1020,10 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, if (!dp_disp->vsc_sdp_supported(dp_disp)) mode->flags &= ~DRM_MODE_FLAG_SUPPORTS_YUV420; - if (!(mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK)) + if (!(mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK)) { + pr_err("unknown mode flag\n"); return MODE_BAD; + } if ((mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK) == DRM_MODE_FLAG_SUPPORTS_YUV420) { @@ -755,6 +1041,7 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, pclk = mode->clock / rate_ratio; +#ifndef CONFIG_SEC_DISPLAYPORT if (pclk > dp_disp->max_pclk_khz) return MODE_BAD; @@ -765,4 +1052,30 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; return dp_disp->validate_mode(dp_disp, pclk, mode->flags); +#else + if (pclk > dp_disp->max_pclk_khz) { + pr_err("pclk error\n"); + ret = MODE_BAD; + goto end; + } + + if (debug->debug_en && (mode->hdisplay != debug->hdisplay || + mode->vdisplay != debug->vdisplay || + mode->vrefresh != debug->vrefresh || + mode->picture_aspect_ratio != debug->aspect_ratio)) { + ret = MODE_BAD; + goto end; + } + + if (ret == MODE_OK && !secdp_check_supported_resolution(mode, dp_disp)) { + ret = MODE_BAD; + goto end; + } + +end: + pr_info("%s@%dhz | %s | max_pclk: %d | cur_pclk: %d\n", mode->name, + drm_mode_vrefresh(mode), ret == MODE_BAD ? "NG" : "OK", + dp_disp->max_pclk_khz, mode->clock); + return ret; +#endif } diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c index f277ac0f36f5..75361b406f32 100644 --- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c +++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c @@ -24,6 +24,10 @@ #include "sde_hdcp.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#endif + #define DP_INTR_STATUS2 (0x00000024) #define DP_INTR_STATUS3 (0x00000028) @@ -325,6 +329,8 @@ static void dp_hdcp2p2_min_level_change(void *client_ctx, struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE}; + pr_debug("+++, min_enc_level(%d)\n", min_enc_level); + if (!ctrl) { pr_err("invalid input\n"); return; @@ -897,6 +903,9 @@ static bool dp_hdcp2p2_supported(struct dp_hdcp2p2_ctrl *ctrl) u32 const rxcaps_dpcd_offset = 0x6921d; ssize_t bytes_read = 0; u8 buf[DP_HDCP_RXCAPS_LENGTH]; +#ifdef CONFIG_SEC_DISPLAYPORT + u32 i; +#endif bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux, rxcaps_dpcd_offset, &buf, DP_HDCP_RXCAPS_LENGTH); @@ -905,6 +914,11 @@ static bool dp_hdcp2p2_supported(struct dp_hdcp2p2_ctrl *ctrl) goto error; } +#ifdef CONFIG_SEC_DISPLAYPORT + for (i = 0; i < DP_HDCP_RXCAPS_LENGTH; i++) + pr_debug("rxcaps[%d] 0x%x\n", i, buf[i]); +#endif + pr_debug("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1); pr_debug("VERSION=%d\n", buf[0]); diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c index 410e0754ebf5..b59d027d6472 100644 --- a/drivers/gpu/drm/msm/dp/dp_link.c +++ b/drivers/gpu/drm/msm/dp/dp_link.c @@ -16,6 +16,12 @@ #include "dp_link.h" #include "dp_panel.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif +#endif enum dynamic_range { DP_DYNAMIC_RANGE_RGB_VESA = 0x00, @@ -897,6 +903,15 @@ static void dp_link_parse_sink_status_field(struct dp_link_private *link) link->link_status); if (len < DP_LINK_STATUS_SIZE) pr_err("DP link status read failed\n"); +#ifdef CONFIG_SEC_DISPLAYPORT + else { + int i; + pr_cont("[drm-dp] %s: ", __func__); + for (i = 0; i < DP_LINK_STATUS_SIZE; i++) + pr_cont("0x%x: 0x%02x ", DP_LANE0_1_STATUS + i, link->link_status[i]); + pr_cont("\n"); + } +#endif dp_link_parse_request(link); } @@ -959,6 +974,8 @@ static int dp_link_psm_config(struct dp_link *dp_link, return -EINVAL; } + pr_debug("+++, enable(%d)\n", enable); + link = container_of(dp_link, struct dp_link_private, dp_link); if (enable) @@ -1137,7 +1154,7 @@ static int dp_link_process_link_status_update(struct dp_link_private *link) return -EINVAL; pr_debug("channel_eq_done = %d, clock_recovery_done = %d\n", - drm_dp_clock_recovery_ok(link->link_status, + drm_dp_channel_eq_ok(link->link_status, link->dp_link.link_params.lane_count), drm_dp_clock_recovery_ok(link->link_status, link->dp_link.link_params.lane_count)); @@ -1266,6 +1283,90 @@ static void dp_link_reset_data(struct dp_link_private *link) link->dp_link.test_response = 0; } +#ifdef CONFIG_SEC_DISPLAYPORT +void secdp_clear_link_status_update_cnt(struct dp_link *dp_link) +{ + dp_link->poor_connection = false; + dp_link->status_update_cnt = 0; +} + +void secdp_reset_link_status(struct dp_link *dp_link) +{ + struct dp_link_private *link; + + if (!dp_link) { + pr_err("invalid input\n"); + goto exit; + } + + link = container_of(dp_link, struct dp_link_private, dp_link); + if (!link) { + pr_err("link is null\n"); + goto exit; + } + + if (!(get_link_status(link->link_status, DP_SINK_STATUS) & DP_RECEIVE_PORT_0_STATUS)) { + pr_err("[205h] port0: out of sync, reset!\n"); + link->link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS] |= DP_RECEIVE_PORT_0_STATUS; + } + + if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) & DP_INTERLANE_ALIGN_DONE)) { + pr_err("[204h] interlane_align_done is zero, reset!\n"); + link->link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] |= DP_INTERLANE_ALIGN_DONE; + } + +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) { + pr_err("invalid input\n"); + goto exit; + } + + link = container_of(dp_link, struct dp_link_private, dp_link); + + if (!link) { + pr_err("link is null\n"); + goto exit; + } + + if (!(get_link_status(link->link_status, DP_SINK_STATUS) & DP_RECEIVE_PORT_0_STATUS)) { + pr_err("[205h] port0: out of sync\n"); + goto exit; + } +/* + if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) & DP_LINK_STATUS_UPDATED)) { + pr_err("[204h] link_status_updated is zero!\n"); + goto exit; + } +*/ + if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) & DP_INTERLANE_ALIGN_DONE)) { + pr_err("[204h] interlane_align_done is zero!\n"); + goto exit; + } + + stable = true; +exit: + pr_debug("connection is <%s>\n", stable ? "stable" : "unstable"); + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + if (!stable) + secdp_bigdata_inc_error_cnt(ERR_INF_IRQHPD); +#endif + return stable; +} +#endif + /** * dp_link_process_request() - handle HPD IRQ transition to HIGH * @link: pointer to link module data @@ -1292,6 +1393,12 @@ static int dp_link_process_request(struct dp_link *dp_link) dp_link_parse_sink_status_field(link); +#ifdef CONFIG_SEC_DISPLAYPORT + if (secdp_get_power_status() && !secdp_check_link_stable(dp_link)) { + dp_link->status_update_cnt++; + pr_info("status_update_cnt %d\n", dp_link->status_update_cnt); + } +#endif if (dp_link_is_test_edid_read(link)) { dp_link->sink_request |= DP_TEST_LINK_EDID_READ; goto exit; diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h index d14b88155444..1fc7cbf34384 100644 --- a/drivers/gpu/drm/msm/dp/dp_link.h +++ b/drivers/gpu/drm/msm/dp/dp_link.h @@ -110,6 +110,11 @@ struct dp_link { u32 sink_request; u32 test_response; +#ifdef CONFIG_SEC_DISPLAYPORT + 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; diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 43f011de5113..0ae9cf2fadc7 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -17,6 +17,12 @@ #include "msm_kms.h" #include "dp_panel.h" #include +#ifdef CONFIG_SEC_DISPLAYPORT +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif +#include "secdp.h" +#endif #define DP_PANEL_DEFAULT_BPP 24 #define DP_MAX_DS_PORT_COUNT 1 @@ -80,6 +86,9 @@ struct dp_panel_private { struct dp_panel dp_panel; struct dp_aux *aux; struct dp_link *link; +#ifdef CONFIG_SEC_DISPLAYPORT + struct dp_parser *parser; +#endif struct dp_catalog_panel *catalog; bool custom_edid; bool custom_dpcd; @@ -111,12 +120,45 @@ static const struct dp_panel_info fail_safe = { .bpp = 24, }; +#ifdef CONFIG_SEC_DISPLAYPORT +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 + static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func) { @@ -204,6 +246,12 @@ skip_dpcd_read: link_info->rate = drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE]); pr_debug("link_rate=%d\n", link_info->rate); +#ifdef CONFIG_SEC_DISPLAYPORT + if (link_info->rate > 540000) { /*DP_LINK_BW_5_4*/ + pr_debug("set it to 540000!\n"); + link_info->rate = 540000; + } +#endif link_info->num_lanes = dp_panel->dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; @@ -236,6 +284,19 @@ skip_dpcd_read: pr_debug("DS port count %d greater that max (%d) supported\n", dfp_count, DP_MAX_DS_PORT_COUNT); +#ifdef CONFIG_SEC_DISPLAYPORT + dp_panel->dsp_type = (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) >> 1; + pr_info("dsp_type: <%s>\n", mdss_dp_dsp_type_to_string(dp_panel->dsp_type)); +#ifdef CONFIG_SEC_DISPLAYPORT_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; } @@ -323,6 +384,78 @@ static int dp_panel_set_dpcd(struct dp_panel *dp_panel, u8 *dpcd) return 0; } +#ifdef CONFIG_SEC_DISPLAYPORT +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); + +/** stores max timing's pclk and bpp values + * 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 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; + + mutex_lock(&dev->mode_config.mutex); + + pinfo = &dp_panel->max_timing_info; + memset(pinfo, 0, sizeof(*pinfo)); + memset(&dp_mode, 0, sizeof(dp_mode)); + + rc = dp_panel_get_modes(dp_panel, conn, &dp_mode); + if (!rc) { + pr_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) { +#ifndef SECDP_HIGH_REFRESH_SUPPORT + if (timing->refresh_rate > 60) { + pr_info("skip %ux%u@%uhz, too high refresh rate!\n", + timing->h_active, timing->v_active, + timing->refresh_rate); + continue; + } +#endif + 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; + pr_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(dev, mode); + } +end: + mutex_unlock(&dev->mode_config.mutex); + return; +} +#endif + static int dp_panel_read_edid(struct dp_panel *dp_panel, struct drm_connector *connector) { @@ -337,6 +470,10 @@ static int dp_panel_read_edid(struct dp_panel *dp_panel, panel = container_of(dp_panel, struct dp_panel_private, dp_panel); +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_dex_res_init(); +#endif + if (panel->custom_edid) { pr_debug("skip edid read in debug mode\n"); goto end; @@ -349,6 +486,9 @@ static int dp_panel_read_edid(struct dp_panel *dp_panel, ret = -EINVAL; goto end; } +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_get_max_timing(dp_panel); +#endif end: return ret; } @@ -368,18 +508,35 @@ static int dp_panel_read_sink_caps(struct dp_panel *dp_panel, panel = container_of(dp_panel, struct dp_panel_private, dp_panel); +#ifdef CONFIG_SEC_DISPLAYPORT + 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)) { + +#ifndef CONFIG_SEC_DISPLAYPORT if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) { pr_err("DPCD read failed, return early\n"); goto end; } +#else + if (!secdp_get_hpd_status() || !secdp_get_cable_status()) { + pr_info("hpd_low or cable_lost\n"); + return -EIO; + } +#endif pr_err("panel dpcd read failed/incorrect, set default params\n"); dp_panel_set_default_link_params(dp_panel); + +#ifdef CONFIG_SEC_DISPLAYPORT + if (rc < 0) + return -EIO; +#endif } downstream_ports = dp_panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] & @@ -401,9 +558,23 @@ static int dp_panel_read_sink_caps(struct dp_panel *dp_panel, rc = dp_panel_read_edid(dp_panel, connector); if (rc) { pr_err("panel edid read failed, set failsafe mode\n"); +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_inc_error_cnt(ERR_EDID); +#endif return rc; } + end: +#ifdef CONFIG_SEC_DISPLAYPORT + pr_info("dpcd_rev: 0x%02x\n", dp_panel->dpcd[DP_DPCD_REV]); + pr_info("vendor_id: <%s>\n", dp_panel->edid_ctrl->vendor_id); + drm_edid_get_monitor_name(dp_panel->edid_ctrl->edid, dp_panel->monitor_name, 14); + pr_info("monitor_name: <%s>\n", dp_panel->monitor_name); +#ifdef CONFIG_SEC_DISPLAYPORT_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 +#endif return rc; } @@ -414,7 +585,15 @@ static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel, const u32 max_supported_bpp = 30, min_supported_bpp = 18; u32 bpp = 0, data_rate_khz = 0; +#ifndef CONFIG_SEC_DISPLAYPORT 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_info = &dp_panel->link_info; data_rate_khz = link_info->num_lanes * link_info->rate * 8; @@ -448,6 +627,10 @@ static u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel, bpp = dp_panel_get_supported_bpp(dp_panel, mode_edid_bpp, mode_pclk_khz); +#ifdef CONFIG_SEC_DISPLAYPORT + pr_debug("video_test(%d), bpp(%d)\n", dp_panel->video_test, bpp); +#endif + return bpp; } @@ -764,6 +947,9 @@ static int dp_panel_init_panel_info(struct dp_panel *dp_panel) { int rc = 0; struct dp_panel_info *pinfo; +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + char buf[20] = {0, }; +#endif if (!dp_panel) { pr_err("invalid input\n"); @@ -777,9 +963,13 @@ static int dp_panel_init_panel_info(struct dp_panel *dp_panel) * print resolution info as this is a result * of user initiated action of cable connection */ - pr_info("SET NEW RESOLUTION:\n"); - pr_info("%dx%d@%dfps\n", pinfo->h_active, + pr_info("SET NEW RESOLUTION: %dx%d@%dfps\n", pinfo->h_active, pinfo->v_active, pinfo->refresh_rate); +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + scnprintf(buf, 20, "%dx%d@%d", + pinfo->h_active, pinfo->v_active, pinfo->refresh_rate); + secdp_bigdata_save_item(BD_RESOLUTION, buf); +#endif pr_info("h_porches(back|front|width) = (%d|%d|%d)\n", pinfo->h_back_porch, pinfo->h_front_porch, @@ -834,7 +1024,11 @@ static u32 dp_panel_get_min_req_link_rate(struct dp_panel *dp_panel) } lane_cnt = dp_panel->link_info.num_lanes; +#ifndef CONFIG_SEC_DISPLAYPORT pinfo = &dp_panel->pinfo; +#else + pinfo = &dp_panel->max_timing_info; +#endif /* num_lanes * lane_count * 8 >= pclk * bpp * 10 */ min_link_rate_khz = pinfo->pixel_clk_khz / @@ -983,6 +1177,49 @@ end: return rc; } +#ifdef CONFIG_SEC_DISPLAYPORT +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) +{ + const u32 num_components = 3, default_bpp = 24; + + dp_mode->timing.h_active = drm_mode->hdisplay; + dp_mode->timing.h_back_porch = drm_mode->htotal - drm_mode->hsync_end; + dp_mode->timing.h_sync_width = drm_mode->htotal - + (drm_mode->hsync_start + dp_mode->timing.h_back_porch); + dp_mode->timing.h_front_porch = drm_mode->hsync_start - + drm_mode->hdisplay; + dp_mode->timing.h_skew = drm_mode->hskew; + + dp_mode->timing.v_active = drm_mode->vdisplay; + dp_mode->timing.v_back_porch = drm_mode->vtotal - drm_mode->vsync_end; + dp_mode->timing.v_sync_width = drm_mode->vtotal - + (drm_mode->vsync_start + dp_mode->timing.v_back_porch); + + dp_mode->timing.v_front_porch = drm_mode->vsync_start - + drm_mode->vdisplay; + + dp_mode->timing.refresh_rate = drm_mode->vrefresh; + + dp_mode->timing.pixel_clk_khz = drm_mode->clock; + + dp_mode->timing.v_active_low = + !!(drm_mode->flags & DRM_MODE_FLAG_NVSYNC); + + dp_mode->timing.h_active_low = + !!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC); + + dp_mode->timing.bpp = + dp_panel->connector->display_info.bpc * num_components; + if (!dp_mode->timing.bpp) + dp_mode->timing.bpp = default_bpp; + + dp_mode->timing.bpp = dp_panel_get_mode_bpp(dp_panel, + dp_mode->timing.bpp, dp_mode->timing.pixel_clk_khz); +} +#endif + struct dp_panel *dp_panel_get(struct dp_panel_in *in) { int rc = 0; @@ -1005,12 +1242,20 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) panel->aux = in->aux; panel->catalog = in->catalog; panel->link = in->link; + panel->parser = in->parser; dp_panel = &panel->dp_panel; +#ifndef CONFIG_SEC_DISPLAYPORT 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; memcpy(panel->spd_vendor_name, vendor_name, (sizeof(u8) * 8)); memcpy(panel->spd_product_description, product_desc, (sizeof(u8) * 16)); +#ifdef CONFIG_SEC_DISPLAYPORT + dp_panel->connector = in->connector; +#endif dp_panel->init = dp_panel_init_panel_info; dp_panel->deinit = dp_panel_deinit_panel_info; diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index 2c4622b4fd8b..ed1e9217d68b 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -61,6 +61,10 @@ struct dp_panel_in { struct dp_aux *aux; struct dp_link *link; struct dp_catalog_panel *catalog; +#ifdef CONFIG_SEC_DISPLAYPORT + struct drm_connector *connector; + struct dp_parser *parser; +#endif }; struct dp_panel { @@ -80,6 +84,15 @@ struct dp_panel { /* debug */ u32 max_bw_code; +#ifdef CONFIG_SEC_DISPLAYPORT + u8 monitor_name[14]; + u32 dsp_type; + struct dp_panel_info max_timing_info; + + /* DRM connector assosiated with this panel */ + struct drm_connector *connector; +#endif + int (*init)(struct dp_panel *dp_panel); int (*deinit)(struct dp_panel *dp_panel); int (*timing_cfg)(struct dp_panel *dp_panel); diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index 09fa4fd54b4f..d4176cb37473 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -18,6 +18,9 @@ #include #include "dp_parser.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#endif static void dp_parser_unmap_io_resources(struct dp_parser *parser) { @@ -258,6 +261,13 @@ static int dp_parser_gpio(struct dp_parser *parser) mp->gpio_config[i].value = 0; } +#ifdef CONFIG_SEC_DISPLAYPORT + for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) { + pr_info("name(%s) gpio(%u) value(%u)\n", mp->gpio_config[i].gpio_name, + mp->gpio_config[i].gpio, mp->gpio_config[i].value); + } +#endif + return 0; } @@ -284,6 +294,11 @@ static int dp_parser_get_vreg(struct dp_parser *parser, mp->num_vreg = 0; pm_supply_name = dp_parser_supply_node_name(module); + +#ifdef CONFIG_SEC_DISPLAYPORT + pr_debug("pm_supply_name: %s\n", pm_supply_name); +#endif + supply_root_node = of_get_child_by_name(of_node, pm_supply_name); if (!supply_root_node) { pr_err("no supply entry present: %s\n", pm_supply_name); @@ -396,6 +411,24 @@ static void dp_parser_put_vreg_data(struct device *dev, mp->num_vreg = 0; } +#ifdef CONFIG_SEC_DISPLAYPORT +struct regulator *aux_pullup_vreg; + +static struct regulator *secdp_get_aux_pullup_vreg(struct device *dev) +{ + struct regulator *vreg = NULL; + + vreg = devm_regulator_get(dev, "aux-pullup"); + if (IS_ERR(vreg)) { + pr_err("unable to get aux_pullup vdd supply\n"); + return NULL; + } + + pr_info("get aux_pullup vdd success\n"); + return vreg; +} +#endif + static int dp_parser_regulator(struct dp_parser *parser) { int i, rc = 0; @@ -415,6 +448,10 @@ static int dp_parser_regulator(struct dp_parser *parser) } } +#ifdef CONFIG_SEC_DISPLAYPORT + aux_pullup_vreg = secdp_get_aux_pullup_vreg(&pdev->dev); +#endif + return rc; } @@ -585,6 +622,31 @@ exit: return rc; } +#ifdef CONFIG_SEC_DISPLAYPORT +static void secdp_parse_misc(struct dp_parser *parser) +{ + struct device *dev = &parser->pdev->dev; + int rc = 0; + + rc = of_property_read_u32(dev->of_node, "secdp,rev_hw", &parser->rev_hw); + pr_debug("secdp,rev_hw: %d, rc: %d\n", parser->rev_hw, rc); + + parser->aux_sw = of_property_read_bool(dev->of_node, + "secdp,aux-sw"); + pr_debug("secdp,aux-sw: %d\n", parser->aux_sw); + + parser->aux_redrv = of_property_read_bool(dev->of_node, + "secdp,aux-redrv"); + pr_debug("secdp,aux-redrv: %d\n", parser->aux_redrv); + + parser->prefer_res = of_property_read_bool(dev->of_node, + "secdp,prefer-res"); + pr_debug("secdp,prefer-res: %d\n", parser->prefer_res); + + return; +} +#endif + static int dp_parser_parse(struct dp_parser *parser) { int rc = 0; @@ -624,6 +686,12 @@ static int dp_parser_parse(struct dp_parser *parser) goto err; rc = dp_parser_msm_hdcp_dev(parser); + if (rc) + goto err; + +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_parse_misc(parser); +#endif err: return rc; } diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h index a32a8ec302db..4107c5dc74c8 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.h +++ b/drivers/gpu/drm/msm/dp/dp_parser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, 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 @@ -181,6 +181,12 @@ struct dp_parser { bool yuv_support; const char *display_type; +#ifdef CONFIG_SEC_DISPLAYPORT + u32 rev_hw; + bool aux_sw; /* true if AUX switch IC is located */ + bool aux_redrv; /* true if Redriver IC is located */ + bool prefer_res; /* true if prefer resolution has high priority */ +#endif int (*parse)(struct dp_parser *parser); struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name); diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c index d793365f3c36..8a1a91b35cba 100644 --- a/drivers/gpu/drm/msm/dp/dp_power.c +++ b/drivers/gpu/drm/msm/dp/dp_power.c @@ -16,6 +16,14 @@ #include #include "dp_power.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include +#include +#include "secdp.h" +#ifdef CONFIG_COMBO_REDRIVER +#include +#endif +#endif #define DP_CLIENT_NAME_SIZE 20 @@ -31,8 +39,15 @@ struct dp_power_private { bool core_clks_on; bool link_clks_on; +#ifdef CONFIG_SEC_DISPLAYPORT + bool aux_pullup_on; +#endif }; +#ifdef CONFIG_SEC_DISPLAYPORT +struct dp_power_private *g_secdp_power; +#endif + static int dp_power_regulator_init(struct dp_power_private *power) { int rc = 0, i = 0, j = 0; @@ -81,6 +96,83 @@ static void dp_power_regulator_deinit(struct dp_power_private *power) } } +#ifdef CONFIG_SEC_DISPLAYPORT +extern struct regulator *aux_pullup_vreg; + +/* factory use only + * ref: qusb_phy_enable_power() + */ +static int secdp_aux_pullup_vreg_enable(bool on) +{ + int rc = 0; + struct dp_power_private *power = g_secdp_power; + struct regulator *aux_pu_vreg = aux_pullup_vreg; + + pr_debug("+++, on(%d)\n", on); + + if (!aux_pu_vreg) { + pr_err("vdda33 is null!\n"); + goto exit; + } + +#define QUSB2PHY_3P3_VOL_MIN 3075000 /* uV */ +#define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */ +#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */ + + if (on) { + if (power->aux_pullup_on) { + pr_info("already on\n"); + goto exit; + } + + rc = regulator_set_load(aux_pu_vreg, QUSB2PHY_3P3_HPM_LOAD); + if (rc < 0) { + pr_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) { + pr_err("Unable to set voltage for vdda33: %d\n", rc); + goto put_vdda33_lpm; + } + + rc = regulator_enable(aux_pu_vreg); + if (rc) { + pr_err("Unable to enable vdda33: %d\n", rc); + goto unset_vdd33; + } + + pr_info("on success\n"); + power->aux_pullup_on = true; + } else { + + rc = regulator_disable(aux_pu_vreg); + if (rc) + pr_err("Unable to disable vdda33: %d\n", rc); + +unset_vdd33: + rc = regulator_set_voltage(aux_pu_vreg, 0, QUSB2PHY_3P3_VOL_MAX); + if (rc) + pr_err("Unable to set (0) voltage for vdda33: %d\n", rc); + +put_vdda33_lpm: + rc = regulator_set_load(aux_pu_vreg, 0); + if (rc < 0) + pr_err("Unable to set (0) HPM of vdda33: %d\n", rc); + + if (!rc) + pr_info("off success\n"); + + power->aux_pullup_on = false; + } + +exit: + return rc; +} +#endif + static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable) { int rc = 0, i = 0, j = 0; @@ -106,6 +198,11 @@ static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable) goto error; } } + +#ifdef CONFIG_SEC_DISPLAYPORT + secdp_aux_pullup_vreg_enable(enable); +#endif + error: return rc; } @@ -136,6 +233,13 @@ static int dp_power_pinctrl_set(struct dp_power_private *power, bool active) : "dp_sleep"); } +#ifdef CONFIG_SEC_DISPLAYPORT + if (rc) { + pr_debug("rc: %d -> it's ok\n", rc); + rc = 0; + } +#endif + return rc; } @@ -288,6 +392,27 @@ static int dp_power_clk_enable(struct dp_power *dp_power, } } +#ifdef CONFIG_SEC_DISPLAYPORT + if (!enable) { + /* consider below abnormal sequence : + * CCIC_NOTIFY_ATTACH + * -> no CCIC_NOTIFY_ID_DP_LINK_CONF, no CCIC_NOTIFY_ID_DP_HPD + * -> CCIC_NOTIFY_DETACH + */ + if ((pm_type == DP_CORE_PM) + && (!power->core_clks_on)) { + pr_debug("core clks already disabled\n"); + return 0; + } + + if ((pm_type == DP_CTRL_PM) + && (!power->link_clks_on)) { + pr_debug("links clks already disabled\n"); + return 0; + } + } +#endif + rc = dp_power_clk_set_rate(power, pm_type, enable); if (rc) { pr_err("failed to '%s' clks for: %s. err=%d\n", @@ -356,6 +481,7 @@ static bool dp_power_find_gpio(const char *gpio1, const char *gpio2) return !!strnstr(gpio1, gpio2, strlen(gpio1)); } +#ifndef CONFIG_SEC_DISPLAYPORT static void dp_power_set_gpio(struct dp_power_private *power, bool flip) { int i; @@ -381,11 +507,310 @@ 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) { + pr_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; +} + +#ifdef CONFIG_COMBO_REDRIVER +void secdp_redriver_onoff(bool enable, int lane) +{ + pr_debug("+++ enable(%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 { + pr_err("error! unknown lane: %d\n", lane); + goto exit; + } + + val = ptn36502_i2c_read(Chip_ID); + pr_info("Chip_ID: 0x%x\n", val); + + val = ptn36502_i2c_read(Chip_Rev); + pr_info("Chip_Rev: 0x%x\n", val); + + } else { + ptn36502_config(SAFE_STATE, 0); + } + +exit: + return; +} + +#define DP_ENUM_STR(x) #x + +enum redriver_switch_t +{ + REDRIVER_SWITCH_UNKNOWN = -1, + REDRIVER_SWITCH_RESET = 0, + REDRIVER_SWITCH_CROSS, + REDRIVER_SWITCH_THROU, +}; + +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"; + } +} + +static void secdp_redriver_aux_ctrl(int cross) +{ + pr_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: + pr_info("unknown: %d\n", cross); + break; + } +} +#endif + +/* 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(bool flip) +{ + int i; + /*int dir = (flip == false) ? 0 : 1;*/ + struct dp_power_private *power = g_secdp_power; + struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM]; + struct dss_gpio *config; + + pr_debug("+++, flip(%d)\n", flip); + +#if 0 /*use flip instead*/ + 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); + pr_info("%s -> dir: %d\n", config->gpio_name, dir); + break; + } + } + config++; + } + + usleep_range(100, 120); +#endif + +#ifndef CONFIG_COMBO_REDRIVER + 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")) { + gpio_direction_output(config->gpio, (flip == false)/*(dir == 0)*/ ? 0 : 1); + usleep_range(100, 120); + pr_info("%s -> set %d\n", config->gpio_name, gpio_get_value(config->gpio)); + break; + } + } + config++; + } +#endif + + 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")) { + gpio_direction_output(config->gpio, 0); + pr_info("%s -> %d\n", config->gpio_name, gpio_get_value(config->gpio)); + break; + } + } + config++; + } +} + +/* turn off EDP_AUX switch */ +static void secdp_power_unset_gpio(void) +{ + int i; + struct dp_power_private *power = g_secdp_power; + struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM]; + struct dss_gpio *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-en")) { + gpio_direction_output(config->gpio, 1); + pr_info("%s -> 1\n", config->gpio_name); + 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")) { + gpio_direction_output(config->gpio, 0); + pr_info("%s -> 0\n", config->gpio_name); + break; + } + } + config++; + } +} + +/* + * @aux_sel : 1 or 0 + */ +void secdp_config_gpios_factory(int aux_sel, bool on) +{ + pr_debug("%s (%d,%d)\n", __func__, aux_sel, on); + + if (on) { + /* power on ldo24 */ + secdp_aux_pullup_vreg_enable(true); + +#ifdef CONFIG_COMBO_REDRIVER + /* set aux_sel, aux_en */ + secdp_power_set_gpio(aux_sel); + + if (aux_sel == 1) + secdp_redriver_aux_ctrl(REDRIVER_SWITCH_CROSS); + else if (aux_sel == 0) + secdp_redriver_aux_ctrl(REDRIVER_SWITCH_THROU); + else + pr_err("unknown <%d>\n", aux_sel); +#else + /* set aux_sel, aux_en */ + secdp_power_set_gpio(aux_sel); +#endif + } else { +#ifdef CONFIG_COMBO_REDRIVER + secdp_redriver_aux_ctrl(REDRIVER_SWITCH_RESET); + + /* unset aux_sel, aux_en */ + secdp_power_unset_gpio(); +#else + /* unset aux_sel, aux_en */ + secdp_power_unset_gpio(); +#endif + + /* power off ldo24 */ + secdp_aux_pullup_vreg_enable(false); + } +} + +extern int CC_DIR; + +enum plug_orientation secdp_get_plug_orientation(void) +{ +#if 1 /*org*/ + int i, dir; + struct dp_power_private *power = g_secdp_power; + struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM]; + struct dss_gpio *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 1 + /*tabs_2019 h/w rev01 -- CC_DIR comes reverted*/ + dir = !dir; +#endif + pr_info("orientation: %s\n", (dir == 0) ? "CC1" : "CC2"); + if (dir == 0) + return ORIENTATION_CC1; + else /* if (dir == 1) */ + return ORIENTATION_CC2; + } + } + config++; + } + + /*cannot be here*/ + return ORIENTATION_NONE; +#else /*test*/ + enum plug_orientation dir; + + pr_debug("+++ CC_DIR:%d\n", CC_DIR); + + if (CC_DIR == 0) { + dir = ORIENTATION_CC2; + pr_info("CC2\n"); + } else if (CC_DIR == 1) { + dir = ORIENTATION_CC1; + pr_info("CC1\n"); + } else { + dir = ORIENTATION_NONE; + pr_info("NONE??\n"); + } + + return dir; +#endif +} + +bool secdp_get_clk_status(enum dp_pm_type type) +{ + struct dp_power_private *power = g_secdp_power; + bool ret = false; + + switch (type) { + case DP_CORE_PM: + ret = power->core_clks_on; + break; + default: + pr_err("invalid type:%d\n", type); + break; + } + + return ret; +} +#endif static int dp_power_config_gpios(struct dp_power_private *power, bool flip, bool enable) { +#ifndef CONFIG_SEC_DISPLAYPORT int rc = 0, i; +#endif struct dss_module_power *mp; struct dss_gpio *config; @@ -393,6 +818,7 @@ static int dp_power_config_gpios(struct dp_power_private *power, bool flip, config = mp->gpio_config; if (enable) { +#ifndef CONFIG_SEC_DISPLAYPORT rc = dp_power_request_gpios(power); if (rc) { pr_err("gpio request failed\n"); @@ -400,11 +826,18 @@ static int dp_power_config_gpios(struct dp_power_private *power, bool flip, } dp_power_set_gpio(power, flip); +#else + secdp_power_set_gpio(flip); +#endif } else { +#ifndef CONFIG_SEC_DISPLAYPORT for (i = 0; i < mp->num_gpio; i++) { gpio_set_value(config[i].gpio, 0); gpio_free(config[i].gpio); } +#else + secdp_power_unset_gpio(); +#endif } return 0; @@ -445,6 +878,14 @@ static int dp_power_client_init(struct dp_power *dp_power, rc = -EINVAL; goto error_client; } + +#ifdef CONFIG_SEC_DISPLAYPORT + rc = dp_power_pinctrl_set(power, false); + if (rc) { + pr_err("failed to set pinctrl state\n"); + goto error_client; + } +#endif return 0; error_client: @@ -614,6 +1055,10 @@ struct dp_power *dp_power_get(struct dp_parser *parser) dp_power->power_client_init = dp_power_client_init; dp_power->power_client_deinit = dp_power_client_deinit; +#ifdef CONFIG_SEC_DISPLAYPORT + g_secdp_power = power; +#endif + return dp_power; error: return ERR_PTR(rc); @@ -629,4 +1074,8 @@ void dp_power_put(struct dp_power *dp_power) power = container_of(dp_power, struct dp_power_private, dp_power); devm_kfree(&power->pdev->dev, power); + +#ifdef CONFIG_SEC_DISPLAYPORT + g_secdp_power = NULL; +#endif } diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c index 3a14f7c96803..9c9eab681ce3 100644 --- a/drivers/gpu/drm/msm/dp/dp_usbpd.c +++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c @@ -19,7 +19,11 @@ #include #include "dp_usbpd.h" +#ifdef CONFIG_SEC_DISPLAYPORT +#include "secdp.h" +#endif +#ifndef CONFIG_SEC_DISPLAYPORT /* DP specific VDM commands */ #define DP_USBPD_VDM_STATUS 0x10 #define DP_USBPD_VDM_CONFIGURE 0x11 @@ -76,7 +80,17 @@ struct dp_usbpd_private { enum dp_usbpd_alt_mode alt_mode; u32 dp_usbpd_config; }; +#else +struct dp_usbpd_private { + bool forced_disconnect; + u32 vdo; + struct device *dev; + struct dp_usbpd_cb *dp_cb; + struct dp_usbpd dp_usbpd; +}; +#endif +#ifndef CONFIG_SEC_DISPLAYPORT static const char *dp_usbpd_pin_name(u8 pin) { switch (pin) { @@ -428,6 +442,7 @@ static void dp_usbpd_response_cb(struct usbpd_svid_handler *hdlr, u8 cmd, break; } } +#endif static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd) { @@ -445,15 +460,23 @@ static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd) dp_usbpd->hpd_high = hpd; pd->forced_disconnect = !hpd; +#ifndef CONFIG_SEC_DISPLAYPORT if (hpd) pd->dp_cb->configure(pd->dev); else pd->dp_cb->disconnect(pd->dev); +#else + if (hpd) + pd->dp_cb->configure(); + else + pd->dp_cb->disconnect(); +#endif error: return rc; } +#ifndef CONFIG_SEC_DISPLAYPORT static int dp_usbpd_simulate_attention(struct dp_usbpd *dp_usbpd, int vdo) { int rc = 0; @@ -528,9 +551,35 @@ struct dp_usbpd *dp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb) dp_usbpd->simulate_attention = dp_usbpd_simulate_attention; return dp_usbpd; + error: return ERR_PTR(rc); } +#else +struct dp_usbpd *secdp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb) +{ + int rc = 0; + struct dp_usbpd_private *usbpd; + struct dp_usbpd *dp_usbpd; + + usbpd = devm_kzalloc(dev, sizeof(*usbpd), GFP_KERNEL); + if (!usbpd) { + rc = -ENOMEM; + goto error; + } + + usbpd->dev = dev; + usbpd->dp_cb = cb; + + dp_usbpd = &usbpd->dp_usbpd; + dp_usbpd->simulate_connect = dp_usbpd_simulate_connect; + + return dp_usbpd; + +error: + return ERR_PTR(rc); +} +#endif/*CONFIG_SEC_DISPLAYPORT*/ void dp_usbpd_put(struct dp_usbpd *dp_usbpd) { @@ -541,7 +590,9 @@ void dp_usbpd_put(struct dp_usbpd *dp_usbpd) usbpd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd); +#ifndef CONFIG_SEC_DISPLAYPORT usbpd_unregister_svid(usbpd->pd, &usbpd->svid_handler); +#endif devm_kfree(usbpd->dev, usbpd); } diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.h b/drivers/gpu/drm/msm/dp/dp_usbpd.h index 0a7efd957d84..2a84e8e73d3a 100644 --- a/drivers/gpu/drm/msm/dp/dp_usbpd.h +++ b/drivers/gpu/drm/msm/dp/dp_usbpd.h @@ -69,6 +69,7 @@ struct dp_usbpd { int (*simulate_attention)(struct dp_usbpd *dp_usbpd, int vdo); }; +#ifndef CONFIG_SEC_DISPLAYPORT /** * struct dp_usbpd_cb - callback functions provided by the client * @@ -97,6 +98,24 @@ struct dp_usbpd_cb { * the callback functions about the connection and status. */ struct dp_usbpd *dp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb); +#else +/** + * struct dp_usbpd_cb - callback functions provided by the client + * + * @configure: called by usbpd module when PD communication has + * been completed and the usb peripheral has been configured on + * dp mode. + * @disconnect: notify the cable disconnect issued by usb. + * @attention: notify any attention message issued by usb. + */ +struct dp_usbpd_cb { + int (*configure)(void); + int (*disconnect)(void); +}; + +struct dp_usbpd *secdp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb); + +#endif /* CONFIG_SEC_DISPLAYPORT */ void dp_usbpd_put(struct dp_usbpd *pd); #endif /* _DP_USBPD_H_ */ diff --git a/drivers/gpu/drm/msm/dp/secdp.h b/drivers/gpu/drm/msm/dp/secdp.h new file mode 100644 index 000000000000..754a7c0714ea --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2017, 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 + +#include +#include +#include +#include "dp_power.h" +#include "dp_panel.h" +#include "dp_catalog.h" + +/*defined at: include/linux/ccic/ccic_alternate.h*/ +#define SAMSUNG_VENDOR_ID 0x04E8 +#define DEXDOCK_PRODUCT_ID 0xA020 /* EE-MG950, DeX station */ +#define HG950_PRODUCT_ID 0xA025 /* EE-H950 */ +#define MPA2_PRODUCT_ID 0xA027 /* EE-P5000 */ +#define DEXPAD_PRODUCT_ID 0xA029 /* EE-M5100 */ +#define DEXCABLE_PRODUCT_ID 0xA048 /* EE-I3100 */ + +enum dex_adapter_t { + DEX_ADAPTER_NONE = 0, + DEX_ADAPTER_DEXSTN, + DEX_ADAPTER_HG950, + DEX_ADAPTER_MPA2, + DEX_ADAPTER_DEXPAD, + DEX_ADAPTER_DEXCBL, +}; + +static inline char *secdp_dex_adapter_to_string(enum dex_adapter_t dongle) +{ + switch (dongle) { + case DEX_ADAPTER_NONE: return DP_ENUM_STR(DEX_ADAPTER_NONE); + case DEX_ADAPTER_DEXSTN: return DP_ENUM_STR(DEX_ADAPTER_DEXSTN); + case DEX_ADAPTER_HG950: return DP_ENUM_STR(DEX_ADAPTER_HG950); + case DEX_ADAPTER_MPA2: return DP_ENUM_STR(DEX_ADAPTER_MPA2); + case DEX_ADAPTER_DEXPAD: return DP_ENUM_STR(DEX_ADAPTER_DEXPAD); + case DEX_ADAPTER_DEXCBL: return DP_ENUM_STR(DEX_ADAPTER_DEXCBL); + default: return "unknown"; + } +} + +#define SECDP_ENUM_STR(x) #x +#define dim(x) ((sizeof(x))/(sizeof(x[0]))) + +#ifdef CONFIG_SEC_FACTORY +#define SECDP_HDCP_DISABLE +#endif +#define SECDP_USB_CONCURRENCY +/*#define SECDP_AUDIO_CTS*/ +/*#define NOT_SUPPORT_DEX_RES_CHANGE*/ + +/*#define SECDP_CALIBRATE_VXPX*/ /* debug for calibrating voltage_level, pre-emphasis_level */ +/*#define SECDP_OPTIMAL_LINK_RATE*/ /* use optimum link_rate, not max link_rate */ +#define SECDP_WIDE_21_9_SUPPORT /* support ultra-wide 21:9 resolution (2560x1080p, 3440x1440p) */ +#define SECDP_WIDE_32_9_SUPPORT /* support ultra-wide 32:9 resolution (3840x1080p) */ +#define SECDP_WIDE_32_10_SUPPORT /* support ultra-wide 32:10 resolution (3840x1200p) */ +/*#define SECDP_HIGH_REFRESH_SUPPORT*/ /* support more than 60hz refresh rate, such as 100/120/144hz */ +#define SECDP_MAX_RESOLUTION_4K30 /* when max dp resolution is 4k@30hz, change to QHD@60hz */ + +#define LEN_BRANCH_REVISION 3 +#define DPCD_BRANCH_HW_REVISION 0x509 +#define DPCD_BRANCH_SW_REVISION_MAJOR 0x50A +#define DPCD_BRANCH_SW_REVISION_MINOR 0x50B + +/* monitor aspect ratio */ +enum mon_aspect_ratio_t { + MON_RATIO_NA = -1, + MON_RATIO_16_9, + MON_RATIO_16_10, + MON_RATIO_21_9, + MON_RATIO_32_9, + MON_RATIO_32_10, +}; + +/* dex supported resolutions */ +enum dex_support_res_t { + DEX_RES_NOT_SUPPORT, + 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_3840X2160, /* UHD */ +}; + +#define DEX_RES_DFT DEX_RES_3440X1440 /* DeX default resolution */ +#define DEX_RES_MAX DEX_RES_3440X1440 /* DeX max resolution */ + +enum DEX_STATUS { + DEX_DISABLED, + DEX_ENABLED, + DEX_DURING_MODE_CHANGE, +}; + +struct secdp_dex { + int prev; /* previously known as "dex_now" */ + int curr; /* previously known as "dex_en" */ + int setting_ui; /* "dex_set", true if setting has Dex mode */ + /* + * 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. + */ + int dex_node_status; + + enum dex_support_res_t res; /* dex supported resolution */ + char fw_ver[10]; /* firmware ver, 0:h/w, 1:s/w major, 2:s/w minor */ + int reconnecting; /* it's 1 during dex reconnecting */ +}; + +struct secdp_misc { + bool ccic_noti_registered; + struct delayed_work ccic_noti_reg_work; + struct notifier_block ccic_noti_block; + + struct delayed_work hdcp_start_work; + struct delayed_work link_status_work; + + struct secdp_dex dex; + + bool cable_connected; /* previously known as "cable_connected_phy" */ + bool link_conf; /* previously known as "sec_link_conf" */ + bool hpd; /* previously known as "sec_hpd" */ + uint vid; /* vendor id */ + uint pid; /* product id */ + + struct mutex notifier_lock; +}; + +struct secdp_display_timing { + int index; /* resolution priority */ + int active_h; + int active_v; + int refresh_rate; + bool interlaced; + 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 */ +}; + +#define EV_USBPD_ATTENTION BIT(13) + +static inline char *secdp_ev_event_to_string(int event) +{ + switch (event) { + case EV_USBPD_ATTENTION: + return DP_ENUM_STR(EV_USBPD_ATTENTION); + default: + return "unknown"; + } +} + +struct secdp_attention_node { + CC_NOTI_TYPEDEF noti; + struct list_head list; +}; + +bool secdp_get_clk_status(enum dp_pm_type type); + +int secdp_ccic_noti_register_ex(struct secdp_misc *sec, bool retry); +int secdp_init(struct platform_device *pdev); +void secdp_deinit(struct platform_device *pdev); +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); +bool secdp_get_poor_connection_status(void); +bool secdp_get_link_train_status(void); +struct dp_panel *secdp_get_panel_info(void); +struct drm_connector *secdp_get_connector(void); + +#if defined (CONFIG_SEC_DISPLAYPORT) && defined(CONFIG_COMBO_REDRIVER) +void secdp_redriver_onoff(bool enable, int lane); +#endif + +int secdp_power_request_gpios(struct dp_power *dp_power); +void secdp_config_gpios_factory(int aux_sel, bool out_en); +enum plug_orientation secdp_get_plug_orientation(void); + +void secdp_dex_res_init(void); +void secdp_dex_do_reconnecting(void); +bool secdp_check_dex_reconnect(void); +bool secdp_check_dex_mode(void); +enum dex_support_res_t secdp_get_dex_res(void); + +void secdp_clear_link_status_update_cnt(struct dp_link *dp_link); +void secdp_reset_link_status(struct dp_link *dp_link); +bool secdp_check_link_stable(struct dp_link *dp_link); + +bool secdp_find_supported_resolution(struct dp_panel_info *timing); + +#ifdef SECDP_CALIBRATE_VXPX +void secdp_catalog_vx_show(void); +int secdp_catalog_vx_store(int *val, int size); +void secdp_catalog_px_show(void); +int secdp_catalog_px_store(int *val, int size); +#endif + +#endif/*__SECDP_H*/ diff --git a/drivers/gpu/drm/msm/dp/secdp_aux_control.c b/drivers/gpu/drm/msm/dp/secdp_aux_control.c new file mode 100644 index 000000000000..4f0c78938c8a --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_aux_control.c @@ -0,0 +1,445 @@ +/* + * Copyright © 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "secdp_aux_control.h" + +#define DP_ENUM_STR(x) #x + +#define IOCTL_MAGIC 't' +#define IOCTL_MAXNR 30 +#define IOCTL_DP_AUXCMD_TYPE _IO(IOCTL_MAGIC, 0) +#define IOCTL_DP_HPD_STATUS _IO(IOCTL_MAGIC, 1) +#define DP_AUX_MAX_BUF 512 + +enum dp_auxcmd_type { + DP_AUXCMD_NATIVE, + DP_AUXCMD_I2C, +}; + +struct ioctl_auxdev_info { + int size; /* unused */ + enum dp_auxcmd_type cmd_type; +}; + +struct secdp_aux_dev { + unsigned int index; + struct device *dev; + struct kref refcount; + atomic_t usecount; + enum dp_auxcmd_type cmd_type; + + ssize_t (*secdp_i2c_write)(void *buffer, size_t size); + ssize_t (*secdp_i2c_read)(void *buffer, size_t size); + ssize_t (*secdp_dpcd_write)(unsigned int offset, + void *buffer, size_t size); + ssize_t (*secdp_dpcd_read)(unsigned int offset, + void *buffer, size_t size); + int (*secdp_get_hpd_status)(void); +}; + +static inline char *auxdev_ioctl_cmd_to_string(u32 cmd) +{ + switch (cmd) { + case IOCTL_DP_AUXCMD_TYPE: + return DP_ENUM_STR(IOCTL_DP_AUXCMD_TYPE); + case IOCTL_DP_HPD_STATUS: + return DP_ENUM_STR(IOCTL_DP_HPD_STATUS); + default: + return "unknown"; + } +} + +static inline char *auxcmd_type_to_string(u32 cmd_type) +{ + switch (cmd_type) { + case DP_AUXCMD_NATIVE: + return DP_ENUM_STR(DP_AUXCMD_NATIVE); + case DP_AUXCMD_I2C: + return DP_ENUM_STR(DP_AUXCMD_I2C); + default: + return "unknown"; + } +} + +#define DRM_AUX_MINORS 256 +#define AUX_MAX_OFFSET (1 << 20) + +static DEFINE_IDR(aux_idr); +static DEFINE_MUTEX(aux_idr_mutex); +static struct class *secdp_aux_dev_class; +static int drm_dev_major = -1; +static bool g_fw_update_status; + +bool secdp_get_fw_update_status(void) +{ + return g_fw_update_status; +} + +static struct secdp_aux_dev *secdp_aux_dev_get_by_minor(unsigned int index) +{ + struct secdp_aux_dev *aux_dev = NULL; + + mutex_lock(&aux_idr_mutex); + aux_dev = idr_find(&aux_idr, index); + if (!kref_get_unless_zero(&aux_dev->refcount)) + aux_dev = NULL; + mutex_unlock(&aux_idr_mutex); + + return aux_dev; +} + + +static void release_secdp_aux_dev(struct kref *ref) +{ + struct secdp_aux_dev *aux_dev = + container_of(ref, struct secdp_aux_dev, refcount); + + kfree(aux_dev); +} + +static int auxdev_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct secdp_aux_dev *aux_dev; + + aux_dev = secdp_aux_dev_get_by_minor(minor); + if (!aux_dev) + return -ENODEV; + + file->private_data = aux_dev; + g_fw_update_status = true; + + pr_info("aux node open\n"); + + return 0; +} + +static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence) +{ + return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET); +} + +static ssize_t auxdev_read(struct file *file, char __user *buf, size_t count, + loff_t *offset) +{ + size_t bytes_pending, num_bytes_processed = 0; + struct secdp_aux_dev *aux_dev = file->private_data; + ssize_t res = 0; + + if (!aux_dev->secdp_get_hpd_status()) + return -ENODEV; + + if (!atomic_inc_not_zero(&aux_dev->usecount)) + return -ENODEV; + + bytes_pending = min((loff_t)count, AUX_MAX_OFFSET - (*offset)); + + if (!access_ok(VERIFY_WRITE, buf, bytes_pending)) { + res = -EFAULT; + goto out; + } + + while (bytes_pending > 0) { + uint8_t localbuf[DP_AUX_MAX_BUF]; + ssize_t todo = min_t(size_t, bytes_pending, sizeof(localbuf)); + + if (signal_pending(current)) { + res = num_bytes_processed ? + num_bytes_processed : -ERESTARTSYS; + goto out; + } + + if (aux_dev->cmd_type == DP_AUXCMD_NATIVE) + res = aux_dev->secdp_dpcd_read(*offset, localbuf, todo); + else + res = aux_dev->secdp_i2c_read(localbuf, todo); + + if (res <= 0) { + res = num_bytes_processed ? num_bytes_processed : res; + goto out; + } + if (__copy_to_user(buf + num_bytes_processed, localbuf, res)) { + res = num_bytes_processed ? + num_bytes_processed : -EFAULT; + goto out; + } + bytes_pending -= res; + + if (aux_dev->cmd_type == DP_AUXCMD_NATIVE) + *offset += res; + + num_bytes_processed += res; + res = num_bytes_processed; + } + +out: + atomic_dec(&aux_dev->usecount); + wake_up_atomic_t(&aux_dev->usecount); + return res; +} + +static ssize_t auxdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + size_t bytes_pending, num_bytes_processed = 0; + struct secdp_aux_dev *aux_dev = file->private_data; + ssize_t res = 0; + + if (!aux_dev->secdp_get_hpd_status()) + return -ENODEV; + + if (!atomic_inc_not_zero(&aux_dev->usecount)) + return -ENODEV; + + bytes_pending = min((loff_t)count, AUX_MAX_OFFSET - *offset); + + if (!access_ok(VERIFY_READ, buf, bytes_pending)) { + res = -EFAULT; + goto out; + } + + while (bytes_pending > 0) { + uint8_t localbuf[DP_AUX_MAX_BUF]; + ssize_t todo = min_t(size_t, bytes_pending, sizeof(localbuf)); + + if (signal_pending(current)) { + res = num_bytes_processed ? + num_bytes_processed : -ERESTARTSYS; + goto out; + } + + if (__copy_from_user(localbuf, + buf + num_bytes_processed, todo)) { + res = num_bytes_processed ? + num_bytes_processed : -EFAULT; + goto out; + } + + if (aux_dev->cmd_type == DP_AUXCMD_NATIVE) + res = aux_dev->secdp_dpcd_write(*offset, localbuf, todo); + else + res = aux_dev->secdp_i2c_write(localbuf, todo); + + if (res <= 0) { + res = num_bytes_processed ? num_bytes_processed : res; + goto out; + } + bytes_pending -= res; + if (aux_dev->cmd_type == DP_AUXCMD_NATIVE) + *offset += res; + num_bytes_processed += res; + res = num_bytes_processed; + } + +out: + atomic_dec(&aux_dev->usecount); + wake_up_atomic_t(&aux_dev->usecount); + return res; +} + +static int auxdev_release(struct inode *inode, struct file *file) +{ + struct secdp_aux_dev *aux_dev = file->private_data; + + g_fw_update_status = false; + pr_info("aux node release\n"); + + kref_put(&aux_dev->refcount, release_secdp_aux_dev); + return 0; +} + +static long auxdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + s32 res = 0; + u32 size = 0; + char hpd; + struct ioctl_auxdev_info info; + struct secdp_aux_dev *aux_dev = file->private_data; + + if (_IOC_TYPE(cmd) != IOCTL_MAGIC) + return -EINVAL; + if (_IOC_NR(cmd) >= IOCTL_MAXNR) + return -EINVAL; + + size = sizeof(struct ioctl_auxdev_info); + pr_info("cmd: %s\n", auxdev_ioctl_cmd_to_string(cmd)); + + switch (cmd) { + case IOCTL_DP_AUXCMD_TYPE: + res = copy_from_user((void *)&info, (void *)arg, size); + if (res) { + pr_debug("error at copy_from_user, rc(%d)\n", res); + break; + } + aux_dev->cmd_type = info.cmd_type; + pr_info("auxcmd_type: %s\n", auxcmd_type_to_string(aux_dev->cmd_type)); + break; + case IOCTL_DP_HPD_STATUS: + hpd = aux_dev->secdp_get_hpd_status(); + res = copy_to_user((void *)arg, (void *)&hpd, 1); + break; + default: + break; + } + + return res; +} + +static const struct file_operations auxdev_fops = { + .owner = THIS_MODULE, + .llseek = auxdev_llseek, + .read = auxdev_read, + .write = auxdev_write, + .open = auxdev_open, + .release = auxdev_release, + .unlocked_ioctl = auxdev_ioctl, + .compat_ioctl = auxdev_ioctl, +}; + +static int auxdev_wait_atomic_t(atomic_t *p) +{ + schedule(); + return 0; +} + + +void secdp_aux_unregister_devnode(struct secdp_aux_dev *aux_dev) +{ + unsigned int minor; + + mutex_lock(&aux_idr_mutex); + idr_remove(&aux_idr, aux_dev->index); + mutex_unlock(&aux_idr_mutex); + + atomic_dec(&aux_dev->usecount); + wait_on_atomic_t(&aux_dev->usecount, auxdev_wait_atomic_t, + TASK_UNINTERRUPTIBLE); + + minor = aux_dev->index; + if (aux_dev->dev) + device_destroy(secdp_aux_dev_class, + MKDEV(drm_dev_major, minor)); + + pr_info("secdp_aux_dev: aux unregistering\n"); + kref_put(&aux_dev->refcount, release_secdp_aux_dev); +} + +int secdp_aux_create_node(struct secdp_aux_dev *aux_dev) +{ + int res; + + secdp_aux_dev_class = class_create(THIS_MODULE, "dp_sec_aux"); + if (IS_ERR(secdp_aux_dev_class)) + return -EINVAL; + + res = register_chrdev(0, "secdp_aux", &auxdev_fops); + if (res < 0) + goto out; + + drm_dev_major = res; + aux_dev->dev = device_create(secdp_aux_dev_class, NULL, + MKDEV(drm_dev_major, aux_dev->index), NULL, + "secdp_aux"); + + if (IS_ERR(aux_dev->dev)) { + res = -EINVAL; + aux_dev->dev = NULL; + goto error; + } + + return 0; +error: + secdp_aux_unregister_devnode(aux_dev); +out: + class_destroy(secdp_aux_dev_class); + return res; +} + +int secdp_aux_dev_init(ssize_t (*secdp_i2c_write)(void *buffer, size_t size), + ssize_t (*secdp_i2c_read)(void *buffer, size_t size), + ssize_t (*secdp_dpcd_write)(unsigned int offset, void *buffer, size_t size), + ssize_t (*secdp_dpcd_read)(unsigned int offset, void *buffer, size_t size), + int (*secdp_get_hpd_status)(void)) +{ + static bool check_init; + struct secdp_aux_dev *aux_dev; + int index; + int ret; + + if (check_init) + return 0; + + aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL); + if (!aux_dev) + return -ENOMEM; + + atomic_set(&aux_dev->usecount, 1); + kref_init(&aux_dev->refcount); + + mutex_lock(&aux_idr_mutex); + index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, + GFP_KERNEL); + mutex_unlock(&aux_idr_mutex); + if (index < 0) { + ret = index; + goto error; + } + + aux_dev->index = index; + aux_dev->cmd_type = DP_AUXCMD_NATIVE; + + ret = secdp_aux_create_node(aux_dev); + if (ret < 0) + goto error; + + aux_dev->secdp_i2c_write = secdp_i2c_write; + aux_dev->secdp_i2c_read = secdp_i2c_read; + aux_dev->secdp_dpcd_write = secdp_dpcd_write; + aux_dev->secdp_dpcd_read = secdp_dpcd_read; + aux_dev->secdp_get_hpd_status = secdp_get_hpd_status; + + check_init = true; + + return 0; +error: + kfree(aux_dev); + return ret; +} + +void secdp_aux_dev_exit(void) +{ + unregister_chrdev(drm_dev_major, "secdp_aux"); + class_destroy(secdp_aux_dev_class); +} diff --git a/drivers/gpu/drm/msm/dp/secdp_aux_control.h b/drivers/gpu/drm/msm/dp/secdp_aux_control.h new file mode 100644 index 000000000000..6046c51a20a9 --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_aux_control.h @@ -0,0 +1,27 @@ + + +/* + * Copyright (c) 2017, 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_AUX_CONTROL_H_ +#define _SECDP_AUX_CONTROL_H_ + +int secdp_aux_dev_init(ssize_t (*secdp_i2c_write)(void *buffer, size_t size), + ssize_t (*secdp_i2c_read)(void *buffer, size_t size), + ssize_t (*secdp_dpcd_write)(unsigned int offset, void *buffer, size_t size), + ssize_t (*secdp_dpcd_read)(unsigned int offset, void *buffer, size_t size), + int (*secdp_get_hpd_status)(void)); + +bool secdp_get_fw_update_status(void); +#endif /* _SECDP_AUX_CONTROL_H_ */ + diff --git a/drivers/gpu/drm/msm/dp/secdp_logger.c b/drivers/gpu/drm/msm/dp/secdp_logger.c new file mode 100644 index 000000000000..b328a1844a95 --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_logger.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * DP 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + +void dp_logger_print_date_time(void) +{ + char tmp[64] = {0x0, }; + struct tm tm; + u64 time; + unsigned long nsec; + unsigned long sec; + + time = local_clock(); + nsec = do_div(time, 1000000000); + sec = get_seconds() - (sys_tz.tz_minuteswest * 60); + time_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, 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(); +} + +void secdp_logger_print(const char *fmt, ...) +{ + int len; + va_list args; + char buf[MAX_STR_LEN] = {0,}; + u64 time; + unsigned long nsec; + volatile unsigned int curpos; + + if (!is_secdp_logger_init) + return; + + if (log_max_count == 0) + return; + else if (log_max_count > 0) + log_max_count--; + + time = local_clock(); + nsec = do_div(time, 1000000000); + len = snprintf(buf, sizeof(buf), "[%5lu.%06ld] ", (unsigned long)time, nsec / 1000); + + va_start(args, fmt); + len += vsnprintf(buf + len, MAX_STR_LEN - len, fmt, args); + va_end(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; +} + +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 == 0) + return; + else 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", pref, tmp); + ptmp = tmp; + } + } + + len = i % 16; + if (len != 0) { + tmp[len] = 0x0; + secdp_logger_print("%s\n", 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; +} + +static const struct file_operations secdp_logger_ops = { + .owner = THIS_MODULE, + .read = secdp_logger_read, +}; + +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; + secdp_logger_print("dp logger init ok\n"); + + return 0; +} diff --git a/drivers/gpu/drm/msm/dp/secdp_sysfs.c b/drivers/gpu/drm/msm/dp/secdp_sysfs.c new file mode 100644 index 000000000000..8d87f1267cd9 --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_sysfs.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2017, 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. + * + */ +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ + +#include +#include +#include +#include +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA +#include +#endif + +#include "secdp.h" +#include "secdp_sysfs.h" +#include "sde_edid_parser.h" +#include "secdp_unit_test.h" + +enum secdp_unit_test_cmd { + SECDP_UTCMD_EDID_PARSE = 0, +}; + +struct secdp_sysfs_private { + struct device *dev; + struct secdp_sysfs dp_sysfs; + struct secdp_misc *sec; + enum secdp_unit_test_cmd test_cmd; +}; + +struct secdp_sysfs_private *g_secdp_sysfs; + +static inline char *secdp_utcmd_to_str(u32 cmd_type) +{ + switch (cmd_type) { + case SECDP_UTCMD_EDID_PARSE: + return SECDP_ENUM_STR(SECDP_UTCMD_EDID_PARSE); + default: + return "unknown"; + } +} + +/** check if buf has range('-') format + * @buf buf to be checked + * @size buf size + * @retval 0 if args are ok, -1 if '-' included + */ +static int secdp_check_store_args(const char *buf, size_t size) +{ + int ret; + + if (strnchr(buf, size, '-')) { + pr_err("range is forbidden!\n"); + ret = -1; + goto exit; + } + + ret = 0; +exit: + return ret; +} + +static ssize_t secdp_sbu_sw_sel_store(struct class *dev, + struct class_attribute *attr, const char *buf, size_t size) +{ + int val[10] = {0,}; + int sbu_sw_sel, sbu_sw_oe; + + if (secdp_check_store_args(buf, size)) { + pr_err("args error!\n"); + goto exit; + } + + get_options(buf, ARRAY_SIZE(val), val); + + sbu_sw_sel = val[1]; + sbu_sw_oe = val[2]; + pr_info("sbu_sw_sel(%d), sbu_sw_oe(%d)\n", sbu_sw_sel, sbu_sw_oe); + + if (sbu_sw_oe == 0/*on*/) + secdp_config_gpios_factory(sbu_sw_sel, true); + else if (sbu_sw_oe == 1/*off*/) + secdp_config_gpios_factory(sbu_sw_sel, false); + else + pr_err("unknown sbu_sw_oe value: %d", sbu_sw_oe); + +exit: + return size; +} + +static ssize_t secdp_forced_resolution_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + int rc = 0; + int vic = 1; + + if (forced_resolution) { + rc = scnprintf(buf, PAGE_SIZE, + "%d : %s\n", forced_resolution, + secdp_vic_to_string(forced_resolution)); + + } else { + while (secdp_vic_to_string(vic) != NULL) { + rc += scnprintf(buf + rc, PAGE_SIZE - rc, + "%d : %s\n", vic, secdp_vic_to_string(vic)); + vic++; + } + } + + return rc; +} + +static ssize_t secdp_forced_resolution_store(struct class *dev, + struct class_attribute *attr, const char *buf, size_t size) +{ + int val[10] = {0, }; + + if (secdp_check_store_args(buf, size)) { + pr_err("args error!\n"); + goto exit; + } + + get_options(buf, ARRAY_SIZE(val), val); + + if (val[1] <= 0) + forced_resolution = 0; + else + forced_resolution = val[1]; + +exit: + return size; +} + +static ssize_t secdp_dex_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + int rc = 0; + struct secdp_sysfs_private *sysfs = g_secdp_sysfs; + struct secdp_dex *dex = &sysfs->sec->dex; + + if (!secdp_get_cable_status() || !secdp_get_hpd_status() || + secdp_get_poor_connection_status() || !secdp_get_link_train_status()) { + pr_info("cable is out\n"); + dex->prev = dex->curr = dex->dex_node_status = DEX_DISABLED; + } + + pr_info("prev: %d, curr: %d, dex_node_status: %d\n", dex->prev, dex->curr, dex->dex_node_status); + rc = scnprintf(buf, PAGE_SIZE, "%d\n", dex->dex_node_status); + + if (dex->dex_node_status == DEX_DURING_MODE_CHANGE) + dex->dex_node_status = dex->curr; + + return rc; +} + +static ssize_t secdp_dex_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t size) +{ + int val[4] = {0,}; + int setting_ui; /* setting has Dex mode? if yes, 1. otherwise 0 */ + int run; /* dex is running now? if yes, 1. otherwise 0 */ + + struct secdp_sysfs_private *sysfs = g_secdp_sysfs; + struct secdp_misc *sec = sysfs->sec; + struct secdp_dex *dex = &sec->dex; + + if (secdp_check_store_args(buf, size)) { + pr_err("args error!\n"); + goto exit; + } + + get_options(buf, ARRAY_SIZE(val), val); + pr_info("%d(0x%02x)\n", val[1], val[1]); + setting_ui = (val[1] & 0xf0) >> 4; + run = (val[1] & 0x0f); + + pr_info("setting_ui: %d, run: %d, cable: %d\n", + setting_ui, run, sec->cable_connected); + + dex->setting_ui = setting_ui; + dex->dex_node_status = dex->curr = run; + + mutex_lock(&sec->notifier_lock); + if (!sec->ccic_noti_registered) { + int rc; + + pr_debug("notifier get registered by dex\n"); + + /* cancel immediately */ + rc = cancel_delayed_work(&sec->ccic_noti_reg_work); + pr_debug("cancel_work, rc(%d)\n", rc); + destroy_delayed_work_on_stack(&sec->ccic_noti_reg_work); + + /* register */ + rc = secdp_ccic_noti_register_ex(sec, false); + if (rc) + pr_err("noti register fail, rc(%d)\n", rc); + + mutex_unlock(&sec->notifier_lock); + goto exit; + } + mutex_unlock(&sec->notifier_lock); + + if (!secdp_get_cable_status() || !secdp_get_hpd_status() || + secdp_get_poor_connection_status() || !secdp_get_link_train_status()) { + pr_info("cable is out\n"); + dex->prev = dex->curr = dex->dex_node_status = DEX_DISABLED; + goto exit; + } + + if (dex->curr == dex->prev) { + pr_info("dex is %s already\n", dex->curr ? "enabled" : "disabled"); + goto exit; + } + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + if (run) + secdp_bigdata_save_item(BD_DP_MODE, "DEX"); + else + secdp_bigdata_save_item(BD_DP_MODE, "MIRROR"); +#endif + + if (sec->dex.res == DEX_RES_NOT_SUPPORT) { + pr_debug("this dongle does not support dex\n"); + goto exit; + } + + if (!secdp_check_dex_reconnect()) { + pr_info("not need reconnect\n"); + goto exit; + } + + secdp_dex_do_reconnecting(); + + dex->prev = run; +exit: + return size; +} + +static ssize_t secdp_dex_version_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + int rc; + struct secdp_sysfs_private *sysfs = g_secdp_sysfs; + struct secdp_misc *sec = sysfs->sec; + struct secdp_dex *dex = &sec->dex; + + pr_info("branch revision: HW(0x%X), SW(0x%X, 0x%X)\n", + dex->fw_ver[0], dex->fw_ver[1], dex->fw_ver[2]); + + rc = scnprintf(buf, PAGE_SIZE, "%02X%02X\n", + dex->fw_ver[1], dex->fw_ver[2]); + + return rc; +} + +/* note: needs test once wifi is fixed */ +static ssize_t secdp_monitor_info_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + int rc = 0; + short prod_id = 0; + struct dp_panel *info = NULL; + struct sde_edid_ctrl *edid_ctrl = NULL; + struct edid *edid = NULL; + + info = secdp_get_panel_info(); + if (!info) { + pr_err("unable to find panel info\n"); + goto exit; + } + + edid_ctrl = info->edid_ctrl; + if (!edid_ctrl) { + pr_err("unable to find edid_ctrl\n"); + goto exit; + } + + edid = edid_ctrl->edid; + if (!edid) { + pr_err("unable to find edid\n"); + goto exit; + } + + pr_debug("prod_code[0]: %02x, [1]: %02x\n", edid->prod_code[0], edid->prod_code[1]); + prod_id |= (edid->prod_code[0] << 8) | (edid->prod_code[1]); + pr_debug("prod_id: %04x\n", prod_id); + + rc = sprintf(buf, "%s,0x%x,0x%x\n", + edid_ctrl->vendor_id, prod_id, edid->serial); /* byte order? */ + +exit: + return rc; +} + +static ssize_t secdp_unit_test_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + struct secdp_sysfs_private *sysfs = g_secdp_sysfs; + int rc, cmd = sysfs->test_cmd; + bool res = false; + + pr_info("test_cmd: %s\n", secdp_utcmd_to_str(cmd)); + + switch (cmd) { + case SECDP_UTCMD_EDID_PARSE: + res = secdp_unit_test_edid_parse(); + break; + default: + pr_info("invalid test_cmd: %d\n", cmd); + break; + } + + rc = scnprintf(buf, 3, "%d\n", res ? 1 : 0); + return rc; +} + +static ssize_t secdp_unit_test_store(struct class *dev, + struct class_attribute *attr, const char *buf, size_t size) +{ + struct secdp_sysfs_private *sysfs = g_secdp_sysfs; + int val[10] = {0, }; + + if (secdp_check_store_args(buf, size)) { + pr_err("args error!\n"); + goto exit; + } + + get_options(buf, ARRAY_SIZE(val), val); + sysfs->test_cmd = val[1]; + + pr_info("test_cmd: %d...%s\n", sysfs->test_cmd, secdp_utcmd_to_str(sysfs->test_cmd)); + +exit: + return size; +} + +#ifdef SECDP_CALIBRATE_VXPX +static ssize_t secdp_voltage_level_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + secdp_catalog_vx_show(); + return 0; +} + +static ssize_t secdp_voltage_level_store(struct class *dev, + struct class_attribute *attr, const char *buf, size_t size) +{ + int i, val[30] = {0, }; + + if (secdp_check_store_args(buf, size)) { + pr_err("args error!\n"); + goto exit; + } + pr_debug("+++, size(%d)\n", (int)size); + + get_options(buf, ARRAY_SIZE(val), val); + for (i = 0; i < 16; i=i+4) + pr_debug("%02x,%02x,%02x,%02x\n", val[i+1],val[i+2],val[i+3],val[i+4]); + + secdp_catalog_vx_store(&val[1], 16); +exit: + return size; +} + +static ssize_t secdp_preemphasis_level_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + secdp_catalog_px_show(); + return 0; +} + +static ssize_t secdp_preemphasis_level_store(struct class *dev, + struct class_attribute *attr, const char *buf, size_t size) +{ + int i, val[30] = {0, }; + + if (secdp_check_store_args(buf, size)) { + pr_err("args error!\n"); + goto exit; + } + pr_debug("+++, size(%d)\n", (int)size); + + get_options(buf, ARRAY_SIZE(val), val); + for (i = 0; i < 16; i=i+4) + pr_debug("%02x,%02x,%02x,%02x\n", val[i+1],val[i+2],val[i+3],val[i+4]); + + secdp_catalog_px_store(&val[1], 16); +exit: + return size; +} +#endif + +static CLASS_ATTR(dp_sbu_sw_sel, 0664, NULL, secdp_sbu_sw_sel_store); +static CLASS_ATTR(forced_resolution, 0664, + secdp_forced_resolution_show, secdp_forced_resolution_store); +static CLASS_ATTR(dex, 0664, secdp_dex_show, secdp_dex_store); +static CLASS_ATTR(dex_ver, 0444, secdp_dex_version_show, NULL); +static CLASS_ATTR(monitor_info, 0444, secdp_monitor_info_show, NULL); +static CLASS_ATTR(unit_test, 0664, secdp_unit_test_show, secdp_unit_test_store); +#ifdef SECDP_CALIBRATE_VXPX +static CLASS_ATTR(vx_lvl, 0664, secdp_voltage_level_show, secdp_voltage_level_store); +static CLASS_ATTR(px_lvl, 0664, secdp_preemphasis_level_show, secdp_preemphasis_level_store); +#endif + +int secdp_sysfs_init(void) +{ + struct class *dp_class; + int rc = -1; + + dp_class = class_create(THIS_MODULE, "dp_sec"); + if (IS_ERR(dp_class)) { + pr_err("failed to create dp_sec_class\n"); + goto exit; + } + + rc = class_create_file(dp_class, &class_attr_dp_sbu_sw_sel); + if (rc) + pr_err("failed to create attr_dp_sbu_sw_sel(%d)\n", rc); + + rc = class_create_file(dp_class, &class_attr_forced_resolution); + if (rc) + pr_err("failed to create attr_dp_forced_resolution(%d)\n", rc); + + rc = class_create_file(dp_class, &class_attr_dex); + if (rc) + pr_err("failed to create attr_dex(%d)\n", rc); + + rc = class_create_file(dp_class, &class_attr_dex_ver); + if (rc) + pr_err("failed to create attr_dex_ver(%d)\n", rc); + + rc = class_create_file(dp_class, &class_attr_monitor_info); + if (rc) + pr_err("failed to create attr_monitor_info(%d)\n", rc); + + rc = class_create_file(dp_class, &class_attr_unit_test); + if (rc) + pr_err("failed to create attr_dp_test(%d)\n", rc); + +#ifdef SECDP_CALIBRATE_VXPX + rc = class_create_file(dp_class, &class_attr_vx_lvl); + if (rc) + pr_err("failed to create attr_voltage_level(%d)\n", rc); + + rc = class_create_file(dp_class, &class_attr_px_lvl); + if (rc) + pr_err("failed to create attr_preemphasis_level(%d)\n", rc); +#endif + +#ifdef CONFIG_SEC_DISPLAYPORT_BIGDATA + secdp_bigdata_init(dp_class); +#endif + rc = 0; + +exit: + return rc; +} + +void secdp_sysfs_deinit(void) +{ + //.TODO: +} + +struct secdp_sysfs *secdp_sysfs_get(struct device *dev, struct secdp_misc *sec) +{ + int rc = 0; + struct secdp_sysfs_private *sysfs; + struct secdp_sysfs *dp_sysfs; + + if (!dev || !sec) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto error; + } + + sysfs = devm_kzalloc(dev, sizeof(*sysfs), GFP_KERNEL); + if (!sysfs) { + rc = -EINVAL; + goto error; + } + + sysfs->dev = dev; + sysfs->sec = sec; + dp_sysfs = &sysfs->dp_sysfs; + + g_secdp_sysfs = sysfs; + return dp_sysfs; + +error: + return ERR_PTR(rc); +} + +void secdp_sysfs_put(struct secdp_sysfs *dp_sysfs) +{ + struct secdp_sysfs_private *sysfs; + + if (!dp_sysfs) + return; + + sysfs = container_of(dp_sysfs, struct secdp_sysfs_private, dp_sysfs); + devm_kfree(sysfs->dev, sysfs); + + g_secdp_sysfs = NULL; +} diff --git a/drivers/gpu/drm/msm/dp/secdp_sysfs.h b/drivers/gpu/drm/msm/dp/secdp_sysfs.h new file mode 100644 index 000000000000..ec17fb56100b --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_sysfs.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 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 { + + + + +}; + + + +int secdp_sysfs_init(void); +void secdp_sysfs_deinit(void); + + + +/** + * secdp_sysfs_get() - get the functionalities of dp test module + * + * + * return: a pointer to dp_link struct + */ +struct secdp_sysfs *secdp_sysfs_get(struct device *dev, struct secdp_misc *sec); + + +/** + * secdp_sysfs_put() - releases the dp test module's resources + * + * @dp_link: an instance of dp_link module + * + */ +void secdp_sysfs_put(struct secdp_sysfs *dp_sysfs); + + + + +#endif /*__SECDP_SYSFS_H*/ diff --git a/drivers/gpu/drm/msm/dp/secdp_unit_test.c b/drivers/gpu/drm/msm/dp/secdp_unit_test.c new file mode 100644 index 000000000000..8ded178f2634 --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_unit_test.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ + +#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) { + pr_err("fail to get connector\n"); + goto exit; + } + + table_size = ARRAY_SIZE(g_parsed_res); + + edid_ctrl = sde_edid_init(); + if (!edid_ctrl) { + pr_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); + pr_debug("_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) { + pr_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) { + + /* since 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) { + pr_err("count is not matched! parsed_res_cnt: %d, table_size: %d\n", + parsed_res_cnt, table_size); + goto exit; + } + + ret = true; +exit: + pr_info("returns %s\n", ret ? "true" : "false"); + return ret; +} diff --git a/drivers/gpu/drm/msm/dp/secdp_unit_test.h b/drivers/gpu/drm/msm/dp/secdp_unit_test.h new file mode 100644 index 000000000000..842ba89f2025 --- /dev/null +++ b/drivers/gpu/drm/msm/dp/secdp_unit_test.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017, 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*/ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index 9df191d5445a..94f9fcead947 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -30,12 +30,19 @@ #include "dsi_clk.h" #include "dsi_pwr.h" #include "dsi_catalog.h" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#include "ss_dsi_panel_common.h" +#endif #include "sde_dbg.h" #define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#define DSI_CTRL_TX_TO_MS 1000 +#else #define DSI_CTRL_TX_TO_MS 200 +#endif #define TO_ON_OFF(x) ((x) ? "ON" : "OFF") @@ -838,6 +845,11 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl, /* Get bits per pxl in desitnation format */ bpp = dsi_ctrl_pixel_format_to_bpp(host_cfg->dst_format); +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* Change MIPI Clock with dsi timing(porch,fps) change */ + /* ss_change_dyn_mipi_clk_timing(samsung_get_vdd()); */ +#endif + if (host_cfg->data_lanes & DSI_DATA_LANE_0) num_of_lanes++; if (host_cfg->data_lanes & DSI_DATA_LANE_1) @@ -1084,6 +1096,37 @@ int dsi_message_validate_tx_mode(struct dsi_ctrl *dsi_ctrl, return rc; } +#if defined(CONFIG_DISPLAY_SAMSUNG) +static void print_cmd_desc(const struct mipi_dsi_msg *msg) +{ + char buf[1024]; + int len = 0; + size_t i; + + /* Packet Info */ + len += snprintf(buf, sizeof(buf) - len, "%02x ", msg->type); + len += snprintf(buf + len, sizeof(buf) - len, "%02x ", + (msg->flags & MIPI_DSI_MSG_LASTCOMMAND) ? 1 : 0); /* Last bit */ + len += snprintf(buf + len, sizeof(buf) - len, "%02x ", msg->channel); + len += snprintf(buf + len, sizeof(buf) - len, "%02x ", + (unsigned int)msg->flags); + len += snprintf(buf + len, sizeof(buf) - len, "%02x ", 0); /* Delay */ + len += snprintf(buf + len, sizeof(buf) - len, "%02x ", + (unsigned int)msg->tx_len); + + /* Packet Payload */ + for (i = 0 ; i < msg->tx_len ; i++) { + len += snprintf(buf + len, sizeof(buf) - len, + "%02x ", msg->tx_buf[i]); + /* Break to prevent show too long command */ + if (i > 250) + break; + } + + LCD_INFO("(%02d) %s\n", (unsigned int)msg->tx_len, buf); +} +#endif + static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, const struct mipi_dsi_msg *msg, u32 flags) @@ -1099,6 +1142,12 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, u8 *cmdbuf; struct dsi_mode_info *timing; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd = ss_get_vdd(dsi_ctrl->cell_index); + if (vdd->debug_data->print_cmds) + print_cmd_desc(msg); +#endif + /* Select the tx mode to transfer the command */ dsi_message_setup_tx_mode(dsi_ctrl, msg->tx_len, &flags); @@ -2307,6 +2356,12 @@ static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl, /* enable back DSI interrupts */ if (dsi_ctrl->hw.ops.error_intr_ctrl) dsi_ctrl->hw.ops.error_intr_ctrl(&dsi_ctrl->hw, true); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + inc_dpui_u32_field_nolock(DPUI_KEY_QCT_DSIE, 1); + ss_get_vdd(dsi_ctrl->cell_index)->dsi_errors = error; +#endif + } /** diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h index 47009bfecc74..166459092ad2 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h @@ -59,6 +59,15 @@ /* max size supported for dsi cmd transfer using TPG */ #define DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE 64 +#if defined(CONFIG_DISPLAY_SAMSUNG) +/* max size supported for dsi cmd transfer using DMA */ +#ifdef CONFIG_SEC_A8SQLTE_PROJECT +#define DSI_CTRL_MAX_CMD_FET_MEMORY_SIZE 100 +#else +#define DSI_CTRL_MAX_CMD_FET_MEMORY_SIZE 200 +#endif +#endif + /** * enum dsi_channel_id - defines dsi channel id. * @DSI_CTRL_LEFT: DSI 0 channel diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c index 7139a51dc9ec..ce5e09a812ab 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c @@ -392,7 +392,12 @@ void dsi_ctrl_hw_cmn_setup_cmd_stream(struct dsi_ctrl_hw *ctrl, } /* HS Timer value */ +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* case 04431034, increase HS transmission timeout counter */ + DSI_W32(ctrl, DSI_HS_TIMER_CTRL, 0x3FFFF); +#else DSI_W32(ctrl, DSI_HS_TIMER_CTRL, 0x3FD08); +#endif stream_ctrl = (stride_final + 1) << 16; stream_ctrl |= (vc_id & 0x3) << 8; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h index a6ada733daaf..5c530cc316b4 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h @@ -268,7 +268,276 @@ enum dsi_cmd_set_type { DSI_CMD_SET_ROI, DSI_CMD_SET_TIMING_SWITCH, DSI_CMD_SET_POST_TIMING_SWITCH, - DSI_CMD_SET_MAX + DSI_CMD_SET_MAX, +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* set type for samsung display driver + * Please add type in proper boundary (TX, RX) + */ + + /* samsung CMD */ + SS_DSI_CMD_SET_START, + + /* TX */ + TX_CMD_START, + TX_TEMP_DSC, + TX_DISPLAY_ON, + TX_DISPLAY_OFF, + TX_BRIGHT_CTRL, + TX_MANUFACTURE_ID_READ_PRE, + TX_LEVEL0_KEY_ENABLE, + TX_LEVEL0_KEY_DISABLE, + TX_LEVEL1_KEY_ENABLE, + TX_LEVEL1_KEY_DISABLE, + TX_LEVEL2_KEY_ENABLE, + TX_LEVEL2_KEY_DISABLE, + TX_MDNIE_ADB_TEST, + TX_LPM_ON, + TX_LPM_OFF, + TX_LPM_AOD_ON, + TX_LPM_AOD_OFF, + TX_LPM_2NIT_CMD, + TX_LPM_10NIT_CMD, + TX_LPM_30NIT_CMD, + TX_LPM_40NIT_CMD, + TX_LPM_60NIT_CMD, + TX_ALPM_2NIT_CMD, + TX_ALPM_10NIT_CMD, + TX_ALPM_30NIT_CMD, + TX_ALPM_40NIT_CMD, + TX_ALPM_60NIT_CMD, + TX_ALPM_OFF, + TX_HLPM_2NIT_CMD, + TX_HLPM_10NIT_CMD, + TX_HLPM_30NIT_CMD, + TX_HLPM_40NIT_CMD, + TX_HLPM_60NIT_CMD, + TX_HLPM_OFF, + TX_LPM_BL_CMD, + TX_PACKET_SIZE, + TX_REG_READ_POS, + TX_MDNIE_TUNE, + TX_READING_MODE_TUNE, + TX_OSC_TE_FITTING, + TX_AVC_ON, + TX_LDI_FPS_CHANGE, + TX_HMT_ENABLE, + TX_HMT_DISABLE, + TX_HMT_LOW_PERSISTENCE_OFF_BRIGHT, + TX_HMT_REVERSE, + TX_HMT_FORWARD, + TX_FFC, + TX_DYNAMIC_FFC_SET, + TX_CABC_ON, + TX_CABC_OFF, + TX_TFT_PWM, + TX_BLIC_DIMMING, + TX_LDI_SET_VDD_OFFSET, + TX_LDI_SET_VDDM_OFFSET, + TX_HSYNC_ON, + TX_CABC_ON_DUTY, + TX_CABC_OFF_DUTY, + TX_COPR_ENABLE, + TX_COPR_DISABLE, + TX_COLOR_WEAKNESS_ENABLE, + TX_COLOR_WEAKNESS_DISABLE, + TX_ESD_RECOVERY_1, + TX_ESD_RECOVERY_2, + TX_MCD_ON, + TX_MCD_OFF, + TX_MCD_READ_RESISTANCE_PRE, /* For read real MCD R/L resistance */ + TX_MCD_READ_RESISTANCE, /* For read real MCD R/L resistance */ + TX_MCD_READ_RESISTANCE_POST, /* For read real MCD R/L resistance */ + TX_GRADUAL_ACL, + TX_HW_CURSOR, + TX_DYNAMIC_HLPM_ENABLE, + TX_DYNAMIC_HLPM_DISABLE, + TX_MULTIRES_FHD_TO_WQHD, + TX_MULTIRES_HD_TO_WQHD, + TX_MULTIRES_FHD, + TX_MULTIRES_HD, + TX_COVER_CONTROL_ENABLE, + TX_COVER_CONTROL_DISABLE, + TX_HBM_GAMMA, + TX_HBM_ETC, + TX_HBM_IRC, + TX_HBM_OFF, + TX_AID, + TX_AID_SUBDIVISION, + TX_PAC_AID_SUBDIVISION, + TX_ACL_ON, + TX_ACL_OFF, + TX_ELVSS, + TX_ELVSS_2, + TX_ELVSS_HIGH, + TX_ELVSS_MID, + TX_ELVSS_LOW, + TX_ELVSS_PRE, + TX_GAMMA, + TX_HMT_ELVSS, + TX_HMT_VINT, + TX_HMT_IRC, + TX_HMT_GAMMA, + TX_HMT_AID, + TX_ELVSS_LOWTEMP, + TX_ELVSS_LOWTEMP2, + TX_SMART_ACL_ELVSS, + TX_SMART_ACL_ELVSS_LOWTEMP, + TX_SMART_ACL_ELVSS_LOWTEMP2, + TX_VINT, + TX_IRC, + TX_IRC_SUBDIVISION, + TX_PAC_IRC_SUBDIVISION, + TX_IRC_OFF, + TX_MICRO_SHORT_TEST_ON, + TX_MICRO_SHORT_TEST_OFF, + TX_POC_CMD_START, /* START POC CMDS */ + TX_POC_WRITE_1BYTE, + TX_POC_ERASE, + TX_POC_ERASE1, + TX_POC_PRE_WRITE, + TX_POC_WRITE_CONTINUE, + TX_POC_WRITE_CONTINUE2, + TX_POC_WRITE_CONTINUE3, + TX_POC_WRITE_END, + TX_POC_POST_WRITE, + TX_POC_PRE_READ, + TX_POC_READ, + TX_POC_POST_READ, + TX_POC_REG_READ_POS, + TX_POC_CMD_END, /* END POC CMDS */ + TX_GCT_ENTER, + TX_GCT_MID, + TX_GCT_EXIT, + TX_DDI_RAM_IMG_DATA, + TX_GRAY_SPOT_TEST_ON, + TX_GRAY_SPOT_TEST_OFF, + TX_ISC_DEFECT_TEST_ON, + TX_ISC_DEFECT_TEST_OFF, + + /* SELF DISPLAY */ + TX_SELF_DISP_CMD_START, + TX_SELF_DISP_ON, + TX_SELF_DISP_OFF, + TX_SELF_TIME_SET, + TX_SELF_MOVE_ON, + TX_SELF_MOVE_ON_100, + TX_SELF_MOVE_ON_200, + TX_SELF_MOVE_ON_500, + TX_SELF_MOVE_ON_1000, + TX_SELF_MOVE_ON_DEBUG, + TX_SELF_MOVE_RESET, + TX_SELF_MOVE_OFF, + TX_SELF_MOVE_2C_SYNC_OFF, + TX_SELF_MASK_SET_PRE, + TX_SELF_MASK_SET_POST, + TX_SELF_MASK_SIDE_MEM_SET, + TX_SELF_MASK_ON, + TX_SELF_MASK_ON_FACTORY, + TX_SELF_MASK_OFF, + TX_SELF_MASK_IMAGE, + TX_SELF_ICON_SET_PRE, + TX_SELF_ICON_SET_POST, + TX_SELF_ICON_SIDE_MEM_SET, + TX_SELF_ICON_GRID, + TX_SELF_ICON_ON, + TX_SELF_ICON_ON_GRID_ON, + TX_SELF_ICON_ON_GRID_OFF, + TX_SELF_ICON_OFF_GRID_ON, + TX_SELF_ICON_OFF_GRID_OFF, + TX_SELF_ICON_GRID_2C_SYNC_OFF, + TX_SELF_ICON_OFF, + TX_SELF_ICON_IMAGE, + TX_SELF_BRIGHTNESS_ICON_ON, + TX_SELF_BRIGHTNESS_ICON_OFF, + TX_SELF_ACLOCK_SET_PRE, + TX_SELF_ACLOCK_SET_POST, + TX_SELF_ACLOCK_SIDE_MEM_SET, + TX_SELF_ACLOCK_ON, + TX_SELF_ACLOCK_TIME_UPDATE, + TX_SELF_ACLOCK_ROTATION, + TX_SELF_ACLOCK_OFF, + TX_SELF_ACLOCK_HIDE, + TX_SELF_ACLOCK_IMAGE, + TX_SELF_DCLOCK_SET_PRE, + TX_SELF_DCLOCK_SET_POST, + TX_SELF_DCLOCK_SIDE_MEM_SET, + TX_SELF_DCLOCK_ON, + TX_SELF_DCLOCK_BLINKING_ON, + TX_SELF_DCLOCK_BLINKING_OFF, + TX_SELF_DCLOCK_TIME_UPDATE, + TX_SELF_DCLOCK_OFF, + TX_SELF_DCLOCK_HIDE, + TX_SELF_DCLOCK_IMAGE, + TX_SELF_CLOCK_2C_SYNC_OFF, + TX_SELF_VIDEO_IMAGE, + TX_SELF_VIDEO_SIDE_MEM_SET, + TX_SELF_VIDEO_ON, + TX_SELF_VIDEO_OFF, + TX_SELF_PARTIAL_HLPM_SCAN_SET, + RX_SELF_DISP_DEBUG, + TX_SELF_DISP_CMD_END, + + /* FLASH GAMMA */ + TX_FLASH_GAMMA_PRE, + TX_FLASH_GAMMA, + TX_FLASH_GAMMA_POST, + + TX_ISC_DATA_THRESHOLD, + TX_STM_ENABLE, + TX_STM_DISABLE, + TX_GAMMA_MODE1_INTERPOLATION, + + TX_SPI_IF_SEL_ON, + TX_SPI_IF_SEL_OFF, + + TX_CCD_ON, + TX_CCD_OFF, + + TX_MTP_WRITE_SYSFS, + + TX_CMD_END, + + /* RX */ + RX_CMD_START, + RX_SMART_DIM_MTP, + RX_MANUFACTURE_ID, + RX_MANUFACTURE_ID0, + RX_MANUFACTURE_ID1, + RX_MANUFACTURE_ID2, + RX_MODULE_INFO, + RX_MANUFACTURE_DATE, + RX_DDI_ID, + RX_CELL_ID, + RX_OCTA_ID, + RX_RDDPM, + RX_MTP_READ_SYSFS, + RX_ELVSS, + RX_IRC, + RX_HBM, + RX_HBM2, + RX_MDNIE, + RX_LDI_DEBUG0, /* 0x0A : RDDPM */ + RX_LDI_DEBUG1, + RX_LDI_DEBUG2, /* 0xEE : ERR_FG */ + RX_LDI_DEBUG3, /* 0x0E : RDDSM */ + RX_LDI_DEBUG4, /* 0x05 : DSI_ERR */ + RX_LDI_DEBUG5, /* 0x0F : OTP loading error count */ + RX_LDI_DEBUG_LOGBUF, /* 0x9C : command log buffer */ + RX_LDI_DEBUG_PPS1, /* 0xA2 : PPS data (0x00 ~ 0x2C) */ + RX_LDI_DEBUG_PPS2, /* 0xA2 : PPS data (0x2d ~ 0x58)*/ + RX_LDI_LOADING_DET, + RX_LDI_FPS, + RX_POC_READ, + RX_POC_STATUS, + RX_POC_CHECKSUM, + RX_GCT_CHECKSUM, + RX_MCD_READ_RESISTANCE, /* For read real MCD R/L resistance */ + RX_FLASH_GAMMA, + RX_CCD_STATE, + RX_CMD_END, + + SS_DSI_CMD_SET_MAX, +#endif }; /** @@ -341,6 +610,22 @@ struct dsi_panel_cmd_set { u32 count; u32 ctrl_idx; struct dsi_cmd_desc *cmds; + +#if defined(CONFIG_DISPLAY_SAMSUNG) +#define SUPPORT_PANEL_REVISION 20 + int read_startoffset; + 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 }; /** diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c index 94b5c41aed28..f6f5f398a0f7 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c @@ -30,6 +30,9 @@ #include "dsi_drm.h" #include "dsi_clk.h" #include "dsi_pwr.h" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#include "ss_dsi_panel_common.h" +#endif #include "sde_dbg.h" #define to_dsi_display(x) container_of(x, struct dsi_display, host) @@ -48,9 +51,15 @@ static LIST_HEAD(dsi_display_list); static DEFINE_MUTEX(dsi_display_clk_mutex); +#if defined(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]; +#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]; +#endif static struct device_node *primary_active_node; static struct device_node *secondary_active_node; @@ -152,6 +161,10 @@ int dsi_display_set_backlight(void *display, u32 bl_lvl) panel = dsi_display->panel; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); if (!dsi_panel_initialized(panel)) { rc = -EINVAL; @@ -192,6 +205,9 @@ int dsi_display_set_backlight(void *display, u32 bl_lvl) error: mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif return rc; } @@ -287,6 +303,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 defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd; +#endif if (!cb_data) { pr_err("aspace cb called with invalid cb_data\n"); @@ -299,6 +318,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 defined(CONFIG_DISPLAY_SAMSUNG) + vdd = display->panel->panel_private; + mutex_lock(&vdd->cmd_lock); +#endif if (is_detach) { /* invalidate the stored iova */ @@ -334,6 +357,9 @@ static void dsi_display_aspace_cb_locked(void *cb_data, bool is_detach) } end: +#if defined(CONFIG_DISPLAY_SAMSUNG) + mutex_unlock(&vdd->cmd_lock); +#endif /* release panel_lock */ dsi_panel_release_panel_lock(display->panel); } @@ -395,6 +421,21 @@ static void dsi_display_register_te_irq(struct dsi_display *display) goto error; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* ss_check_inval_te_period_init() registers already TE gpio irq + * to monitor invalid TE period. + * case 1) status_mode = ESD_MODE_PANEL_TE: + * register TE irq for ESD check and no TE period monitor + * case 2) status_mode != ESD_MODE_PANEL_TE: + * no TE irq for ESD check and allow to monitor TE period + */ + if (display->panel->esd_config.status_mode != ESD_MODE_PANEL_TE) { + pr_info("%s: status_mode=%d, not ESD_MODE_PANEL_TE, skip!\n", + __func__, display->panel->esd_config.status_mode); + return; + } +#endif + init_completion(&display->esd_te_gate); rc = devm_request_irq(dev, gpio_to_irq(display->disp_te_gpio), @@ -644,8 +685,6 @@ static int dsi_display_status_reg_read(struct dsi_display *display) int rc = 0, i, cmd_channel_idx = DSI_CTRL_LEFT; struct dsi_display_ctrl *m_ctrl, *ctrl; - pr_debug(" ++\n"); - /* * Check the Panel DSI command channel. * If the cmd_channel is set, then we should @@ -702,7 +741,6 @@ static int dsi_display_status_bta_request(struct dsi_display *display) { int rc = 0; - pr_debug(" ++\n"); /* TODO: trigger SW BTA and wait for acknowledgment */ return rc; @@ -774,6 +812,11 @@ int dsi_display_check_status(void *display, bool te_check_override) rc = dsi_display_status_bta_request(dsi_display); } else if (status_mode == ESD_MODE_PANEL_TE) { rc = dsi_display_status_check_te(dsi_display); +#if defined(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 { pr_warn("unsupported check status mode\n"); panel->esd_config.esd_enabled = false; @@ -878,6 +921,25 @@ end: return rc; } +#if defined(CONFIG_DISPLAY_SAMSUNG) +void dsi_display_continuous_clk_ctrl(struct dsi_display *display, + bool enable) +{ + int i; + struct dsi_display_ctrl *ctrl; + + if (!display ) + return; + + pr_err("Force Clock HS Enable:%d\n", enable); + + for (i = 0; i < display->ctrl_count; i++) { + ctrl = &display->ctrl[i]; + dsi_ctrl_set_continuous_clk(ctrl->ctrl, enable); + } +} +#endif + static void _dsi_display_continuous_clk_ctrl(struct dsi_display *display, bool enable) { @@ -982,6 +1044,11 @@ int dsi_display_set_power(struct drm_connector *connector, return -EINVAL; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + pr_err("%s ++\n", power_mode == SDE_MODE_DPMS_LP1 ? "LP1" : + power_mode == SDE_MODE_DPMS_LP2 ? "LP2" : "NO_LP"); +#endif + switch (power_mode) { case SDE_MODE_DPMS_LP1: rc = dsi_panel_set_lp1(display->panel); @@ -993,6 +1060,12 @@ int dsi_display_set_power(struct drm_connector *connector, rc = dsi_panel_set_nolp(display->panel); break; } + +#if defined(CONFIG_DISPLAY_SAMSUNG) + pr_err("%s --\n", power_mode == SDE_MODE_DPMS_LP1 ? "LP1" : + power_mode == SDE_MODE_DPMS_LP2 ? "LP2" : "NO_LP"); +#endif + return rc; } @@ -2799,6 +2872,9 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, { struct dsi_display *display; int rc = 0, ret = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd; +#endif if (!host || !msg) { pr_err("Invalid params\n"); @@ -2854,6 +2930,37 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, int ctrl_idx = (msg->flags & MIPI_DSI_MSG_UNICAST) ? msg->ctrl : 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* To support MIPI RX, set flags to RX + * if msg has valid rx_len and rx_buf + */ + + u32 flags = DSI_CTRL_CMD_FETCH_MEMORY; + + vdd = display->panel->panel_private; + + if (msg->rx_len && msg->rx_buf) + flags |= DSI_CTRL_CMD_READ; + + rc = dsi_ctrl_cmd_transfer(display->ctrl[ctrl_idx].ctrl, msg, + flags); + /* TX: rc means error code, so rc=0 means no error. + * RX: rc means length of received data, so rc=0 means error. + */ + + if (((flags & DSI_CTRL_CMD_READ) && rc <= 0) || + (!(flags & DSI_CTRL_CMD_READ) && rc)) { + pr_err("[%s] cmd transfer failed, rc=%d, flags=%x cmd = %x\n", + ss_get_cmd_name(vdd->cmd_type), rc, flags, msg->tx_buf[0]); +/* + SDE_DBG_DUMP("sde", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", + "dsi1_phy", "vbif", "dbg_bus", + "vbif_dbg_bus", "panic"); +*/ + rc = -EINVAL; + goto error_disable_cmd_engine; + } +#else rc = dsi_ctrl_cmd_transfer(display->ctrl[ctrl_idx].ctrl, msg, DSI_CTRL_CMD_FETCH_MEMORY); if (rc) { @@ -2861,6 +2968,7 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, display->name, rc); goto error_disable_cmd_engine; } +#endif } error_disable_cmd_engine: @@ -4701,7 +4809,14 @@ static int dsi_display_sysfs_init(struct dsi_display *display) int rc = 0; struct device *dev = &display->pdev->dev; +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* In case of vidoe mode panel, dynamic mipi clock feature is required for the noise test. + * HW team keep requesting test binary changing mipi clock... + * Dynamic mipi clock feature allows the test by running time. + */ +#else if (display->panel->panel_mode == DSI_OP_CMD_MODE) +#endif rc = sysfs_create_group(&dev->kobj, &dynamic_dsi_clock_fs_attrs_group); pr_debug("[%s] dsi_display_sysfs_init:%d,panel mode:%d\n", @@ -5982,6 +6097,9 @@ error: static int dsi_display_pre_switch(struct dsi_display *display) { int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + pr_info("DMS : update dsi ctrl for new mode\n"); +#endif rc = dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_CORE_CLK, DSI_CLK_ON); @@ -6256,6 +6374,10 @@ int dsi_display_prepare(struct dsi_display *display) int rc = 0; struct dsi_display_mode *mode; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd = display->panel->panel_private; +#endif + if (!display) { pr_err("Invalid params\n"); return -EINVAL; @@ -6311,6 +6433,19 @@ int dsi_display_prepare(struct dsi_display *display) goto error_panel_post_unprep; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (vdd->dtsi_data.samsung_tcon_clk_on_support) { + LCD_INFO("increase core clk refcount to keep hs clock while display on\n"); + rc = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_ON); + if (rc) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + display->name, rc); + goto error_panel_post_unprep; + } + } +#endif + /* * If ULPS during suspend feature is enabled, then DSI PHY was * left on during suspend. In this case, we do not need to reset/init @@ -6364,6 +6499,18 @@ int dsi_display_prepare(struct dsi_display *display) display->name, rc); goto error_host_engine_off; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (vdd->dtsi_data.samsung_tcon_clk_on_support) { + LCD_INFO("increase link clk refcount to keep hs clock while display on\n"); + rc = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_LINK_CLK, DSI_CLK_ON); + if (rc) { + pr_err("[%s] failed to enable DSI link clocks, rc=%d\n", + display->name, rc); + goto error_host_engine_off; + } + } +#endif if (!display->is_cont_splash_enabled) { /* @@ -6404,6 +6551,10 @@ error_panel_post_unprep: error: mutex_unlock(&display->display_lock); SDE_EVT32(SDE_EVTLOG_FUNC_EXIT); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + LCD_INFO("--\n"); +#endif return rc; } @@ -6618,6 +6769,11 @@ int dsi_display_enable(struct dsi_display *display) int rc = 0; struct dsi_display_mode *mode; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd; + vdd = display->panel->panel_private; +#endif + if (!display || !display->panel) { pr_err("Invalid params\n"); return -EINVAL; @@ -6643,8 +6799,33 @@ int dsi_display_enable(struct dsi_display *display) return -EINVAL; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* Initialize samsung display driver in continuous splash mode, + * like smart dimming, mdnie, and etc. + */ + LCD_INFO("%s : is_cont_splash_enabled \n", __func__); + + mutex_lock(&display->display_lock); + + /* tft panel skip send init cmd in continuous splash mode */ + if(vdd->dtsi_data.tft_common_support) + vdd->skip_display_on_cmd = true; + + dsi_panel_enable(display->panel); + vdd->skip_display_on_cmd = false; + + mode = display->panel->cur_mode; + if (mode->priv_info->dsc_enabled) { + ss_set_exclusive_tx_lock_from_qct(display->panel->panel_private, true); + mode->priv_info->dsc.pic_width *= display->ctrl_count; + dsi_panel_update_pps(display->panel); + ss_set_exclusive_tx_lock_from_qct(display->panel->panel_private, false); + } + mutex_unlock(&display->display_lock); +#else display->panel->panel_initialized = true; pr_debug("cont splash enabled, display enable not required\n"); +#endif return 0; } @@ -6660,6 +6841,10 @@ int dsi_display_enable(struct dsi_display *display) goto error; } } else { +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (vdd->dtsi_data.samsung_dsi_force_clock_lane_hs) + dsi_display_continuous_clk_ctrl(display, true); +#endif rc = dsi_panel_enable(display->panel); if (rc) { pr_err("[%s] failed to enable DSI panel, rc=%d\n", @@ -6670,7 +6855,13 @@ int dsi_display_enable(struct dsi_display *display) if (mode->priv_info && mode->priv_info->dsc_enabled) { mode->priv_info->dsc.pic_width *= display->ctrl_count; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(display->panel->panel_private, true); +#endif rc = dsi_panel_update_pps(display->panel); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(display->panel->panel_private, false); +#endif if (rc) { pr_err("[%s] panel pps cmd update failed, rc=%d\n", display->name, rc); @@ -6714,6 +6905,10 @@ error_disable_panel: error: mutex_unlock(&display->display_lock); SDE_EVT32(SDE_EVTLOG_FUNC_EXIT); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + LCD_INFO("--\n"); +#endif return rc; } @@ -6806,6 +7001,9 @@ int dsi_display_disable(struct dsi_display *display) mutex_unlock(&display->display_lock); SDE_EVT32(SDE_EVTLOG_FUNC_EXIT); +#if defined(CONFIG_DISPLAY_SAMSUNG) + pr_err("--\n"); +#endif return rc; } @@ -6830,12 +7028,17 @@ int dsi_display_unprepare(struct dsi_display *display) { int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd = display->panel->panel_private; +#endif + if (!display) { pr_err("Invalid params\n"); return -EINVAL; } SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY); + mutex_lock(&display->display_lock); rc = dsi_display_wake_up(display); @@ -6848,6 +7051,11 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] panel unprepare failed, rc=%d\n", display->name, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (vdd->dtsi_data.samsung_dsi_force_clock_lane_hs) + dsi_display_continuous_clk_ctrl(display, false); +#endif + rc = dsi_display_ctrl_host_disable(display); if (rc) pr_err("[%s] failed to disable DSI host, rc=%d\n", @@ -6859,6 +7067,18 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] failed to disable Link clocks, rc=%d\n", display->name, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (vdd->dtsi_data.samsung_tcon_clk_on_support) { + pr_info("decrease link clk refcount to balance\n"); + + rc = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_LINK_CLK, DSI_CLK_OFF); + if (rc) + pr_err("[%s] failed to disable Link clocks, rc=%d\n", + display->name, rc); + } +#endif + /* Free up DSI ERROR event callback */ dsi_display_unregister_error_handler(display); @@ -6880,6 +7100,20 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] failed to disable DSI clocks, rc=%d\n", display->name, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (vdd->dtsi_data.samsung_tcon_clk_on_support) { + pr_info("decrease core clk refcount to balance\n"); + rc = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_OFF); + if (rc) + pr_err("[%s] failed to disable DSI clocks, rc=%d\n", + display->name, rc); + + /* wait for dsi discharging */ + usleep_range(2000, 2000); + } +#endif + /* destrory dsi isr set up */ dsi_display_ctrl_isr_configure(display, false); @@ -6890,6 +7124,9 @@ int dsi_display_unprepare(struct dsi_display *display) mutex_unlock(&display->display_lock); SDE_EVT32(SDE_EVTLOG_FUNC_EXIT); + + LCD_INFO("--\n"); + return rc; } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index f65f0f59e336..cb5ada1adb9b 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h @@ -601,6 +601,10 @@ int dsi_display_check_status(void *display, bool te_check_override); */ int dsi_display_cmd_transfer(void *display, const char *cmd_buffer, u32 cmd_buf_len); +#if defined(CONFIG_DISPLAY_SAMSUNG) +void dsi_display_continuous_clk_ctrl(struct dsi_display *display, + bool enable); +#endif /** * dsi_display_soft_reset() - perform a soft reset on DSI controller diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c index e2584cd9c14c..582e41386697 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c @@ -21,7 +21,13 @@ #include #include "dsi_panel.h" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#include "dsi_ctrl.h" +#endif #include "dsi_ctrl_hw.h" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#include "ss_dsi_panel_common.h" +#endif /** * topology is currently defined by a set of following 3 values: @@ -656,9 +662,22 @@ static void dsi_panel_exd_disable(struct dsi_panel *panel) gpio_set_value(e_config->switch_power, 0); } +#if defined(CONFIG_DISPLAY_SAMSUNG) +int dsi_panel_power_on(struct dsi_panel *panel) +#else static int dsi_panel_power_on(struct dsi_panel *panel) +#endif { int rc = 0; + struct samsung_display_driver_data *vdd = panel->panel_private; + +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (!ss_panel_attach_get(panel->panel_private)) { + pr_info("PBA booting, skip to power on panel\n"); + return 0; + } + +#endif rc = dsi_pwr_enable_regulator(&panel->power_info, true); if (rc) { @@ -666,13 +685,29 @@ static int dsi_panel_power_on(struct dsi_panel *panel) goto exit; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_bl_ic_en)) + LCD_ERR("no samsung_bl_ic_en function\n"); + else + vdd->panel_func.samsung_bl_ic_en(true); +#endif + rc = dsi_panel_set_pinctrl_state(panel, true); if (rc) { pr_err("[%s] failed to set pinctrl, rc=%d\n", panel->name, rc); goto error_disable_vregs; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (gpio_is_valid(vdd->dtsi_data.samsung_tcon_rdy_gpio)) { + LCD_DEBUG("skip panel reset while panel power on sequence \n"); + goto exit; + } +#endif + + LCD_ERR("%s dsi_panel_reset called \n", __func__); rc = dsi_panel_reset(panel); + if (rc) { pr_err("[%s] failed to reset panel, rc=%d\n", panel->name, rc); goto error_disable_gpio; @@ -684,6 +719,9 @@ static int dsi_panel_power_on(struct dsi_panel *panel) dsi_panel_exd_disable(panel); goto error_disable_gpio; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + vdd->reset_time_64 = ktime_to_ms(ktime_get()); +#endif goto exit; @@ -703,10 +741,22 @@ exit: return rc; } +#if defined(CONFIG_DISPLAY_SAMSUNG) +int dsi_panel_power_off(struct dsi_panel *panel) +#else static int dsi_panel_power_off(struct dsi_panel *panel) +#endif { int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd = panel->panel_private; + + if (!ss_panel_attach_get(panel->panel_private)) { + pr_info("PBA booting, skip to power off panel\n"); + return 0; + } +#endif dsi_panel_exd_disable(panel); if (gpio_is_valid(panel->reset_config.disp_en_gpio)) @@ -718,6 +768,13 @@ static int dsi_panel_power_off(struct dsi_panel *panel) if (gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio)) gpio_set_value(panel->reset_config.lcd_mode_sel_gpio, 0); +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_bl_ic_en)) + LCD_ERR("no samsung_bl_ic_en function\n"); + else + vdd->panel_func.samsung_bl_ic_en(false); +#endif + rc = dsi_panel_set_pinctrl_state(panel, false); if (rc) { pr_err("[%s] failed set pinctrl state, rc=%d\n", panel->name, @@ -730,17 +787,57 @@ static int dsi_panel_power_off(struct dsi_panel *panel) return rc; } + +#if defined(CONFIG_DISPLAY_SAMSUNG) +int dsi_panel_tx_cmd_set(struct dsi_panel *panel, + enum dsi_cmd_set_type 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 !defined(CONFIG_DISPLAY_SAMSUNG) struct dsi_display_mode *mode; +#endif const struct mipi_dsi_host_ops *ops = panel->host->ops; +#if defined(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; + + /* ss_get_cmds() gets proper QCT cmds or SS cmds for panel revision. */ + set = ss_get_cmds(vdd, type); + + cmds = set->cmds; + count = set->count; + state = set->state; + + pr_info("[SDE] cmd type (%d) state (%d)\n", type, state); + + /* Block to send mipi packet in case of that exclusive mode + * (exclusive_tx.enable) is enabled and + * the packet has no token (exclusive_pass). + * After exclusive mode released, send mipi packets. + */ + if (unlikely(vdd->exclusive_tx.enable && + !set->exclusive_pass)) { + pr_info("[SDE] %s: wait.. cmd[%d]=%s\n", __func__, + type, ss_get_cmd_name(type)); + wait_event(vdd->exclusive_tx.ex_tx_waitq, + !vdd->exclusive_tx.enable); + pr_info("[SDE] %s: pass, cmd[%d]=%s\n", __func__, + type, ss_get_cmd_name(type)); + } + mutex_lock(&vdd->cmd_lock); +#else if (!panel || !panel->cur_mode) return -EINVAL; @@ -752,7 +849,23 @@ static int dsi_panel_tx_cmd_set(struct dsi_panel *panel, cmds = mode->priv_info->cmd_sets[type].cmds; count = mode->priv_info->cmd_sets[type].count; state = mode->priv_info->cmd_sets[type].state; +#endif +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (cmds && (display->ctrl[cmds->msg.ctrl].ctrl->secure_mode)) { + for (i = 0 ; i < count ; i++) { + if (cmds->msg.tx_len > DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE) { + pr_err("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) { pr_debug("[%s] No commands to be sent for state(%d)\n", panel->name, type); @@ -760,24 +873,76 @@ static int dsi_panel_tx_cmd_set(struct dsi_panel *panel, } for (i = 0; i < count; i++) { + +#if defined(CONFIG_DISPLAY_SAMSUNG) + 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); + + if ((tot_tx_len > DSI_CTRL_MAX_CMD_FET_MEMORY_SIZE) || (i == count-1) || (cmds->post_wait_ms)) { + pr_debug("tot %zd is over than max || last cmd set, set last_command", + tot_tx_len); + cmds->last_command = true; + tot_tx_len = 0; + } + else{ +#if defined(CONFIG_SEC_GTS4LV_PROJECT) + cmds->last_command = true; +#else + cmds->last_command = false; +#endif + } + + if (vdd->dtsi_data.samsung_cmds_unicast) + cmds->msg.flags |= MIPI_DSI_MSG_UNICAST; +#endif + if (state == DSI_CMD_SET_STATE_LP) cmds->msg.flags |= MIPI_DSI_MSG_USE_LPM; if (cmds->last_command) cmds->msg.flags |= MIPI_DSI_MSG_LASTCOMMAND; + else + cmds->msg.flags &= ~MIPI_DSI_MSG_LASTCOMMAND; - len = ops->transfer(panel->host, &cmds->msg); - if (len < 0) { - rc = len; - pr_err("failed to set cmds(%d), rc=%d\n", type, rc); - goto error; +#if defined(CONFIG_DISPLAY_SAMSUNG) + while (retry-- >= 0) { +#endif + len = ops->transfer(panel->host, &cmds->msg); + if (len < 0) { + rc = len; + pr_err("failed to set cmds(%d), rc=%d\n", type, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + pr_err("transfer retry!(%d)\n", retry); + continue; +#endif + goto error; + } +#if defined(CONFIG_DISPLAY_SAMSUNG) + else { + retry = 5; + break; + } } + + if (retry < 0) + { + SDE_DBG_DUMP("sde", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", + "dsi1_phy", "vbif", "dbg_bus", + "vbif_dbg_bus", "panic"); + } +#endif if (cmds->post_wait_ms) usleep_range(cmds->post_wait_ms*1000, ((cmds->post_wait_ms*1000)+10)); cmds++; } error: +#if defined(CONFIG_DISPLAY_SAMSUNG) + mutex_unlock(&vdd->cmd_lock); +#endif return rc; } @@ -877,9 +1042,16 @@ static int dsi_panel_update_backlight(struct dsi_panel *panel, dsi = &panel->mipi_device; + LCD_INFO("bl_lvl = %d\n", bl_lvl); +#if defined(CONFIG_DISPLAY_SAMSUNG) + rc = ss_brightness_dcs(panel->panel_private, bl_lvl); + if (rc < 0) + pr_err("failed to update dcs backlight:%d\n", bl_lvl); +#else rc = mipi_dsi_dcs_set_display_brightness(dsi, bl_lvl); if (rc < 0) pr_err("failed to update dcs backlight:%d\n", bl_lvl); +#endif return rc; } @@ -1726,6 +1898,307 @@ static int dsi_panel_parse_phy_props(struct dsi_panel_phy_props *props, error: return rc; } + +#if defined(CONFIG_DISPLAY_SAMSUNG) +char *cmd_set_prop_map[SS_DSI_CMD_SET_MAX] = { + "qcom,mdss-dsi-pre-on-command", + "qcom,mdss-dsi-on-command", + "qcom,mdss-dsi-post-panel-on-command", + "qcom,mdss-dsi-pre-off-command", + "qcom,mdss-dsi-off-command", + "qcom,mdss-dsi-post-off-command", + "qcom,mdss-dsi-pre-res-switch", + "qcom,mdss-dsi-res-switch", + "qcom,mdss-dsi-post-res-switch", + "qcom,cmd-to-video-mode-switch-commands", + "qcom,cmd-to-video-mode-post-switch-commands", + "qcom,video-to-cmd-mode-switch-commands", + "qcom,video-to-cmd-mode-post-switch-commands", + "qcom,mdss-dsi-panel-status-command", + "qcom,mdss-dsi-lp1-command", + "qcom,mdss-dsi-lp2-command", + "qcom,mdss-dsi-nolp-command", + "PPS not parsed from DTSI, generated dynamically", + "ROI not parsed from DTSI, generated dynamically", + "qcom,mdss-dsi-timing-switch-command", + "qcom,mdss-dsi-post-mode-switch-on-command", + "DSI_CMD_SET_MAX not parsed from DTSI", + + /* prop map for samsung display driver + * Please property in proper boundary (TX, RX) + */ + + /* samsung feature */ + "SS_DSI_CMD_SET_START not parsed from DTSI", + + /* TX */ + "TX_CMD_START not parsed from DTSI", + "samsung,temp_dsc_tx_cmds_revA", + "samsung,display_on_tx_cmds_revA", + "samsung,display_off_tx_cmds_revA", + "samsung,brightness_tx_cmds_revA", + "samsung,manufacture_read_pre_tx_cmds_revA", + "samsung,level0_key_enable_tx_cmds_revA", + "samsung,level0_key_disable_tx_cmds_revA", + "samsung,level1_key_enable_tx_cmds_revA", + "samsung,level1_key_disable_tx_cmds_revA", + "samsung,level2_key_enable_tx_cmds_revA", + "samsung,level2_key_disable_tx_cmds_revA", + "TX_MDNIE_ADB_TEST not parsed from DTSI", + "samsung,lpm_on_tx_cmds_revA", + "samsung,lpm_off_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_aod_on_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_aod_off_tx_cmds_revA", + "samsung,lpm_2nit_tx_cmds_revA", + "samsung,lpm_10nit_tx_cmds_revA", + "samsung,lpm_30nit_tx_cmds_revA", + "samsung,lpm_40nit_tx_cmds_revA", + "samsung,lpm_60nit_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_2nit_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_10nit_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_30nit_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_40nit_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_60nit_tx_cmds_revA", + "samsung,lpm_ctrl_alpm_off_tx_cmds_revA", + "samsung,lpm_ctrl_hlpm_2nit_tx_cmds_revA", + "samsung,lpm_ctrl_hlpm_10nit_tx_cmds_revA", + "samsung,lpm_ctrl_hlpm_30nit_tx_cmds_revA", + "samsung,lpm_ctrl_hlpm_40nit_tx_cmds_revA", + "samsung,lpm_ctrl_hlpm_60nit_tx_cmds_revA", + "samsung,lpm_ctrl_hlpm_off_tx_cmds_revA", + "samsung,lpm_brightnes_tx_cmds_revA", + "samsung,packet_size_tx_cmds_revA", + "samsung,reg_read_pos_tx_cmds_revA", + "samsung,mdnie_tx_cmds_revA", + "samsung,reading_mode_tx_cmds_revA", + "samsung,osc_te_fitting_tx_cmds_revA", + "samsung,avc_on_revA", + "samsung,ldi_fps_change_tx_cmds_revA", + "samsung,hmt_enable_tx_cmds_revA", + "samsung,hmt_disable_tx_cmds_revA", + "TX_HMT_LOW_PERSISTENCE_OFF_BRIGHT not parsed from DTSI", + "samsung,hmt_reverse_tx_cmds_revA", + "samsung,hmt_forward_tx_cmds_revA", + "samsung,ffc_tx_cmds_revA", + "samsung,dyn_mipi_clk_ffc_cmds_revA", + "samsung,cabc_on_tx_cmds_revA", + "samsung,cabc_off_tx_cmds_revA", + "samsung,tft_pwm_tx_cmds_revA", + "samsung,blic_dimming_cmds_revA", + "samsung,panel_ldi_vdd_offset_write_cmds_revA", + "samsung,panel_ldi_vddm_offset_write_cmds_revA", + "samsung,hsync_on_tx_cmds_revA", + "samsung,cabc_on_duty_tx_cmds_revA", + "samsung,cabc_off_duty_tx_cmds_revA", + "samsung,copr_enable_tx_cmds_revA", + "samsung,copr_disable_tx_cmds_revA", + "samsung,ccb_on_tx_cmds_revA", + "samsung,ccb_off_tx_cmds_revA", + "samsung,esd_recovery_1_tx_cmds_revA", + "samsung,esd_recovery_2_tx_cmds_revA", + "samsung,mcd_on_tx_cmds_revA", + "samsung,mcd_off_tx_cmds_revA", + "samsung,mcd_read_resistantant_pre_tx_cmds_revA", /* For read real MCD R/L resistance */ + "samsung,mcd_read_resistantant_tx_cmds_revA", /* For read real MCD R/L resistance */ + "samsung,mcd_read_resistantant_post_tx_cmds_revA", /* For read real MCD R/L resistance */ + "samsung,gradual_acl_tx_cmds_revA", + "samsung,hw_cursor_tx_cmds_revA", + "samsung,dynamic_hlpm_enable_tx_cmds_revA", + "samsung,dynamic_hlpm_disable_tx_cmds_revA", + "samsung,panel_multires_fhd_to_wqhd_revA", + "samsung,panel_multires_hd_to_wqhd_revA", + "samsung,panel_multires_fhd_revA", + "samsung,panel_multires_hd_revA", + "samsung,panel_cover_control_enable_cmds_revA", + "samsung,panel_cover_control_disable_cmds_revA", + "samsung,hbm_gamma_tx_cmds_revA", + "samsung,hbm_etc_tx_cmds_revA", + "samsung,hbm_irc_tx_cmds_revA", + "samsung,hbm_off_tx_cmds_revA", + "samsung,aid_tx_cmds_revA", + "samsung,aid_subdivision_tx_cmds_revA", + "samsung,pac_aid_subdivision_tx_cmds_revA", + "samsung,acl_on_tx_cmds_revA", + "samsung,acl_off_tx_cmds_revA", + "samsung,elvss_tx_cmds_revA", + "samsung,elvss_2_tx_cmds_revA", + "samsung,elvss_high_tx_cmds_revA", + "samsung,elvss_mid_tx_cmds_revA", + "samsung,elvss_low_tx_cmds_revA", + "samsung,elvss_pre_tx_cmds_revA", + "samsung,gamma_tx_cmds_revA", + "samsung,hmt_elvss_tx_cmds_revA", + "samsung,hmt_vint_tx_cmds_revA", + "samsung,hmt_irc_tx_cmds_revA", + "samsung,hmt_gamma_tx_cmds_revA", + "samsung,hmt_aid_tx_cmds_revA", + "samsung,elvss_lowtemp_tx_cmds_revA", + "samsung,elvss_lowtemp2_tx_cmds_revA", + "samsung,smart_acl_elvss_tx_cmds_revA", + "samsung,smart_acl_elvss_lowtemp_tx_cmds_revA", + "samsung,smart_acl_elvss_lowtemp2_tx_cmds_revA", + "samsung,vint_tx_cmds_revA", + "samsung,irc_tx_cmds_revA", + "samsung,irc_subdivision_tx_cmds_revA", + "samsung,pac_irc_subdivision_tx_cmds_revA", + "samsung,irc_off_tx_cmds_revA", + "samsung,micro_short_test_on_tx_cmds_revA", + "samsung,micro_short_test_off_tx_cmds_revA", + "TX_POC_CMD_START not parsed from DTSI", + "samsung,poc_write_1byte_tx_cmds_revA", + "samsung,poc_erase_tx_cmds_revA", + "samsung,poc_erase1_tx_cmds_revA", + "samsung,poc_pre_write_tx_cmds_revA", + "samsung,poc_write_continue_tx_cmds_revA", + "samsung,poc_write_continue2_tx_cmds_revA", + "samsung,poc_write_continue3_tx_cmds_revA", + "samsung,poc_write_end_tx_cmds_revA", + "samsung,poc_post_write_tx_cmds_revA", + "samsung,poc_pre_read_tx_cmds_revA", + "samsung,poc_read_tx_cmds_revA", + "samsung,poc_post_read_tx_cmds_revA", + "samsung,reg_poc_read_pos_tx_cmds_revA", + "TX_POC_CMD_END not parsed from DTSI", + "samsung,gct_enter_tx_cmds_revA", + "samsung,gct_mid_tx_cmds_revA", + "samsung,gct_exit_tx_cmds_revA", + "TX_DDI_RAM_IMG_DATA not parsed from DTSI", + "samsung,gray_spot_test_on_tx_cmds_revA", + "samsung,gray_spot_test_off_tx_cmds_revA", + "samsung,isc_defect_test_on_tx_cmds_revA", + "samsung,isc_defect_test_off_tx_cmds_revA", + + /* self display */ + "TX_SELF_DISP_CMD_START not parsed from DTSI", + "samsung,self_dispaly_on", + "samsung,self_dispaly_off", + "samsung,self_time_set", + "samsung,self_move_on", + "samsung,self_move_on_100", + "samsung,self_move_on_200", + "samsung,self_move_on_500", + "samsung,self_move_on_1000", + "samsung,self_move_on_debug", + "samsung,self_move_reset", + "samsung,self_move_off", + "samsung,self_move_2c_sync_off", + "samsung,self_mask_setting_pre", + "samsung,self_mask_setting_post", + "samsung,self_mask_mem_setting", + "samsung,self_mask_on", + "samsung,self_mask_on_factory", + "samsung,self_mask_off", + "TX_SELF_MASK_IMAGE not parsed from DTSI", + "samsung,self_icon_setting_pre", + "samsung,self_icon_setting_post", + "samsung,self_icon_mem_setting", + "samsung,self_icon_grid", + "samsung,self_icon_on", + "samsung,self_icon_on_grid_on", + "samsung,self_icon_on_grid_off", + "samsung,self_icon_off_grid_on", + "samsung,self_icon_off_grid_off", + "samsung,self_icon_grid_2c_sync_off", + "samsung,self_icon_off", + "TX_SELF_ICON_IMAGE not parsed from DTSI", + "samsung,self_brightness_icon_on", + "samsung,self_brightness_icon_off", + "samsung,self_aclock_setting_pre", + "samsung,self_aclock_setting_post", + "samsung,self_aclock_sidemem_setting", + "samsung,self_aclock_on", + "samsung,self_aclock_time_update", + "samsung,self_aclock_rotation", + "samsung,self_aclock_off", + "samsung,self_aclock_hide", + "TX_SELF_ACLOCK_IMAGE not parsed from DTSI", + "samsung,self_dclock_setting_pre", + "samsung,self_dclock_setting_post", + "samsung,self_dclock_sidemem_setting", + "samsung,self_dclock_on", + "samsung,self_dclock_blinking_on", + "samsung,self_dclock_blinking_off", + "samsung,self_dclock_time_update", + "samsung,self_dclock_off", + "samsung,self_dclock_hide", + "TX_SELF_DCLOCK_IMAGE not parsed from DTSI", + "samsung,self_clock_2c_sync_off", + "TX_SELF_VIDEO_IMAGE not parsed from DTSI", + "samsung,self_video_mem_setting", + "samsung,self_video_on", + "samsung,self_video_of", + "samsung,self_partial_hlpm_scan_set", + "samsung,self_disp_debug_rx_cmds", + "TX_SELF_DISP_CMD_END not parsed from DTSI", + + /*FLASH GAMMA */ + "samsung,flash_gamma_pre_tx_cmds_revA", + "samsung,flash_gamma_tx_cmds_revA", + "samsung,flash_gamma_post_tx_cmds_revA ", + + /* ISC data threshold test */ + "samsung,isc_data_threshold_tx_cmds_revA", + + /* STM */ + "samsung,stm_enable_tx_cmds_revA", + "samsung,stm_disable_tx_cmds_revA", + + /* Gamma Mode 1 interpolation */ + "samsung,gamma_mode1_interpolation_test_tx_cmds_revA", + + /* SPI i/f sel on/off */ + "samsung,spi_if_sel_on_tx_cmds_revA", + "samsung,spi_if_sel_off_tx_cmds_revA", + + /* CCD test */ + "samsung,ccd_test_on_tx_cmds_revA", + "samsung,ccd_test_off_tx_cmds_revA", + + "samsung,mtp_write_sysfs_tx_cmds_revA", + + "TX_CMD_END not parsed from DTSI", + + /* RX */ + "RX_CMD_START not parsed from DTSI", + "samsung,smart_dimming_mtp_rx_cmds_revA", + "samsung,manufacture_id_rx_cmds_revA", + "samsung,manufacture_id0_rx_cmds_revA", + "samsung,manufacture_id1_rx_cmds_revA", + "samsung,manufacture_id2_rx_cmds_revA", + "samsung,module_info_rx_cmds_revA", + "samsung,manufacture_date_rx_cmds_revA", + "samsung,ddi_id_rx_cmds_revA", + "samsung,cell_id_rx_cmds_revA", + "samsung,octa_id_rx_cmds_revA", + "samsung,rddpm_rx_cmds_revA", + "samsung,mtp_read_sysfs_rx_cmds_revA", + "samsung,elvss_rx_cmds_revA", + "samsung,irc_rx_cmds_revA", + "samsung,hbm_rx_cmds_revA", + "samsung,hbm2_rx_cmds_revA", + "samsung,mdnie_read_rx_cmds_revA", + "samsung,ldi_debug0_rx_cmds_revA", + "samsung,ldi_debug1_rx_cmds_revA", + "samsung,ldi_debug2_rx_cmds_revA", + "samsung,ldi_debug3_rx_cmds_revA", + "samsung,ldi_debug4_rx_cmds_revA", + "samsung,ldi_debug5_rx_cmds_revA", + "samsung,ldi_debug_logbuf_rx_cmds_revA", + "samsung,ldi_debug_pps1_rx_cmds_revA", + "samsung,ldi_debug_pps2_rx_cmds_revA", + "samsung,ldi_loading_det_rx_cmds_revA", + "samsung,ldi_fps_rx_cmds_revA", + "samsung,poc_read_rx_cmds_revA", + "samsung,poc_status_rx_cmds_revA", + "samsung,poc_checksum_rx_cmds_revA", + "samsung,gct_checksum_rx_cmds_revA", + "samsung,mcd_read_resistantant_rx_cmds_revA", /* For read real MCD R/L resistance */ + "samsung,flash_gamma_rx_cmds_revA", + "samsung,ccd_state_rx_cmds_revA", + "RX_CMD_END not parsed from DTSI", + +}; +#else /* #if defined(CONFIG_DISPLAY_SAMSUNG) */ const char *cmd_set_prop_map[DSI_CMD_SET_MAX] = { "qcom,mdss-dsi-pre-on-command", "qcom,mdss-dsi-on-command", @@ -1748,7 +2221,10 @@ const char *cmd_set_prop_map[DSI_CMD_SET_MAX] = { "ROI not parsed from DTSI, generated dynamically", "qcom,mdss-dsi-timing-switch-command", "qcom,mdss-dsi-post-mode-switch-on-command", + "qcom,mdss-dsi-qsync-on-commands", + "qcom,mdss-dsi-qsync-off-commands", }; +#endif const char *cmd_set_state_map[DSI_CMD_SET_MAX] = { "qcom,mdss-dsi-pre-on-command-state", @@ -1914,6 +2390,26 @@ static int dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd, goto error_free_mem; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* samsung commands are set HS mode as default + * without cmd_set_state_map + */ + if (type >= SS_DSI_CMD_SET_START) { + if (ss_is_read_cmd(type)) { + /* send mipi rx packets in LP mode to prevent SoT error. + * case 03377897 + */ + cmd->state = DSI_CMD_SET_STATE_LP; + cmd->cmds[0].msg.rx_len = data[8+cmd->cmds[0].msg.tx_len-1]; + cmd->read_startoffset = data[9+cmd->cmds[0].msg.tx_len-1]; + } else { + cmd->state = DSI_CMD_SET_STATE_HS; + } + + return rc; + } +#endif + state = of_get_property(of_node, cmd_set_state_map[type], NULL); if (!state || !strcmp(state, "dsi_lp_mode")) { cmd->state = DSI_CMD_SET_STATE_LP; @@ -1934,6 +2430,100 @@ error: } +#if defined(CONFIG_DISPLAY_SAMSUNG) +/* ss_dsi_panel_parse_cmd_sets_sub: + * parse and save samsung mipi commands for each panel revision. + * If the panel revision has no dtsi data, + * set pointer to previous panel revision dtsi data. + */ +static int ss_dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd, + enum dsi_cmd_set_type type, + struct device_node *of_node) +{ + int rc = 0; + int rev; + char *map; + struct dsi_panel_cmd_set *set_rev[SUPPORT_PANEL_REVISION]; + int len; + char org_val; + + /* For revA */ + cmd->cmd_set_rev[0] = cmd; + + /* For revB ~ revT */ + map = cmd_set_prop_map[type]; + len = strlen(cmd_set_prop_map[type]); + + org_val = map[len - 1]; + + for (rev = 1; rev < SUPPORT_PANEL_REVISION; rev++) { + set_rev[rev] = kzalloc(sizeof(struct dsi_panel_cmd_set), + GFP_KERNEL); + if (map[len - 1] >= 'A' && map[len - 1] <= 'Z') + map[len - 1] = 'A' + rev; + + rc = dsi_panel_parse_cmd_sets_sub(set_rev[rev], type, of_node); + if (rc) { + /* If there is no data for the panel rev, + * copy previous panel rev data pointer. + */ + kfree(set_rev[rev]); + cmd->cmd_set_rev[rev] = cmd->cmd_set_rev[rev - 1]; + } else { + cmd->cmd_set_rev[rev] = set_rev[rev]; + } + } + + /* cmd_set_prop_map will be used only for debugging log. + * cmd_set_prop_map variable is located in ro. area. + * PMK feature checks ro data between vmlinux and runtime kernel ram, + * and reports error if thoes have different value. + * Reset revision value to original value to make same value in vmlinux. + */ + if (map[len - 1] >= 'A' && map[len - 1] <= 'Z') + map[len - 1] = org_val; + + return rc; +} + +int ss_dsi_panel_parse_cmd_sets(struct dsi_panel_cmd_set *cmd_sets, + struct device_node *of_node) +{ + int rc = 0; + struct dsi_panel_cmd_set *set; + u32 i; + struct device_node *node = NULL; + struct device_node *self_display_node = of_parse_phandle(of_node, + "ss,self_display", 0); + + for (i = SS_DSI_CMD_SET_START; i < SS_DSI_CMD_SET_MAX; i++) { + set = &cmd_sets[i]; + set->type = i; + set->count = 0; + + /* Self display has different device node */ + if (i >= TX_SELF_DISP_CMD_START && i <= TX_SELF_DISP_CMD_END) + { + node = self_display_node; + if (!node) + continue; + } + else + node = of_node; + + rc = dsi_panel_parse_cmd_sets_sub(set, i, node); + if (rc) + pr_debug("failed to parse set %d\n", i); + + ss_dsi_panel_parse_cmd_sets_sub(set, i, node); + } + + rc = 0; + return rc; +} + +#endif /* #if defined(CONFIG_DISPLAY_SAMSUNG)*/ + static int dsi_panel_parse_cmd_sets( struct dsi_display_mode_priv_info *priv_info, struct device_node *of_node) @@ -2360,6 +2950,18 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel, panel->bl_config.brightness_max_level = val; } +#if defined(CONFIG_DISPLAY_SAMSUNG) + rc = of_property_read_u32(of_node, "qcom,mdss-brightness-default-level", + &val); + if (rc) { + pr_debug("[%s] brigheness-default-level unspecified, defaulting to 25500\n", + panel->name); + panel->bl_config.bl_level = 25500; + } else { + panel->bl_config.bl_level = val; + } +#endif + if (panel->bl_config.type == DSI_BACKLIGHT_PWM) { rc = dsi_panel_parse_bl_pwm_config(&panel->bl_config, of_node); if (rc) { @@ -3190,6 +3792,10 @@ 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 defined(CONFIG_DISPLAY_SAMSUNG) + } else if (!strcmp(string, "irq_check")) { + esd_config->status_mode = ESD_MODE_PANEL_IRQ; +#endif } else if (!strcmp(string, "te_signal_check")) { if (panel->panel_mode == DSI_OP_CMD_MODE) { esd_config->status_mode = ESD_MODE_PANEL_TE; @@ -3239,6 +3845,9 @@ struct dsi_panel *dsi_panel_get(struct device *parent, { struct dsi_panel *panel; int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + u8 *esd_mode = NULL; +#endif panel = kzalloc(sizeof(*panel), GFP_KERNEL); if (!panel) @@ -3320,6 +3929,13 @@ struct dsi_panel *dsi_panel_get(struct device *parent, rc = dsi_panel_parse_esd_config(panel, of_node); if (rc) pr_debug("failed to parse esd config, rc=%d\n", rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + else { + if (panel->esd_config.status_mode == ESD_MODE_PANEL_IRQ) + esd_mode = "irq_check"; + pr_info("%s : ESD enabled with mode: %s\n", __func__, esd_mode); + } +#endif panel->type = DSI_PANEL; } else if (type == EXT_BRIDGE) { @@ -3378,6 +3994,16 @@ int dsi_panel_drv_init(struct dsi_panel *panel, dev->lanes = 4; panel->host = host; +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* In this point, vdd->panel_attach_status has invalid data. + * So, use panel name to verify PBA booting, + * intead of ss_panel_attach_get(). + */ + if (!strcmp(panel->name, "ss_dsi_panel_PBA_BOOTING_FHD")) { + pr_info("PBA booting, skip to get vreg, gpios\n"); + goto pba_booting; + } +#endif rc = dsi_panel_vreg_get(panel); if (rc) { pr_err("[%s] failed to get panel regulators, rc=%d\n", @@ -3413,6 +4039,10 @@ int dsi_panel_drv_init(struct dsi_panel *panel, goto error_exd_gpio_release; } +#if defined(CONFIG_DISPLAY_SAMSUNG) +pba_booting: + ss_panel_init(panel); +#endif goto exit; error_exd_gpio_release: @@ -3493,6 +4123,14 @@ int dsi_panel_get_mode_count(struct dsi_panel *panel, panel->num_timing_nodes = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (!strcmp(panel->name, "ss_dsi_panel_PBA_BOOTING_FHD")) { + pr_info("PBA booting, force to set num_timing_nodes 1\n"); + panel->num_timing_nodes = 1; + return 0; + } +#endif + timings_np = of_get_child_by_name(of_node, "qcom,mdss-dsi-display-timings"); if (!timings_np) { @@ -3589,6 +4227,51 @@ int dsi_panel_get_mode(struct dsi_panel *panel, prv_info = mode->priv_info; +#if defined(CONFIG_DISPLAY_SAMSUNG) // JUN_TEMP + if (!strcmp(panel->name, "ss_dsi_panel_PBA_BOOTING_FHD")) { + pr_info("PBA booting, skip DMS\n"); + + rc = dsi_panel_parse_timing(panel->parent, &mode->timing, panel->name, panel->panel_of_node); + if (rc) { + pr_err("failed to parse panel timing, rc=%d\n", rc); + goto parse_fail; + } + + rc = dsi_panel_parse_dsc_params(mode, panel->panel_of_node); + if (rc) { + pr_err("failed to parse dsc params, rc=%d\n", rc); + goto parse_fail; + } + + rc = dsi_panel_parse_topology(prv_info, panel->panel_of_node, + topology_override); + if (rc) { + pr_err("failed to parse panel topology, rc=%d\n", rc); + goto parse_fail; + } + + rc = dsi_panel_parse_cmd_sets(prv_info, panel->panel_of_node); + if (rc) { + pr_err("failed to parse command sets, rc=%d\n", rc); + goto parse_fail; + } + + rc = dsi_panel_parse_jitter_config(mode, panel->panel_of_node); + if (rc) + pr_err( + "failed to parse panel jitter config, rc=%d\n", rc); + + rc = dsi_panel_parse_phy_timing(mode, panel->panel_of_node); + if (rc) { + pr_err( + "failed to parse panel phy timings, rc=%d\n", rc); + goto parse_fail; + } + + goto done; + } +#endif + timings_np = of_get_child_by_name(panel->panel_of_node, "qcom,mdss-dsi-display-timings"); if (!timings_np) { @@ -3666,6 +4349,10 @@ int dsi_panel_get_host_cfg_for_mode(struct dsi_panel *panel, { int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd; +#endif + if (!panel || !mode || !config) { pr_err("invalid params\n"); return -EINVAL; @@ -3694,6 +4381,13 @@ int dsi_panel_get_host_cfg_for_mode(struct dsi_panel *panel, config->bit_clk_rate_hz = mode->timing.clk_rate_hz; } config->esc_clk_rate_hz = 19200000; + +#if defined(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; } @@ -3742,6 +4436,9 @@ int dsi_panel_update_pps(struct dsi_panel *panel) return 0; mutex_lock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_send_cmd(panel->panel_private, TX_LEVEL0_KEY_ENABLE); +#endif priv_info = panel->cur_mode->priv_info; @@ -3763,6 +4460,9 @@ int dsi_panel_update_pps(struct dsi_panel *panel) dsi_panel_destroy_cmds_packets_buf(set); error: +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_send_cmd(panel->panel_private, TX_LEVEL0_KEY_DISABLE); +#endif mutex_unlock(&panel->panel_lock); return rc; } @@ -3779,12 +4479,21 @@ int dsi_panel_set_lp1(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_LP1); if (rc) pr_err("[%s] failed to send DSI_CMD_SET_LP1 cmd, rc=%d\n", panel->name, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_panel_low_power_config(panel->panel_private, true); +#endif mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif return rc; } @@ -3800,12 +4509,22 @@ int dsi_panel_set_lp2(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_LP2); if (rc) pr_err("[%s] failed to send DSI_CMD_SET_LP2 cmd, rc=%d\n", panel->name, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_panel_low_power_config(panel->panel_private, true); +#endif mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } @@ -3821,19 +4540,59 @@ int dsi_panel_set_nolp(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_NOLP); if (rc) pr_err("[%s] failed to send DSI_CMD_SET_NOLP cmd, rc=%d\n", panel->name, rc); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_panel_low_power_config(panel->panel_private, false); +#endif mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } +#if defined(CONFIG_DISPLAY_SAMSUNG) +int wait_tcon_ready(struct dsi_panel *panel) +{ + struct samsung_display_driver_data *vdd = panel->panel_private; + + int i; + int max_wait_cnt = 100; /* max 100ms */ + + LCD_INFO("ANAPASS DDI: +: tcon_rdy val: %d\n", gpio_get_value(vdd->dtsi_data.samsung_tcon_rdy_gpio)); + for (i = 0; i < max_wait_cnt; i++) { + if (gpio_get_value(vdd->dtsi_data.samsung_tcon_rdy_gpio)) { + LCD_INFO("ANAPASS DDI: tcon_rdy becomes level high!!!\n"); + break; + } + usleep_range(1000, 1100); + } + LCD_INFO("ANAPASS DDI: -: tcon_rdy val: %d, wait_time: %d[ms]\n", gpio_get_value(vdd->dtsi_data.samsung_tcon_rdy_gpio), i); + + if (i == max_wait_cnt) + return false; + else + return true; +} +#endif + int dsi_panel_prepare(struct dsi_panel *panel) { int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd = panel->panel_private; +#endif + if (!panel) { pr_err("invalid params\n"); return -EINVAL; @@ -3842,6 +4601,10 @@ int dsi_panel_prepare(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); if (panel->lp11_init) { @@ -3853,6 +4616,24 @@ int dsi_panel_prepare(struct dsi_panel *panel) } } +#if defined(CONFIG_DISPLAY_SAMSUNG) + /* + There is panel power on requst. So panel reset executes here. + panle power on -> LP11 -> RESET -> wait tcon ready + + */ + + if (gpio_is_valid(vdd->dtsi_data.samsung_tcon_rdy_gpio)) { + /* panel reset here */ + dsi_panel_reset(panel); + + if (!wait_tcon_ready(panel)) + LCD_INFO("ANAPASS DDI tcon_rdy fail \n"); + + vdd->reset_time_64 = ktime_to_ms(ktime_get()); + } +#endif + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_PRE_ON); if (rc) { pr_err("[%s] failed to send DSI_CMD_SET_PRE_ON cmds, rc=%d\n", @@ -3862,6 +4643,10 @@ int dsi_panel_prepare(struct dsi_panel *panel) error: mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } @@ -3968,6 +4753,9 @@ int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx, } pr_debug("[%s] send roi x %d y %d w %d h %d\n", panel->name, roi->x, roi->y, roi->w, roi->h); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif mutex_lock(&panel->panel_lock); @@ -3977,6 +4765,9 @@ int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx, panel->name, rc); mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif dsi_panel_destroy_cmd_packets(set); @@ -3995,6 +4786,11 @@ int dsi_panel_switch(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + pr_info("DMS : send switch cmd\n"); + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_TIMING_SWITCH); @@ -4003,6 +4799,10 @@ int dsi_panel_switch(struct dsi_panel *panel) panel->name, rc); mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } @@ -4018,6 +4818,10 @@ int dsi_panel_post_switch(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_TIMING_SWITCH); @@ -4026,12 +4830,28 @@ int dsi_panel_post_switch(struct dsi_panel *panel) panel->name, rc); mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } int dsi_panel_enable(struct dsi_panel *panel) { int rc = 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + struct samsung_display_driver_data *vdd; +// struct dsi_display *display = container_of(panel->host, struct dsi_display, host); + + s64 cur_time_64; + + int wait_time_32; + s64 wait_time_64; + + int reset_delay_32; + s64 reset_delay_64; +#endif if (!panel) { pr_err("Invalid params\n"); @@ -4040,16 +4860,63 @@ int dsi_panel_enable(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; - mutex_lock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + vdd = panel->panel_private; + + if (vdd->dtsi_data.samsung_wait_after_reset_delay) { + reset_delay_32 = vdd->dtsi_data.samsung_wait_after_reset_delay; + reset_delay_64 = (s64)reset_delay_32; + + cur_time_64 = ktime_to_ms(ktime_get()); + + wait_time_64 = reset_delay_64 - (cur_time_64 - vdd->reset_time_64); + + /* To protect 64bit overflow & underflow */ + if (wait_time_64 <= 0) + wait_time_32 = 0; + else if (wait_time_64 > reset_delay_64) + wait_time_32 = reset_delay_32; + else + wait_time_32 = (s32)wait_time_64; + + if (wait_time_32 > 0) { + LCD_ERR("reset_delay:%d reset_t:%llu cur_t:%llu wait_t:%d start\n", reset_delay_32, vdd->reset_time_64, cur_time_64, wait_time_32); + usleep_range(wait_time_32*1000, wait_time_32*1000); + LCD_ERR("wait_t: %d end\n", wait_time_32); + } else + LCD_ERR("reset_delay:%d reset_t:%llu cur_t:%llu wait_t:%d skip\n", reset_delay_32, vdd->reset_time_64, cur_time_64, wait_time_32); + } + + ss_panel_on_pre(panel->panel_private); + + if(!vdd->skip_display_on_cmd) { + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_ON); + if (rc) { + pr_err("[%s] failed to send DSI_CMD_SET_ON cmds, rc=%d\n", + panel->name, rc); + } + } else { + LCD_INFO("skip to send DSI_CMD_SET_ON on kernel booting to avoid fifo error\n"); + } + +#else rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_ON); if (rc) { pr_err("[%s] failed to send DSI_CMD_SET_ON cmds, rc=%d\n", panel->name, rc); } +#endif panel->panel_initialized = true; + +#if defined(CONFIG_DISPLAY_SAMSUNG) + vdd->sleep_out_time_64 = ktime_to_ms(ktime_get()); + ss_panel_on_post(panel->panel_private); +#endif + mutex_unlock(&panel->panel_lock); + return rc; } @@ -4065,6 +4932,10 @@ int dsi_panel_post_enable(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_ON); @@ -4075,6 +4946,11 @@ int dsi_panel_post_enable(struct dsi_panel *panel) } error: mutex_unlock(&panel->panel_lock); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } @@ -4090,8 +4966,15 @@ int dsi_panel_pre_disable(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); +#if defined(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) { pr_err("[%s] failed to send DSI_CMD_SET_PRE_OFF cmds, rc=%d\n", @@ -4101,6 +4984,11 @@ int dsi_panel_pre_disable(struct dsi_panel *panel) error: mutex_unlock(&panel->panel_lock); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } @@ -4116,6 +5004,10 @@ int dsi_panel_disable(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); /* Avoid sending panel off commands when ESD recovery is underway */ @@ -4127,10 +5019,16 @@ int dsi_panel_disable(struct dsi_panel *panel) goto error; } } +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_panel_off_post(panel->panel_private); +#endif panel->panel_initialized = false; error: mutex_unlock(&panel->panel_lock); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif return rc; } @@ -4146,6 +5044,10 @@ int dsi_panel_unprepare(struct dsi_panel *panel) if (panel->type == EXT_BRIDGE) return 0; +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, true); +#endif + mutex_lock(&panel->panel_lock); rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_OFF); @@ -4157,6 +5059,11 @@ int dsi_panel_unprepare(struct dsi_panel *panel) error: mutex_unlock(&panel->panel_lock); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + ss_set_exclusive_tx_lock_from_qct(panel->panel_private, false); +#endif + return rc; } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h index 87023752e398..c53180d68631 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h @@ -138,6 +138,9 @@ enum esd_check_status_mode { ESD_MODE_REG_READ, ESD_MODE_SW_BTA, ESD_MODE_PANEL_TE, +#if defined(CONFIG_DISPLAY_SAMSUNG) + ESD_MODE_PANEL_IRQ, +#endif ESD_MODE_MAX }; @@ -212,6 +215,9 @@ struct dsi_panel { bool te_using_watchdog_timer; char dsc_pps_cmd[DSI_CMD_PPS_SIZE]; +#if defined(CONFIG_DISPLAY_SAMSUNG) + void *panel_private; +#endif enum dsi_dms_mode dms_mode; bool sync_broadcast_en; @@ -318,4 +324,12 @@ int dsi_panel_parse_esd_reg_read_configs(struct dsi_panel *panel, void dsi_panel_ext_bridge_put(struct dsi_panel *panel); +#if defined(CONFIG_DISPLAY_SAMSUNG) +int dsi_panel_power_on(struct dsi_panel *panel); +int dsi_panel_power_off(struct dsi_panel *panel); +int dsi_panel_tx_cmd_set(struct dsi_panel *panel, enum dsi_cmd_set_type type); +int ss_dsi_panel_parse_cmd_sets(struct dsi_panel_cmd_set *cmd_sets, + struct device_node *of_node); +#endif + #endif /* _DSI_PANEL_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c index 6c6286de99a1..1dda8722fe70 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c @@ -235,6 +235,9 @@ static void dsi_phy_hw_v3_0_lane_settings(struct dsi_phy_hw *phy, DSI_W32(phy, DSIPHY_LNX_OFFSET_TOP_CTRL(i), 0x0); DSI_W32(phy, DSIPHY_LNX_OFFSET_BOT_CTRL(i), 0x0); DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]); + + pr_debug("%s: [%d] %x %x %x \n", __func__, i, + cfg->lanecfg.lane[i][0], cfg->lanecfg.lane[i][1], cfg->lanecfg.lane[i][2]); } } diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 5e38236aeae9..e3c5a10c2377 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -24,6 +24,9 @@ #include "msm_gem.h" #include "msm_fence.h" #include "sde_trace.h" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#include "ss_dsi_panel_common.h" +#endif #define MULTIPLE_CONN_DETECTED(x) (x > 1) @@ -535,6 +538,9 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, } SDE_ATRACE_END("msm_enable"); } +#if defined(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. @@ -545,6 +551,9 @@ 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 defined(CONFIG_DISPLAY_SAMSUNG) + int ndx; +#endif drm_atomic_helper_wait_for_fences(dev, state, false); @@ -554,6 +563,20 @@ static void complete_commit(struct msm_commit *c) drm_atomic_helper_commit_planes(dev, state, 0); +#if defined(CONFIG_DISPLAY_SAMSUNG) + ndx = ss_get_vdd_ndx_from_state(state); + + /* TODO: check if _sde_encoder_trigger_start() is suitable + * for ss_callback called.. + */ + if (!kms->funcs->ss_callback) { + DRM_ERROR("No ss_callback function...\n"); + } else { + kms->funcs->ss_callback(ndx, SS_EVENT_PANEL_ESD_RECOVERY, NULL); + kms->funcs->ss_callback(ndx, SS_EVENT_FRAME_UPDATE_PRE, NULL); + } +#endif + msm_atomic_helper_commit_modeset_enables(dev, state); /* NOTE: _wait_for_vblanks() only waits for vblank on @@ -571,6 +594,14 @@ static void complete_commit(struct msm_commit *c) msm_atomic_wait_for_commit_done(dev, state); +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (!kms->funcs->ss_callback) { + DRM_ERROR("No ss_callback function...\n"); + } else { + kms->funcs->ss_callback(ndx, SS_EVENT_FRAME_UPDATE_POST, NULL); + } +#endif + drm_atomic_helper_cleanup_planes(dev, state); kms->funcs->complete_commit(kms, state); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 2dd43dd3722a..44926d9c1fdd 100755 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -59,6 +59,28 @@ static DEFINE_MUTEX(msm_release_lock); +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); + +#if defined(CONFIG_DISPLAY_SAMSUNG) +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) { struct msm_drm_private *priv = NULL; diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 4b504a62643d..a35594e8212a 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -798,6 +798,9 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev); void msm_fbdev_free(struct drm_device *dev); +#if defined(CONFIG_DISPLAY_SAMSUNG) +int __msm_drm_notifier_call_chain(unsigned long event, void *data); +#endif struct hdmi; #ifdef CONFIG_DRM_MSM_HDMI int msm_hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev, diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 828e0f64e2ee..3fbd1a3eac8a 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -66,6 +66,20 @@ #define MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422 (1<<9) /* Choose YUV420 format to display */ #define MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420 (1<<10) +#if defined(CONFIG_DISPLAY_SAMSUNG) +enum mdss_intf_events { + SS_EVENT_FRAME_UPDATE_POST = 0, + SS_EVENT_FRAME_UPDATE_PRE, + SS_EVENT_FB_EVENT_CALLBACK, + SS_EVENT_PANEL_ON, + SS_EVENT_PANEL_OFF, + SS_EVENT_PANEL_RECOVERY, + SS_EVENT_PANEL_ESD_RECOVERY, + SS_EVENT_CHECK_TE, + SS_EVENT_SDE_HW_CATALOG_INIT, + SS_EVENT_MAX, +}; +#endif /* As there are different display controller blocks depending on the * snapdragon version, the kms support is split out and the appropriate @@ -136,6 +150,11 @@ struct msm_kms_funcs { int (*cont_splash_config)(struct msm_kms *kms); /* check for continuous splash status */ bool (*check_for_splash)(struct msm_kms *kms); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + int (*ss_callback)(int display_ndx, + enum mdss_intf_events event, void *arg); +#endif }; struct msm_kms { diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c index ccd5e20a5c32..b4bf3cc8ae20 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -29,6 +29,10 @@ #include "msm_mmu.h" #include "sde_dbg.h" +#if defined(CONFIG_DISPLAY_SAMSUNG) +#include "ss_dsi_panel_common.h" +#endif + #ifndef SZ_4G #define SZ_4G (((size_t) SZ_1G) * 4) #endif @@ -302,6 +306,10 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, unsigned long attrs = 0x0; int ret; +#if defined(CONFIG_DISPLAY_SAMSUNG) + int retry_cnt; +#endif + if (!sgt || !client) { DRM_ERROR("sg table is invalid\n"); return -ENOMEM; @@ -312,6 +320,24 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, ret = msm_dma_map_sg_attrs(client->dev, sgt->sgl, sgt->nents, dir, dma_buf, attrs); + +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (!in_interrupt()) { + if (ret != sgt->nents) { + 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); + msm_dma_map_sg_attrs(client->dev, sgt->sgl, sgt->nents, dir, + dma_buf, attrs); + if (ret == sgt->nents) + break; + } + } + } +#endif + if (ret != sgt->nents) { DRM_ERROR("dma map sg failed\n"); return -ENOMEM; @@ -324,6 +350,10 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, dir, attrs, client->secure); } +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (sec_debug_is_enabled() && sgt && sgt->sgl) + ss_smmu_debug_map(SMMU_RT_DISPLAY_DEBUG, 0, NULL, sgt); +#endif return 0; } @@ -346,6 +376,10 @@ static void msm_smmu_unmap_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, client->secure); } +#if defined(CONFIG_DISPLAY_SAMSUNG) + if (sec_debug_is_enabled() && sgt && sgt->sgl) + ss_smmu_debug_unmap(SMMU_RT_DISPLAY_DEBUG, sgt); +#endif msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, dir, dma_buf); } @@ -516,7 +550,11 @@ static int msm_smmu_fault_handler(struct iommu_domain *domain, DRM_ERROR("SMMU device:%s", client->dev ? client->dev->kobj.name : ""); /* generate dump, but no panic */ +#if defined(CONFIG_DISPLAY_SAMSUNG) + SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic"); // case 03250922 +#else SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus"); +#endif /* * return -ENOSYS to allow smmu driver to dump out useful diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/Makefile b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/Makefile new file mode 100755 index 000000000000..07440dce7fc2 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/Makefile @@ -0,0 +1,11 @@ +# +# panel object +# namd : ss_dsi_panel_"DDI NAME"_"PANEL NAME".c +# + +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -Idrivers/gpu/drm/msm/dsi-staging +ccflags-y += -Idrivers/gpu/drm/msm/sde +ccflags-y += -Idrivers/gpu/drm/msm/samsung + +obj-y += ss_dsi_panel_ANA38401_AMSA05RB01.o +obj-y += ss_dsi_smart_dimming_ANA38401_AMSA05RB01.o diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/dsi_panel_ANA38401_AMSA05RB01_wqxga_octa_cmd.dtsi b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/dsi_panel_ANA38401_AMSA05RB01_wqxga_octa_cmd.dtsi new file mode 100755 index 000000000000..aa8a83ad54f8 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/dsi_panel_ANA38401_AMSA05RB01_wqxga_octa_cmd.dtsi @@ -0,0 +1,1208 @@ +/* Copyright (c) 2013-2014, 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. + */ + +&mdss_mdp { + ss_dsi_panel_ANA38401_AMSA05RB01_WQXGA: ss_dsi_panel_ANA38401_AMSA05RB01_WQXGA { + qcom,mdss-dsi-panel-name = "ss_dsi_panel_ANA38401_AMSA05RB01_WQXGA"; + label = "ss_dsi_panel_ANA38401_AMSA05RB01_WQXGA"; + + qcom,mdss-dsi-panel-width = <1280>; + qcom,mdss-dsi-panel-height = <1600>; + qcom,mdss-dsi-bpp = <24>; + + /*qcom,mdss-dsi-force-clk-lane-hs;*/ + /*qcom,cmd-sync-wait-broadcast;*/ + /*qcom,cmd-sync-wait-trigger;*/ + /*qcom,mdss-dsi-fbc-enable=*/ + /*qcom,mdss-dsi-fbc-bpp=*/ + /*qcom,mdss-dsi-fbc-packing=*/ + /*qcom,mdss-dsi-fbc-quant-error=*/ + /*qcom,mdss-dsi-fbc-bias=*/ + /*qcom,mdss-dsi-fbc-pat-mode=*/ + /*qcom,mdss-dsi-fbc-vlc-mode=*/ + /*qcom,mdss-dsi-fbc-bflc-mode=*/ + /*qcom,mdss-dsi-fbc-h-line-budget=*/ + /*qcom,mdss-dsi-fbc-budget-ctrl=*/ + /*qcom,mdss-dsi-fbc-block-budget=*/ + /*qcom,mdss-dsi-fbc-lossless-threshold=*/ + /*qcom,mdss-dsi-fbc-lossy-threshold=*/ + /*qcom,mdss-dsi-fbc-rgb-threshold=*/ + /*qcom,mdss-dsi-fbc-lossy-mode-idx=*/ + + qcom,mdss-dsi-h-back-porch = <108>; + qcom,mdss-dsi-h-front-porch = <212>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <6>; + qcom,mdss-dsi-v-front-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + + qcom,mdss-dsi-underflow-color = <0xFF>; + qcom,mdss-dsi-border-color = <0>; + + /*qcom,mdss-dsi-pan-enable-dynamic-fps=*/ + /*qcom,mdss-dsi-pan-fps-update=*/ + + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + /*qcom,mdss-dsi-bl-pmic-bank-select=*/ + /*qcom,mdss-dsi-bl-pmic-pwm-frequency=*/ + /*qcom,mdss-dsi-pwm-gpio=*/ + + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <355>; + qcom,mdss-brightness-max-level = <355>; + qcom,mdss-dsi-interleave-mode = <0>; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + /*qcom,mdss-dsi-force-clock-lane-hs;*/ + + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-te-pin-select = <1>; + + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2C>; + qcom,mdss-dsi-wr-mem-continue = <0x3C>; + + /* CMD mode panel doesn't need below stuff */ + /*qcom,mdss-dsi-h-sync-pulse=*/ + /*qcom,mdss-dsi-hfp-power-mode=*/ + /*qcom,mdss-dsi-hbp-power-mode=*/ + /*qcom,mdss-dsi-hsa-power-mode=*/ + /*qcom,mdss-dsi-last-line-interleave=*/ + /*qcom,mdss-dsi-bllp-eof-power-mode;*/ + /*qcom,mdss-dsi-bllp-power-mode;*/ + /*qcom,mdss-dsi-traffic-mode= "burst_mode";*/ + + qcom,mdss-dsi-pixel-packing = "loose"; + + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-t-clk-pre = <0x2F>; + qcom,mdss-dsi-t-clk-post = <0x0D>; + + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-clockrate = <898020000>; + qcom,mdss-dsi-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,mdss-pan-physical-width-dimension = <227>; + qcom,mdss-pan-physical-height-dimension = <142>; + + qcom,mdss-dsi-panel-mode-gpio-state = "invalid"; + qcom,mdss-dsi-reset-sequence = <0 70>, <1 0>; + + /*qcom,partial-update-enabled;*/ + /*qcom,partial-update-roi-merge;*/ + /*qcom,panel-roi-alignment=<8 8 1 1 1 1>;*/ + /*qcom,mdss-dsi-horizontal-line-idle*/ + + /* qcom,mdss-dsi-lp11-init; */ + qcom,mdss-dsi-init-delay-us = <1000>; + + qcom,mdss-dsi-rx-eot-ignore; + qcom,mdss-dsi-tx-eot-append; + + /*qcom,ulps-enabled*/ + /*qcom,suspend-ulps-enabled;*/ + /*qcom,mdss-dsi-panel-status-command=*/ + /*qcom,mdss-dsi-panel-status-command-mode="dsi_lp_mode";*/ + + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode="irq_check"; + qcom,mdss-dsi-panel-status-irq-trigger1="falling"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14880 15935 32435 + 16555 14945 30910 7790 3415>; + qcom,mdss-dsi-panel-peak-brightness = <5643000>; + qcom,mdss-dsi-panel-average-brightness = <2000000>; + qcom,mdss-dsi-panel-blackness-level = <6134>; + + /*qcom,mdss-dsi-panel-status-value = <1>;*/ + /*qcom,mdss-dsi-panel-orientation = "180";*/ + /*qcom,dynamic-mode-switch-enabled*/ + /*qcom,video-to-cmd-mode-switch-commands=*/ + /*qcom,cmd-to-video-mode-switch-commands=*/ +/* + * ************************************************************************************************************************************ + * + * + * Below parameters are samsung dependent thigs + * + * + * ************************************************************************************************************************************ + */ + samsung,panel-vendor = "SDC"; + samsung,disp-model = "AMSA05RB01"; + samsung,support_panel_max = <1>; + samsung,tcon-clk-on-support; + samsung,tcon-rdy-gpio = <&tlmm 79 0>; + samsung,cmds-unicast; /* if display->ctrl_count is 2, it broadcasts. To prevent to send mipi cmd thru mipi dsi1, set unicast flag */ + samsung,anapass-power-seq; + /*samsung,panel-lpm-enable;*/ + /*samsung,support_irc;*/ + /*samsung,elvss_interpolation_temperature = <(-15)>;*/ + /*samsung,support_factory_panel_swap;*/ + /*samsung,panel-extra-power-gpio1 = <&tlmm 117 0>;*/ + /*samsung ESD pin*/ + //samsung,esd-irq-gpio1 = <&tlmm 5 0>; + samsung,dsi_force_clock_lane_hs; + samsung,wait_after_sleep_out_delay = <220>; + + samsung,support_mdnie_lite; + /*samsung,line_split;*/ + + /* + the last byte in read commands represent READ LENGH, + like below READ LENGH = 0x21 + */ + + /* + * ************************************************************************************************************************ + * Tx + * ************************************************************************************************************************ + */ + + /* BRIGHTNESS_MAX_PACKET = 50 */ + samsung,brightness_tx_cmds_revA = [ // JUN_TEMP_1123 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + 39 01 00 00 00 00 22 83 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 80 80 80 80 80 80 80 00 80 80 00 + ]; + + samsung,packet_size_tx_cmds_revA = [ + 37 01 00 00 00 00 02 21 00 + ]; + + /* + * First parameters can be changed + * by function of samsung_nv_read() + * because of that means offset of + * read position + */ + samsung,reg_read_pos_tx_cmds_revA = [ + 15 01 00 00 00 00 02 B0 00 + ]; + + samsung,smart_dimming_mtp_rx_cmds_revA = [24 01 00 00 00 00 02 D8 08 08 5A]; /* Offset 0x5A */ + samsung,mdnie_read_rx_cmds_revA = [24 01 00 00 00 00 02 D8 04 04 7B]; /* Offset 0x7B : 123~ 126th para */ + samsung,elvss_rx_cmds_revA = [24 01 00 00 00 00 02 D2 01 01 70]; /* Offset 0x70 */ + samsung,mtp_read_sysfs_rx_cmds_revA = [24 01 00 00 00 00 02 00 00 00 00]; + + samsung,manufacture_id0_rx_cmds_revA = [06 01 00 00 00 00 01 DA 01 00]; + samsung,manufacture_id1_rx_cmds_revA = [06 01 00 00 00 00 01 DB 01 00]; + samsung,manufacture_id2_rx_cmds_revA = [06 01 00 00 00 00 01 DC 01 00]; + + samsung,manufacture_date_rx_cmds_revA = [24 01 00 00 00 00 02 D8 07 07 86]; /* Offset 0x86 */ + samsung,cell_id_rx_cmds_revA = [24 01 00 00 00 00 02 D8 07 07 86]; /* Offset 0x86 */ + samsung,support_gpara; + + samsung,display_on_tx_cmds_revA = [05 01 00 00 00 00 02 29 00]; + samsung,display_off_tx_cmds_revA = [05 01 00 00 64 00 02 28 00]; + + /* + * ************************************************************************************************************************ + * GAMMA + * ************************************************************************************************************************ + */ + samsung,gamma_tx_cmds_revA = [ + 39 01 00 00 00 00 22 /* Brightness condition set */ + 83 80 80 80 80 80 80 + 80 00 80 80 00 80 80 + 80 80 80 80 80 00 80 + 80 00 80 80 80 80 80 + 80 80 00 80 80 00 + + 15 01 00 00 00 00 02 B0 35 /* Global Para 53rd */ + 39 01 00 00 00 00 02 B2 01 /* Update key */ + ]; + + /* + * ************************************************************************************************************************ + * HBM + * ************************************************************************************************************************ + */ + samsung,enter_hbm_lux = <10000>; + samsung,hbm_rx_cmds_revA = [24 01 00 00 00 00 02 D4 08 08 5B]; /*Offset 91(0x5B) Para : HBM GAMMA */ + samsung,hbm2_rx_cmds_revA = [24 01 00 00 00 00 02 D8 01 01 81]; /*Offset 129(0x81) Para : HBM ELVSS */ + + samsung,hbm_gamma_tx_cmds_revA = [ + 39 01 00 00 00 00 22 /* Brightness condition set */ + 83 80 80 80 80 80 80 + 80 00 80 80 00 80 80 + 80 80 80 80 80 00 80 + 80 00 80 80 80 80 80 + 80 80 00 80 80 00 + ]; + + samsung,hbm_etc_tx_cmds_revA = [ + 39 01 00 00 00 00 04 90 00 00 0E /* AID 360 nit */ + 15 01 00 00 00 00 02 B0 70 /* Global Para 112nd */ + 39 01 00 00 00 00 02 B2 00 /* HBM ELVSS */ + 15 01 00 00 00 00 02 B0 67 /* Global Para 103rd */ + 39 01 00 00 00 00 02 B2 0F /* ELVSS dim offset : Table2 */ + 15 01 00 00 00 00 02 B0 4D /* Global Para 77th */ + 39 01 00 00 00 00 02 B2 05 /* 32 Frame Avg at ACL on */ + 15 01 00 00 00 00 02 B0 49 /* Global Para 73th */ + 39 01 00 00 00 00 02 B2 99 /* Start setting: 60% start */ + 15 01 00 00 00 00 02 B0 36 /* Global Para 54th */ + 39 01 00 00 00 00 02 B2 11 /* ACL 8% on */ + 15 01 00 00 00 00 02 B0 35 /* Global Para 53rd */ + 39 01 00 00 00 00 02 B2 01 /* Update key */ + ]; + + samsung,hbm_off_tx_cmds_revA = [ + 15 00 00 00 00 00 02 B0 70 /* Global Para 112nd */ + 39 00 00 00 00 00 02 B2 00 /* HBM ELVSS */ + ]; + + /* + * ************************************************************************************************************************ + * AID command list and mappings + * ************************************************************************************************************************ + */ + + samsung,aid_subdivision_tx_cmds_revA = [ + /* AOR bl */ + 39 01 00 00 00 00 04 90 01 00 8D /* 0 */ + 39 01 00 00 00 00 04 90 01 00 8A /* 1 */ + 39 01 00 00 00 00 04 90 01 00 87 /* 2 */ + 39 01 00 00 00 00 04 90 01 00 84 /* 3 */ + 39 01 00 00 00 00 04 90 01 00 80 /* 4 */ + 39 01 00 00 00 00 04 90 01 00 7E /* 5 */ + 39 01 00 00 00 00 04 90 01 00 7A /* 6 */ + 39 01 00 00 00 00 04 90 01 00 77 /* 7 */ + 39 01 00 00 00 00 04 90 01 00 74 /* 8 */ + 39 01 00 00 00 00 04 90 01 00 6F /* 9 */ + 39 01 00 00 00 00 04 90 01 00 6D /* 10 */ + 39 01 00 00 00 00 04 90 01 00 6A /* 11 */ + 39 01 00 00 00 00 04 90 01 00 66 /* 12 */ + 39 01 00 00 00 00 04 90 01 00 63 /* 13 */ + 39 01 00 00 00 00 04 90 01 00 5F /* 14 */ + 39 01 00 00 00 00 04 90 01 00 5C /* 15 */ + 39 01 00 00 00 00 04 90 01 00 55 /* 16 */ + 39 01 00 00 00 00 04 90 01 00 52 /* 17 */ + 39 01 00 00 00 00 04 90 01 00 4E /* 18 */ + 39 01 00 00 00 00 04 90 01 00 4A /* 19 */ + 39 01 00 00 00 00 04 90 01 00 44 /* 20 */ + 39 01 00 00 00 00 04 90 01 00 40 /* 21 */ + 39 01 00 00 00 00 04 90 01 00 3D /* 22 */ + 39 01 00 00 00 00 04 90 01 00 3A /* 23 */ + 39 01 00 00 00 00 04 90 01 00 36 /* 24 */ + 39 01 00 00 00 00 04 90 01 00 32 /* 25 */ + 39 01 00 00 00 00 04 90 01 00 30 /* 26 */ + 39 01 00 00 00 00 04 90 01 00 2F /* 27 */ + 39 01 00 00 00 00 04 90 01 00 2C /* 28 */ + 39 01 00 00 00 00 04 90 01 00 28 /* 29 */ + 39 01 00 00 00 00 04 90 01 00 24 /* 30 */ + 39 01 00 00 00 00 04 90 01 00 20 /* 31 */ + 39 01 00 00 00 00 04 90 01 00 1B /* 32 */ + 39 01 00 00 00 00 04 90 01 00 16 /* 33 */ + 39 01 00 00 00 00 04 90 01 00 12 /* 34 */ + 39 01 00 00 00 00 04 90 01 00 0E /* 35 */ + 39 01 00 00 00 00 04 90 01 00 0B /* 36 */ + 39 01 00 00 00 00 04 90 01 00 07 /* 37 */ + 39 01 00 00 00 00 04 90 01 00 02 /* 38 */ + 39 01 00 00 00 00 04 90 00 00 FC /* 39 */ + 39 01 00 00 00 00 04 90 00 00 F7 /* 40 */ + 39 01 00 00 00 00 04 90 00 00 F2 /* 41 */ + 39 01 00 00 00 00 04 90 00 00 EC /* 42 */ + 39 01 00 00 00 00 04 90 00 00 E6 /* 43 */ + 39 01 00 00 00 00 04 90 00 00 E1 /* 44 */ + 39 01 00 00 00 00 04 90 00 00 DB /* 45 */ + 39 01 00 00 00 00 04 90 00 00 D7 /* 46 */ + 39 01 00 00 00 00 04 90 00 00 D3 /* 47 */ + 39 01 00 00 00 00 04 90 00 00 CF /* 48 */ + 39 01 00 00 00 00 04 90 00 00 CA /* 49 */ + 39 01 00 00 00 00 04 90 00 00 C4 /* 50 */ + 39 01 00 00 00 00 04 90 00 00 BF /* 51 */ + 39 01 00 00 00 00 04 90 00 00 BA /* 52 */ + 39 01 00 00 00 00 04 90 00 00 B6 /* 53 */ + 39 01 00 00 00 00 04 90 00 00 B1 /* 54 */ + 39 01 00 00 00 00 04 90 00 00 AC /* 55 */ + 39 01 00 00 00 00 04 90 00 00 A8 /* 56 */ + 39 01 00 00 00 00 04 90 00 00 A3 /* 57 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 58 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 59 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 60 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 61 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 62 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 63 */ + 39 01 00 00 00 00 04 90 00 00 A6 /* 64 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 65 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 66 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 67 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 68 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 69 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 70 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 71 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 72 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 73 */ + 39 01 00 00 00 00 04 90 00 00 A2 /* 74 */ + 39 01 00 00 00 00 04 90 00 00 9F /* 75 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 76 */ + 39 01 00 00 00 00 04 90 00 00 A8 /* 77 */ + 39 01 00 00 00 00 04 90 00 00 A4 /* 78 */ + 39 01 00 00 00 00 04 90 00 00 A0 /* 79 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 80 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 81 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 82 */ + 39 01 00 00 00 00 04 90 00 00 A3 /* 83 */ + 39 01 00 00 00 00 04 90 00 00 A0 /* 84 */ + 39 01 00 00 00 00 04 90 00 00 9E /* 85 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 86 */ + 39 01 00 00 00 00 04 90 00 00 A9 /* 87 */ + 39 01 00 00 00 00 04 90 00 00 A6 /* 88 */ + 39 01 00 00 00 00 04 90 00 00 A3 /* 89 */ + 39 01 00 00 00 00 04 90 00 00 9F /* 90 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 91 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 92 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 93 */ + 39 01 00 00 00 00 04 90 00 00 A3 /* 94 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 95 */ + 39 01 00 00 00 00 04 90 00 00 9E /* 96 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 97 */ + 39 01 00 00 00 00 04 90 00 00 A9 /* 98 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 99 */ + 39 01 00 00 00 00 04 90 00 00 A4 /* 100 */ + 39 01 00 00 00 00 04 90 00 00 A2 /* 101 */ + 39 01 00 00 00 00 04 90 00 00 A0 /* 102 */ + 39 01 00 00 00 00 04 90 00 00 9E /* 103 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 104 */ + 39 01 00 00 00 00 04 90 00 00 A9 /* 105 */ + 39 01 00 00 00 00 04 90 00 00 A6 /* 106 */ + 39 01 00 00 00 00 04 90 00 00 A4 /* 107 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 108 */ + 39 01 00 00 00 00 04 90 00 00 9F /* 109 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 110 */ + 39 01 00 00 00 00 04 90 00 00 A9 /* 111 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 112 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 113 */ + 39 01 00 00 00 00 04 90 00 00 A3 /* 114 */ + 39 01 00 00 00 00 04 90 00 00 A1 /* 115 */ + 39 01 00 00 00 00 04 90 00 00 A0 /* 116 */ + 39 01 00 00 00 00 04 90 00 00 9E /* 117 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 118 */ + 39 01 00 00 00 00 04 90 00 00 A9 /* 119 */ + 39 01 00 00 00 00 04 90 00 00 A7 /* 120 */ + 39 01 00 00 00 00 04 90 00 00 A5 /* 121 */ + 39 01 00 00 00 00 04 90 00 00 A3 /* 122 */ + 39 01 00 00 00 00 04 90 00 00 A0 /* 123 */ + 39 01 00 00 00 00 04 90 00 00 9E /* 124 */ + 39 01 00 00 00 00 04 90 00 00 9C /* 125 */ + 39 01 00 00 00 00 04 90 00 00 9B /* 126 */ + 39 01 00 00 00 00 04 90 00 00 99 /* 127 */ + 39 01 00 00 00 00 04 90 00 00 98 /* 128 */ + 39 01 00 00 00 00 04 90 00 00 96 /* 129 */ + 39 01 00 00 00 00 04 90 00 00 94 /* 130 */ + 39 01 00 00 00 00 04 90 00 00 92 /* 131 */ + 39 01 00 00 00 00 04 90 00 00 90 /* 132 */ + 39 01 00 00 00 00 04 90 00 00 8E /* 133 */ + 39 01 00 00 00 00 04 90 00 00 8C /* 134 */ + 39 01 00 00 00 00 04 90 00 00 8A /* 135 */ + 39 01 00 00 00 00 04 90 00 00 88 /* 136 */ + 39 01 00 00 00 00 04 90 00 00 86 /* 137 */ + 39 01 00 00 00 00 04 90 00 00 84 /* 138 */ + 39 01 00 00 00 00 04 90 00 00 82 /* 139 */ + 39 01 00 00 00 00 04 90 00 00 80 /* 140 */ + 39 01 00 00 00 00 04 90 00 00 7E /* 141 */ + 39 01 00 00 00 00 04 90 00 00 7C /* 142 */ + 39 01 00 00 00 00 04 90 00 00 79 /* 143 */ + 39 01 00 00 00 00 04 90 00 00 77 /* 144 */ + 39 01 00 00 00 00 04 90 00 00 74 /* 145 */ + 39 01 00 00 00 00 04 90 00 00 72 /* 146 */ + 39 01 00 00 00 00 04 90 00 00 70 /* 147 */ + 39 01 00 00 00 00 04 90 00 00 6D /* 148 */ + 39 01 00 00 00 00 04 90 00 00 6A /* 149 */ + 39 01 00 00 00 00 04 90 00 00 68 /* 150 */ + 39 01 00 00 00 00 04 90 00 00 66 /* 151 */ + 39 01 00 00 00 00 04 90 00 00 64 /* 152 */ + 39 01 00 00 00 00 04 90 00 00 62 /* 153 */ + 39 01 00 00 00 00 04 90 00 00 60 /* 154 */ + 39 01 00 00 00 00 04 90 00 00 5E /* 155 */ + 39 01 00 00 00 00 04 90 00 00 5C /* 156 */ + 39 01 00 00 00 00 04 90 00 00 5A /* 157 */ + 39 01 00 00 00 00 04 90 00 00 58 /* 158 */ + 39 01 00 00 00 00 04 90 00 00 56 /* 159 */ + 39 01 00 00 00 00 04 90 00 00 54 /* 160 */ + 39 01 00 00 00 00 04 90 00 00 52 /* 161 */ + 39 01 00 00 00 00 04 90 00 00 50 /* 162 */ + 39 01 00 00 00 00 04 90 00 00 4D /* 163 */ + 39 01 00 00 00 00 04 90 00 00 4B /* 164 */ + 39 01 00 00 00 00 04 90 00 00 49 /* 165 */ + 39 01 00 00 00 00 04 90 00 00 47 /* 166 */ + 39 01 00 00 00 00 04 90 00 00 45 /* 167 */ + 39 01 00 00 00 00 04 90 00 00 42 /* 168 */ + 39 01 00 00 00 00 04 90 00 00 40 /* 169 */ + 39 01 00 00 00 00 04 90 00 00 3E /* 170 */ + 39 01 00 00 00 00 04 90 00 00 3C /* 171 */ + 39 01 00 00 00 00 04 90 00 00 39 /* 172 */ + 39 01 00 00 00 00 04 90 00 00 37 /* 173 */ + 39 01 00 00 00 00 04 90 00 00 35 /* 174 */ + 39 01 00 00 00 00 04 90 00 00 32 /* 175 */ + 39 01 00 00 00 00 04 90 00 00 30 /* 176 */ + 39 01 00 00 00 00 04 90 00 00 2D /* 177 */ + 39 01 00 00 00 00 04 90 00 00 2B /* 178 */ + 39 01 00 00 00 00 04 90 00 00 29 /* 179 */ + 39 01 00 00 00 00 04 90 00 00 26 /* 180 */ + 39 01 00 00 00 00 04 90 00 00 24 /* 181 */ + 39 01 00 00 00 00 04 90 00 00 22 /* 182 */ + 39 01 00 00 00 00 04 90 00 00 20 /* 183 */ + 39 01 00 00 00 00 04 90 00 00 1E /* 184 */ + 39 01 00 00 00 00 04 90 00 00 1C /* 185 */ + 39 01 00 00 00 00 04 90 00 00 1A /* 186 */ + 39 01 00 00 00 00 04 90 00 00 18 /* 187 */ + 39 01 00 00 00 00 04 90 00 00 16 /* 188 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 189 */ + 39 01 00 00 00 00 04 90 00 00 12 /* 190 */ + 39 01 00 00 00 00 04 90 00 00 10 /* 191 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 192 */ + 39 01 00 00 00 00 04 90 00 00 23 /* 193 */ + 39 01 00 00 00 00 04 90 00 00 21 /* 194 */ + 39 01 00 00 00 00 04 90 00 00 1F /* 195 */ + 39 01 00 00 00 00 04 90 00 00 1D /* 196 */ + 39 01 00 00 00 00 04 90 00 00 1B /* 197 */ + 39 01 00 00 00 00 04 90 00 00 19 /* 198 */ + 39 01 00 00 00 00 04 90 00 00 17 /* 199 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 200 */ + 39 01 00 00 00 00 04 90 00 00 12 /* 201 */ + 39 01 00 00 00 00 04 90 00 00 10 /* 202 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 203 */ + 39 01 00 00 00 00 04 90 00 00 23 /* 204 */ + 39 01 00 00 00 00 04 90 00 00 21 /* 205 */ + 39 01 00 00 00 00 04 90 00 00 1F /* 206 */ + 39 01 00 00 00 00 04 90 00 00 1D /* 207 */ + 39 01 00 00 00 00 04 90 00 00 1B /* 208 */ + 39 01 00 00 00 00 04 90 00 00 19 /* 209 */ + 39 01 00 00 00 00 04 90 00 00 17 /* 210 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 211 */ + 39 01 00 00 00 00 04 90 00 00 12 /* 212 */ + 39 01 00 00 00 00 04 90 00 00 10 /* 213 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 214 */ + 39 01 00 00 00 00 04 90 00 00 23 /* 215 */ + 39 01 00 00 00 00 04 90 00 00 21 /* 216 */ + 39 01 00 00 00 00 04 90 00 00 1F /* 217 */ + 39 01 00 00 00 00 04 90 00 00 1D /* 218 */ + 39 01 00 00 00 00 04 90 00 00 1B /* 219 */ + 39 01 00 00 00 00 04 90 00 00 19 /* 220 */ + 39 01 00 00 00 00 04 90 00 00 17 /* 221 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 222 */ + 39 01 00 00 00 00 04 90 00 00 12 /* 223 */ + 39 01 00 00 00 00 04 90 00 00 10 /* 224 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 225 */ + 39 01 00 00 00 00 04 90 00 00 20 /* 226 */ + 39 01 00 00 00 00 04 90 00 00 1E /* 227 */ + 39 01 00 00 00 00 04 90 00 00 1C /* 228 */ + 39 01 00 00 00 00 04 90 00 00 1A /* 229 */ + 39 01 00 00 00 00 04 90 00 00 18 /* 230 */ + 39 01 00 00 00 00 04 90 00 00 16 /* 231 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 232 */ + 39 01 00 00 00 00 04 90 00 00 12 /* 233 */ + 39 01 00 00 00 00 04 90 00 00 10 /* 234 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 235 */ + 39 01 00 00 00 00 04 90 00 00 20 /* 236 */ + 39 01 00 00 00 00 04 90 00 00 1E /* 237 */ + 39 01 00 00 00 00 04 90 00 00 1C /* 238 */ + 39 01 00 00 00 00 04 90 00 00 1A /* 239 */ + 39 01 00 00 00 00 04 90 00 00 18 /* 240 */ + 39 01 00 00 00 00 04 90 00 00 16 /* 241 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 242 */ + 39 01 00 00 00 00 04 90 00 00 12 /* 243 */ + 39 01 00 00 00 00 04 90 00 00 10 /* 244 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 245 */ + 39 01 00 00 00 00 04 90 00 00 28 /* 246 */ + 39 01 00 00 00 00 04 90 00 00 25 /* 247 */ + 39 01 00 00 00 00 04 90 00 00 22 /* 248 */ + 39 01 00 00 00 00 04 90 00 00 20 /* 249 */ + 39 01 00 00 00 00 04 90 00 00 1D /* 250 */ + 39 01 00 00 00 00 04 90 00 00 1A /* 251 */ + 39 01 00 00 00 00 04 90 00 00 17 /* 252 */ + 39 01 00 00 00 00 04 90 00 00 14 /* 253 */ + 39 01 00 00 00 00 04 90 00 00 11 /* 254 */ + 39 01 00 00 00 00 04 90 00 00 0E /* 255 */ + ]; + + /* + * ************************************************************************************************************************ + * ACL command list and mappings + * ************************************************************************************************************************ + */ + samsung,acl_on_tx_cmds_revA = [ + 15 01 00 00 00 00 02 B0 4D /* Global Para 77th */ + 39 01 00 00 00 00 02 B2 05 /* 32 Frame Avg at ACL on */ + 15 01 00 00 00 00 02 B0 49 /* Global Para 73th */ + 39 01 00 00 00 00 02 B2 99 /* Start setting: 60% start */ + 15 01 00 00 00 00 02 B0 36 /* Global Para 54th */ + 39 01 00 00 00 00 02 B2 12 /* ACL 15% on */ + ]; /* ACL on, B2 <- 0x05 */ + + samsung,acl_off_tx_cmds_revA = [ + 15 01 00 00 00 00 02 B0 4D /* Global Para 77th */ + 39 01 00 00 00 00 02 B2 04 /* 16 Frame Avg at ACL off */ + 15 01 00 00 00 00 02 B0 49 /* Global Para 73th */ + 39 01 00 00 00 00 02 B2 99 /* Start setting: 60% start */ + 15 01 00 00 00 00 02 B0 36 /* Global Para 54th */ + 39 01 00 00 00 00 02 B2 10 /* ACL off */ + ];/* ACL off, B2 <- 0x04 */ + + /* + * ************************************************************************************************************************ + * ELVSS(ACL Off) command list and mappings + * ************************************************************************************************************************ + */ + /* 0xB2 address 103rd parameter value*/ + samsung,elvss_pre_tx_cmds_revA = [15 01 00 00 00 00 02 B0 67]; /* 0x67 : 103rd Para */ + samsung,elvss_tx_cmds_revA = [ + 39 01 00 00 00 00 02 B2 0F /* 0: 360cd */ + 39 01 00 00 00 00 02 B2 0E /* 1: 333cd */ + 39 01 00 00 00 00 02 B2 0C /* 2: 316cd */ + 39 01 00 00 00 00 02 B2 0C /* 3: 300cd */ + 39 01 00 00 00 00 02 B2 0B /* 4: 282cd */ + 39 01 00 00 00 00 02 B2 0B /* 5: 265cd */ + 39 01 00 00 00 00 02 B2 0A /* 6: 249cd */ + 39 01 00 00 00 00 02 B2 0A /* 7: 234cd */ + 39 01 00 00 00 00 02 B2 0A /* 8: 220cd */ + 39 01 00 00 00 00 02 B2 09 /* 9: 207cd */ + 39 01 00 00 00 00 02 B2 09 /* 10: 195cd */ + 39 01 00 00 00 00 02 B2 09 /* 11: 183cd */ + 39 01 00 00 00 00 02 B2 09 /* 12: 172cd */ + 39 01 00 00 00 00 02 B2 08 /* 13: 162cd */ + 39 01 00 00 00 00 02 B2 08 /* 14: 152cd */ + 39 01 00 00 00 00 02 B2 06 /* 15: 143cd */ + 39 01 00 00 00 00 02 B2 06 /* 16: 134cd */ + 39 01 00 00 00 00 02 B2 06 /* 17: 126cd */ + 39 01 00 00 00 00 02 B2 05 /* 18: 119cd */ + 39 01 00 00 00 00 02 B2 05 /* 19: 111cd */ + 39 01 00 00 00 00 02 B2 05 /* 20: 105-60cd */ + 39 01 00 00 00 00 02 B2 05 /* 21: 56cd */ + 39 01 00 00 00 00 02 B2 06 /* 22: 53cd */ + 39 01 00 00 00 00 02 B2 06 /* 23: 50cd */ + 39 01 00 00 00 00 02 B2 06 /* 24: 47cd */ + 39 01 00 00 00 00 02 B2 06 /* 25: 44cd */ + 39 01 00 00 00 00 02 B2 08 /* 26: 41cd */ + 39 01 00 00 00 00 02 B2 08 /* 27: 39cd */ + 39 01 00 00 00 00 02 B2 08 /* 28: 37cd */ + 39 01 00 00 00 00 02 B2 09 /* 29: 34cd */ + 39 01 00 00 00 00 02 B2 09 /* 30: 32cd */ + 39 01 00 00 00 00 02 B2 0A /* 31: 30cd */ + 39 01 00 00 00 00 02 B2 0A /* 32: 29cd */ + 39 01 00 00 00 00 02 B2 0A /* 33: 27cd */ + 39 01 00 00 00 00 02 B2 0B /* 34: 25-21cd */ + 39 01 00 00 00 00 02 B2 0C /* 35: 20-16cd */ + 39 01 00 00 00 00 02 B2 0C /* 36: 15-11cd */ + 39 01 00 00 00 00 02 B2 0E /* 37: 10-5cd */ + 39 01 00 00 00 00 02 B2 0F /* 38: 4-2cd */ + ]; + + samsung,elvss_lowtemp_tx_cmds_revA = [ + 39 01 00 00 00 00 02 B2 05 /* 0: 360cd */ + 39 01 00 00 00 00 02 B2 04 /* 1: 333cd */ + 39 01 00 00 00 00 02 B2 02 /* 2: 316cd */ + 39 01 00 00 00 00 02 B2 02 /* 3: 300cd */ + 39 01 00 00 00 00 02 B2 01 /* 4: 282cd */ + 39 01 00 00 00 00 02 B2 01 /* 5: 265cd */ + 39 01 00 00 00 00 02 B2 00 /* 6: 249cd */ + 39 01 00 00 00 00 02 B2 00 /* 7: 234cd */ + 39 01 00 00 00 00 02 B2 00 /* 8: 220cd */ + 39 01 00 00 00 00 02 B2 00 /* 9: 207cd */ + 39 01 00 00 00 00 02 B2 00 /* 10: 195cd */ + 39 01 00 00 00 00 02 B2 00 /* 11: 183cd */ + 39 01 00 00 00 00 02 B2 00 /* 12: 172cd */ + 39 01 00 00 00 00 02 B2 00 /* 13: 162cd */ + 39 01 00 00 00 00 02 B2 00 /* 14: 152cd */ + 39 01 00 00 00 00 02 B2 00 /* 15: 143cd */ + 39 01 00 00 00 00 02 B2 00 /* 16: 134cd */ + 39 01 00 00 00 00 02 B2 00 /* 17: 126cd */ + 39 01 00 00 00 00 02 B2 00 /* 18: 119cd */ + 39 01 00 00 00 00 02 B2 00 /* 19: 111cd */ + 39 01 00 00 00 00 02 B2 00 /* 20: 105-60cd */ + 39 01 00 00 00 00 02 B2 00 /* 21: 56cd */ + 39 01 00 00 00 00 02 B2 00 /* 22: 53cd */ + 39 01 00 00 00 00 02 B2 00 /* 23: 50cd */ + 39 01 00 00 00 00 02 B2 00 /* 24: 47cd */ + 39 01 00 00 00 00 02 B2 00 /* 25: 44cd */ + 39 01 00 00 00 00 02 B2 00 /* 26: 41cd */ + 39 01 00 00 00 00 02 B2 00 /* 27: 39cd */ + 39 01 00 00 00 00 02 B2 00 /* 28: 37cd */ + 39 01 00 00 00 00 02 B2 00 /* 29: 34cd */ + 39 01 00 00 00 00 02 B2 00 /* 30: 32cd */ + 39 01 00 00 00 00 02 B2 00 /* 31: 30cd */ + 39 01 00 00 00 00 02 B2 00 /* 32: 29cd */ + 39 01 00 00 00 00 02 B2 00 /* 33: 27cd */ + 39 01 00 00 00 00 02 B2 01 /* 34: 25-21cd */ + 39 01 00 00 00 00 02 B2 02 /* 35: 20-16cd */ + 39 01 00 00 00 00 02 B2 02 /* 36: 15-11cd */ + 39 01 00 00 00 00 02 B2 04 /* 37: 10-5cd */ + 39 01 00 00 00 00 02 B2 05 /* 38: 4-2cd */ + ]; + + samsung,elvss_lowtemp2_tx_cmds_revA = [ + 39 01 00 00 00 00 02 B2 00 /* 0: 360cd */ + 39 01 00 00 00 00 02 B2 00 /* 1: 333cd */ + 39 01 00 00 00 00 02 B2 00 /* 2: 316cd */ + 39 01 00 00 00 00 02 B2 00 /* 3: 300cd */ + 39 01 00 00 00 00 02 B2 00 /* 4: 282cd */ + 39 01 00 00 00 00 02 B2 00 /* 5: 265cd */ + 39 01 00 00 00 00 02 B2 00 /* 6: 249cd */ + 39 01 00 00 00 00 02 B2 00 /* 7: 234cd */ + 39 01 00 00 00 00 02 B2 00 /* 8: 220cd */ + 39 01 00 00 00 00 02 B2 00 /* 9: 207cd */ + 39 01 00 00 00 00 02 B2 00 /* 10: 195cd */ + 39 01 00 00 00 00 02 B2 00 /* 11: 183cd */ + 39 01 00 00 00 00 02 B2 00 /* 12: 172cd */ + 39 01 00 00 00 00 02 B2 00 /* 13: 162cd */ + 39 01 00 00 00 00 02 B2 00 /* 14: 152cd */ + 39 01 00 00 00 00 02 B2 00 /* 15: 143cd */ + 39 01 00 00 00 00 02 B2 00 /* 16: 134cd */ + 39 01 00 00 00 00 02 B2 00 /* 17: 126cd */ + 39 01 00 00 00 00 02 B2 00 /* 18: 119cd */ + 39 01 00 00 00 00 02 B2 00 /* 19: 111cd */ + 39 01 00 00 00 00 02 B2 00 /* 20: 105-60cd */ + 39 01 00 00 00 00 02 B2 00 /* 21: 56cd */ + 39 01 00 00 00 00 02 B2 00 /* 22: 53cd */ + 39 01 00 00 00 00 02 B2 00 /* 23: 50cd */ + 39 01 00 00 00 00 02 B2 00 /* 24: 47cd */ + 39 01 00 00 00 00 02 B2 00 /* 25: 44cd */ + 39 01 00 00 00 00 02 B2 00 /* 26: 41cd */ + 39 01 00 00 00 00 02 B2 00 /* 27: 39cd */ + 39 01 00 00 00 00 02 B2 00 /* 28: 37cd */ + 39 01 00 00 00 00 02 B2 00 /* 29: 34cd */ + 39 01 00 00 00 00 02 B2 00 /* 30: 32cd */ + 39 01 00 00 00 00 02 B2 00 /* 31: 30cd */ + 39 01 00 00 00 00 02 B2 00 /* 32: 29cd */ + 39 01 00 00 00 00 02 B2 00 /* 33: 27cd */ + 39 01 00 00 00 00 02 B2 00 /* 34: 25-21cd */ + 39 01 00 00 00 00 02 B2 00 /* 35: 20-16cd */ + 39 01 00 00 00 00 02 B2 00 /* 36: 15-11cd */ + 39 01 00 00 00 00 02 B2 00 /* 37: 10-5cd */ + 39 01 00 00 00 00 02 B2 00 /* 38: 4-2cd */ + ]; + + samsung,elvss_2_tx_cmds_revA = [ + 15 01 00 00 00 00 02 B0 67 + 39 01 00 00 00 00 02 B2 00 + 15 01 00 00 00 00 02 B0 70 + 39 01 00 00 00 00 02 B2 00 + ]; + + /* bl_level, Index in elvss command list */ + samsung,elvss_map_table_revA = < + /* candela elvss_tx_cmd_id */ + 2 38 + 3 38 + 4 38 + 5 37 + 6 37 + 7 37 + 8 37 + 9 37 + 10 37 + 11 36 + 12 36 + 13 36 + 14 36 + 15 36 + 16 35 + 17 35 + 19 35 + 20 35 + 21 34 + 22 34 + 24 34 + 25 34 + 27 33 + 29 32 + 30 31 + 32 30 + 34 29 + 37 28 + 39 27 + 41 26 + 44 25 + 47 24 + 50 23 + 53 22 + 56 21 + 60 20 + 64 20 + 68 20 + 72 20 + 77 20 + 82 20 + 87 20 + 93 20 + 98 20 + 105 20 + 111 19 + 119 18 + 126 17 + 134 16 + 143 15 + 152 14 + 162 13 + 172 12 + 183 11 + 195 10 + 207 9 + 220 8 + 234 7 + 249 6 + 265 5 + 282 4 + 300 3 + 316 2 + 333 1 + 360 0 + >; + + /* + * ************************************************************************************************************************ + * SMART ACL(ACL On) ELVSS command list and mappings + * ************************************************************************************************************************ + */ + + /* 0xB2 address 103rd parameter value*/ + samsung,smart_acl_elvss_tx_cmds_revA = [ + 39 01 00 00 00 00 02 B2 0F /* 0: 360cd */ + 39 01 00 00 00 00 02 B2 10 /* 1: 333cd */ + 39 01 00 00 00 00 02 B2 11 /* 2: 316cd */ + 39 01 00 00 00 00 02 B2 12 /* 3: 300cd */ + 39 01 00 00 00 00 02 B2 13 /* 4: 282cd */ + 39 01 00 00 00 00 02 B2 13 /* 5: 265cd */ + 39 01 00 00 00 00 02 B2 14 /* 6: 249cd */ + 39 01 00 00 00 00 02 B2 14 /* 7: 234cd */ + 39 01 00 00 00 00 02 B2 14 /* 8: 220cd */ + 39 01 00 00 00 00 02 B2 15 /* 9: 207cd */ + 39 01 00 00 00 00 02 B2 15 /* 10: 195cd */ + 39 01 00 00 00 00 02 B2 15 /* 11: 183cd */ + 39 01 00 00 00 00 02 B2 15 /* 12: 172cd */ + 39 01 00 00 00 00 02 B2 16 /* 13: 162cd */ + 39 01 00 00 00 00 02 B2 16 /* 14: 152cd */ + 39 01 00 00 00 00 02 B2 17 /* 15: 143cd */ + 39 01 00 00 00 00 02 B2 18 /* 16: 134cd */ + 39 01 00 00 00 00 02 B2 18 /* 17: 126cd */ + 39 01 00 00 00 00 02 B2 19 /* 18: 119cd */ + 39 01 00 00 00 00 02 B2 19 /* 19: 111cd */ + 39 01 00 00 00 00 02 B2 19 /* 20: 105-60cd */ + 39 01 00 00 00 00 02 B2 19 /* 21: 56cd */ + 39 01 00 00 00 00 02 B2 18 /* 22: 53cd */ + 39 01 00 00 00 00 02 B2 18 /* 23: 50cd */ + 39 01 00 00 00 00 02 B2 17 /* 24: 47cd */ + 39 01 00 00 00 00 02 B2 17 /* 25: 44cd */ + 39 01 00 00 00 00 02 B2 16 /* 26: 41cd */ + 39 01 00 00 00 00 02 B2 16 /* 27: 39cd */ + 39 01 00 00 00 00 02 B2 16 /* 28: 37cd */ + 39 01 00 00 00 00 02 B2 15 /* 29: 34cd */ + 39 01 00 00 00 00 02 B2 15 /* 30: 32cd */ + 39 01 00 00 00 00 02 B2 14 /* 31: 30cd */ + 39 01 00 00 00 00 02 B2 14 /* 32: 29cd */ + 39 01 00 00 00 00 02 B2 14 /* 33: 27cd */ + 39 01 00 00 00 00 02 B2 13 /* 34: 25-21cd */ + 39 01 00 00 00 00 02 B2 12 /* 35: 20-16cd */ + 39 01 00 00 00 00 02 B2 11 /* 36: 15-11cd */ + 39 01 00 00 00 00 02 B2 10 /* 37: 10-5cd */ + 39 01 00 00 00 00 02 B2 0F /* 38: 4-2cd */ + ]; + + samsung,smart_acl_elvss_lowtemp_tx_cmds_revA = [ + 39 01 00 00 00 00 02 B2 05 /* 0: 360cd */ + 39 01 00 00 00 00 02 B2 06 /* 1: 333cd */ + 39 01 00 00 00 00 02 B2 07 /* 2: 316cd */ + 39 01 00 00 00 00 02 B2 08 /* 3: 300cd */ + 39 01 00 00 00 00 02 B2 09 /* 4: 282cd */ + 39 01 00 00 00 00 02 B2 09 /* 5: 265cd */ + 39 01 00 00 00 00 02 B2 0A /* 6: 249cd */ + 39 01 00 00 00 00 02 B2 0A /* 7: 234cd */ + 39 01 00 00 00 00 02 B2 0A /* 8: 220cd */ + 39 01 00 00 00 00 02 B2 0B /* 9: 207cd */ + 39 01 00 00 00 00 02 B2 0B /* 10: 195cd */ + 39 01 00 00 00 00 02 B2 0B /* 11: 183cd */ + 39 01 00 00 00 00 02 B2 0B /* 12: 172cd */ + 39 01 00 00 00 00 02 B2 0C /* 13: 162cd */ + 39 01 00 00 00 00 02 B2 0C /* 14: 152cd */ + 39 01 00 00 00 00 02 B2 0D /* 15: 143cd */ + 39 01 00 00 00 00 02 B2 0E /* 16: 134cd */ + 39 01 00 00 00 00 02 B2 0E /* 17: 126cd */ + 39 01 00 00 00 00 02 B2 0F /* 18: 119cd */ + 39 01 00 00 00 00 02 B2 0F /* 19: 111cd */ + 39 01 00 00 00 00 02 B2 0F /* 20: 105-60cd */ + 39 01 00 00 00 00 02 B2 0F /* 21: 56cd */ + 39 01 00 00 00 00 02 B2 0E /* 22: 53cd */ + 39 01 00 00 00 00 02 B2 0E /* 23: 50cd */ + 39 01 00 00 00 00 02 B2 0D /* 24: 47cd */ + 39 01 00 00 00 00 02 B2 0D /* 25: 44cd */ + 39 01 00 00 00 00 02 B2 0C /* 26: 41cd */ + 39 01 00 00 00 00 02 B2 0C /* 27: 39cd */ + 39 01 00 00 00 00 02 B2 0C /* 28: 37cd */ + 39 01 00 00 00 00 02 B2 0B /* 29: 34cd */ + 39 01 00 00 00 00 02 B2 0B /* 30: 32cd */ + 39 01 00 00 00 00 02 B2 0A /* 31: 30cd */ + 39 01 00 00 00 00 02 B2 0A /* 32: 29cd */ + 39 01 00 00 00 00 02 B2 0A /* 33: 27cd */ + 39 01 00 00 00 00 02 B2 09 /* 34: 25-21cd */ + 39 01 00 00 00 00 02 B2 08 /* 35: 20-16cd */ + 39 01 00 00 00 00 02 B2 07 /* 36: 15-11cd */ + 39 01 00 00 00 00 02 B2 06 /* 37: 10-5cd */ + 39 01 00 00 00 00 02 B2 05 /* 38: 4-2cd */ + ]; + + samsung,smart_acl_elvss_lowtemp2_tx_cmds_revA = [ + 39 01 00 00 00 00 02 B2 00 /* 0: 360cd */ + 39 01 00 00 00 00 02 B2 01 /* 1: 333cd */ + 39 01 00 00 00 00 02 B2 02 /* 2: 316cd */ + 39 01 00 00 00 00 02 B2 03 /* 3: 300cd */ + 39 01 00 00 00 00 02 B2 04 /* 4: 282cd */ + 39 01 00 00 00 00 02 B2 04 /* 5: 265cd */ + 39 01 00 00 00 00 02 B2 05 /* 6: 249cd */ + 39 01 00 00 00 00 02 B2 05 /* 7: 234cd */ + 39 01 00 00 00 00 02 B2 05 /* 8: 220cd */ + 39 01 00 00 00 00 02 B2 06 /* 9: 207cd */ + 39 01 00 00 00 00 02 B2 06 /* 10: 195cd */ + 39 01 00 00 00 00 02 B2 06 /* 11: 183cd */ + 39 01 00 00 00 00 02 B2 06 /* 12: 172cd */ + 39 01 00 00 00 00 02 B2 07 /* 13: 162cd */ + 39 01 00 00 00 00 02 B2 07 /* 14: 152cd */ + 39 01 00 00 00 00 02 B2 08 /* 15: 143cd */ + 39 01 00 00 00 00 02 B2 09 /* 16: 134cd */ + 39 01 00 00 00 00 02 B2 09 /* 17: 126cd */ + 39 01 00 00 00 00 02 B2 0A /* 18: 119cd */ + 39 01 00 00 00 00 02 B2 0A /* 19: 111cd */ + 39 01 00 00 00 00 02 B2 0A /* 20: 105-60cd */ + 39 01 00 00 00 00 02 B2 0A /* 21: 56cd */ + 39 01 00 00 00 00 02 B2 09 /* 22: 53cd */ + 39 01 00 00 00 00 02 B2 09 /* 23: 50cd */ + 39 01 00 00 00 00 02 B2 08 /* 24: 47cd */ + 39 01 00 00 00 00 02 B2 08 /* 25: 44cd */ + 39 01 00 00 00 00 02 B2 07 /* 26: 41cd */ + 39 01 00 00 00 00 02 B2 07 /* 27: 39cd */ + 39 01 00 00 00 00 02 B2 07 /* 28: 37cd */ + 39 01 00 00 00 00 02 B2 06 /* 29: 34cd */ + 39 01 00 00 00 00 02 B2 06 /* 30: 32cd */ + 39 01 00 00 00 00 02 B2 05 /* 31: 30cd */ + 39 01 00 00 00 00 02 B2 05 /* 32: 29cd */ + 39 01 00 00 00 00 02 B2 05 /* 33: 27cd */ + 39 01 00 00 00 00 02 B2 04 /* 34: 25-21cd */ + 39 01 00 00 00 00 02 B2 03 /* 35: 20-16cd */ + 39 01 00 00 00 00 02 B2 02 /* 36: 15-11cd */ + 39 01 00 00 00 00 02 B2 01 /* 37: 10-5cd */ + 39 01 00 00 00 00 02 B2 00 /* 38: 4-2cd */ + ]; + + /* bl_level, Index in elvss command list */ + samsung,smart_acl_elvss_map_table_revA = < + /* candela elvss_tx_cmd_id */ + 2 38 + 3 38 + 4 38 + 5 37 + 6 37 + 7 37 + 8 37 + 9 37 + 10 37 + 11 36 + 12 36 + 13 36 + 14 36 + 15 36 + 16 35 + 17 35 + 19 35 + 20 35 + 21 34 + 22 34 + 24 34 + 25 34 + 27 33 + 29 32 + 30 31 + 32 30 + 34 29 + 37 28 + 39 27 + 41 26 + 44 25 + 47 24 + 50 23 + 53 22 + 56 21 + 60 20 + 64 20 + 68 20 + 72 20 + 77 20 + 82 20 + 87 20 + 93 20 + 98 20 + 105 20 + 111 19 + 119 18 + 126 17 + 134 16 + 143 15 + 152 14 + 162 13 + 172 12 + 183 11 + 195 10 + 207 9 + 220 8 + 234 7 + 249 6 + 265 5 + 282 4 + 300 3 + 316 2 + 333 1 + 360 0 + >; + /* + * ************************************************************************************************************************ + * candela to index mappings + * ************************************************************************************************************************ + */ + samsung,candela_map_table_revA = < + /* */ + 0 0 0 2 + 1 1 1 3 + 2 2 2 4 + 3 3 3 5 + 4 4 4 6 + 5 5 5 7 + 6 6 6 8 + 7 7 7 9 + 8 8 8 10 + 9 9 9 11 + 10 10 10 12 + 11 11 11 13 + 12 12 12 14 + 13 13 13 15 + 14 14 14 16 + 15 15 15 17 + 16 16 16 19 + 17 17 17 20 + 18 18 18 21 + 19 19 19 22 + 20 20 20 24 + 21 21 21 25 + 22 22 23 27 + 23 24 25 29 + 24 26 27 30 + 25 28 29 32 + 26 30 31 34 + 27 32 33 37 + 28 34 35 39 + 29 36 37 41 + 30 38 39 44 + 31 40 41 47 + 32 42 43 50 + 33 44 45 53 + 34 46 48 56 + 35 49 51 60 + 36 52 54 64 + 37 55 57 68 + 38 58 60 72 + 39 61 63 77 + 40 64 66 82 + 41 67 69 87 + 42 70 72 93 + 43 73 76 98 + 44 77 80 105 + 45 81 86 111 + 46 87 91 119 + 47 92 97 126 + 48 98 104 134 + 49 105 110 143 + 50 111 118 152 + 51 119 125 162 + 52 126 133 172 + 53 134 142 183 + 54 143 150 195 + 55 151 160 207 + 56 161 170 220 + 57 171 181 234 + 58 182 192 249 + 59 193 203 265 + 60 204 214 282 + 61 215 225 300 + 62 226 235 316 + 63 236 245 333 + 64 246 255 360 + >; + + samsung,hbm_candela_map_table_revA = < + 0 268 279 378 6 + 1 280 292 395 7 + 2 293 304 413 8 + 3 305 317 430 9 + 4 318 329 448 10 + 5 330 341 465 11 + 6 342 354 483 12 + 7 355 355 500 13 + >; + + /* + * ************************************************************************************************************************ + * MULTI_RESOLUTION (DSC configs were moved all to below timing set) + * (C10P does not support MULTI_RESOLUTION, only FHD.) + * ************************************************************************************************************************ + */ + + qcom,mdss-dsi-display-timings { + fhd { + qcom,display-topology = <2 0 2>; + qcom,default-topology-index = <0>; + + qcom,mdss-dsi-timing-default; + + qcom,mdss-dsi-panel-width = <1280>; // 2560/2 + qcom,mdss-dsi-panel-height = <1600>; + qcom,mdss-dsi-t-clk-pre = <0x2F>; + qcom,mdss-dsi-t-clk-post = <0x0D>; + qcom,mdss-dsi-panel-phy-timings = [00 1E 08 08 24 22 08 08 05 02 04 00]; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-clockrate = <898020000>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-back-porch = <108>; + qcom,mdss-dsi-h-front-porch = <212>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-back-porch = <6>; + qcom,mdss-dsi-v-front-porch = <10>; + + /*qcom,mdss-dsi-timing-switch-command; use samsung,panel_multires_XXX instead*/ + /*qcom,mdss-dsi-timing-switch-command-state;*/ + + qcom,mdss-dsi-on-command= [ + /* Common Setting */ + 15 01 00 00 00 00 02 B0 7F /* Global Para 127th */ + 39 01 00 00 00 00 02 B2 15 /* TSP_HTE, TSP_VTE Setting */ + + /* Calculate Gamma */ + 15 01 00 00 00 00 02 B0 BC /* Global Para 188th */ + 39 01 00 00 00 00 02 B2 04 /* Change Gamma Offset Index : 4 */ + + 39 01 00 00 00 00 22 83 /* Brightness condition set */ + 80 80 + 80 80 + 80 80 + 80 00 + 80 80 + 00 + 80 80 + 80 80 + 80 80 + 80 00 + 80 80 + 00 + 80 80 + 80 80 + 80 80 + 80 00 + 80 80 + 00 + + 39 01 00 00 00 00 04 90 00 00 0E /* AID 360 nit */ + + 15 01 00 00 00 00 02 B0 67 /* Global Para 103rd */ + 39 01 00 00 00 00 02 B2 0F /* 360 nit ELVSS */ + + 15 01 00 00 00 00 02 B0 4D /* Global Para 77th */ + 39 01 00 00 00 00 02 B2 04 /* 16 Frame Avg at ACL off */ + + 15 01 00 00 00 00 02 B0 49 /* Global Para 73rd */ + 39 01 00 00 00 00 02 B2 99 /* 60% Start */ + + 15 01 00 00 00 00 02 B0 36 /* Global Para 54th */ + 39 01 00 00 00 00 02 B2 10 /* ACL off */ + + 15 01 00 00 00 00 02 B0 35 /* Global Para 53rd */ + 39 01 00 00 00 00 02 B2 01 /* Gamma Update + Wait 220ms (Panel Spec) */ + + /* + Display On (29h) Command should be transferred after 220ms from TCON_RDY (L'H) + Please check "samsung,wait_after_sleep_out_delay = <220>;" + + */ + //05 01 00 00 00 00 02 29 00 /* Display on */ + ]; + + qcom,mdss-dsi-off-command=[ + 05 01 00 00 64 00 02 28 00 /* Display Off + Wait 100ms */ + ]; + + qcom,mdss-dsi-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + }; + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_mdnie_ANA38401_AMSA05RB01.h b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_mdnie_ANA38401_AMSA05RB01.h new file mode 100755 index 000000000000..598f3b5889f6 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_mdnie_ANA38401_AMSA05RB01.h @@ -0,0 +1,10374 @@ +/* + * ================================================================= + * + * + * Description: samsung display common file + * + * Author: wu.deng + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +Copyright (C) 2017, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * +*/ +#ifndef _SS_DSI_MDNIE_ANA38401_AMSA05RB01_H_ +#define _SS_DSI_MDNIE_ANA38401_AMSA05RB01_H_ + +#include "ss_dsi_mdnie_lite_common.h" + +#define MDNIE_COLOR_BLINDE_CMD_OFFSET 32 + +#define ADDRESS_SCR_WHITE_RED 0x32 +#define ADDRESS_SCR_WHITE_GREEN 0x34 +#define ADDRESS_SCR_WHITE_BLUE 0x36 + +#define MDNIE_RGB_SENSOR_INDEX 1 + +#define MDNIE_STEP1_INDEX 1 +#define MDNIE_STEP2_INDEX 3 + +static char mdnie_address_1[] = { + 0xB0, + 0xA8, +}; + +static char mdnie_address_2[] = { + 0xB0, + 0x42, +}; + +static char adjust_ldu_data_1[] = { + 0xff, 0xff, 0xff, + 0xf6, 0xfa, 0xff, + 0xf4, 0xf8, 0xff, + 0xe9, 0xf2, 0xff, + 0xe2, 0xef, 0xff, + 0xd4, 0xe8, 0xff, +}; + +static char adjust_ldu_data_2[] = { + 0xff, 0xfa, 0xf1, + 0xff, 0xfd, 0xf8, + 0xff, 0xfd, 0xfa, + 0xfa, 0xfd, 0xff, + 0xf5, 0xfb, 0xff, + 0xe5, 0xf3, 0xff, +}; + +static char *adjust_ldu_data[MAX_MODE] = { + adjust_ldu_data_2, + adjust_ldu_data_2, + adjust_ldu_data_2, + adjust_ldu_data_1, + adjust_ldu_data_1, +}; + +static char night_mode_data[] = { + 0x00, 0xff, 0xfa, 0x00, 0xf1, 0x00, 0xff, 0x00, 0x00, 0xfa, 0xf1, 0x00, 0xff, 0x00, 0xfa, 0x00, 0x00, 0xf1, 0xff, 0x00, 0xfa, 0x00, 0xf1, 0x00, /* 6500K */ + 0x00, 0xff, 0xf8, 0x00, 0xeb, 0x00, 0xff, 0x00, 0x00, 0xf8, 0xeb, 0x00, 0xff, 0x00, 0xf8, 0x00, 0x00, 0xeb, 0xff, 0x00, 0xf8, 0x00, 0xeb, 0x00, /* 6100K */ + 0x00, 0xff, 0xf5, 0x00, 0xe2, 0x00, 0xff, 0x00, 0x00, 0xf5, 0xe2, 0x00, 0xff, 0x00, 0xf5, 0x00, 0x00, 0xe2, 0xff, 0x00, 0xf5, 0x00, 0xe2, 0x00, /* 5700K */ + 0x00, 0xff, 0xf2, 0x00, 0xda, 0x00, 0xff, 0x00, 0x00, 0xf2, 0xda, 0x00, 0xff, 0x00, 0xf2, 0x00, 0x00, 0xda, 0xff, 0x00, 0xf2, 0x00, 0xda, 0x00, /* 5300K */ + 0x00, 0xff, 0xee, 0x00, 0xd0, 0x00, 0xff, 0x00, 0x00, 0xee, 0xd0, 0x00, 0xff, 0x00, 0xee, 0x00, 0x00, 0xd0, 0xff, 0x00, 0xee, 0x00, 0xd0, 0x00, /* 4900K */ + 0x00, 0xff, 0xe9, 0x00, 0xc4, 0x00, 0xff, 0x00, 0x00, 0xe9, 0xc4, 0x00, 0xff, 0x00, 0xe9, 0x00, 0x00, 0xc4, 0xff, 0x00, 0xe9, 0x00, 0xc4, 0x00, /* 4500K */ + 0x00, 0xff, 0xe3, 0x00, 0xb4, 0x00, 0xff, 0x00, 0x00, 0xe3, 0xb4, 0x00, 0xff, 0x00, 0xe3, 0x00, 0x00, 0xb4, 0xff, 0x00, 0xe3, 0x00, 0xb4, 0x00, /* 4100K */ + 0x00, 0xff, 0xdd, 0x00, 0xa4, 0x00, 0xff, 0x00, 0x00, 0xdd, 0xa4, 0x00, 0xff, 0x00, 0xdd, 0x00, 0x00, 0xa4, 0xff, 0x00, 0xdd, 0x00, 0xa4, 0x00, /* 3700K */ + 0x00, 0xff, 0xd6, 0x00, 0x91, 0x00, 0xff, 0x00, 0x00, 0xd6, 0x91, 0x00, 0xff, 0x00, 0xd6, 0x00, 0x00, 0x91, 0xff, 0x00, 0xd6, 0x00, 0x91, 0x00, /* 3300K */ + 0x00, 0xff, 0xcb, 0x00, 0x7b, 0x00, 0xff, 0x00, 0x00, 0xcb, 0x7b, 0x00, 0xff, 0x00, 0xcb, 0x00, 0x00, 0x7b, 0xff, 0x00, 0xcb, 0x00, 0x7b, 0x00, /* 2900K */ + 0x00, 0xff, 0xbe, 0x00, 0x68, 0x00, 0xff, 0x00, 0x00, 0xbe, 0x68, 0x00, 0xff, 0x00, 0xbe, 0x00, 0x00, 0x68, 0xff, 0x00, 0xbe, 0x00, 0x68, 0x00, /* 2500K */ +}; + +static char color_lens_data[] = { + //Blue + 0x00, 0xcc, 0xcc, 0x00, 0xff, 0x33, 0xcc, 0x00, 0x00, 0xcc, 0xff, 0x33, 0xcc, 0x00, 0xcc, 0x00, 0x33, 0xff, 0xcc, 0x00, 0xcc, 0x00, 0xff, 0x33, /* 20% */ + 0x00, 0xbf, 0xbf, 0x00, 0xff, 0x40, 0xbf, 0x00, 0x00, 0xbf, 0xff, 0x40, 0xbf, 0x00, 0xbf, 0x00, 0x40, 0xff, 0xbf, 0x00, 0xbf, 0x00, 0xff, 0x40, /* 25% */ + 0x00, 0xb2, 0xb2, 0x00, 0xff, 0x4d, 0xb2, 0x00, 0x00, 0xb2, 0xff, 0x4d, 0xb2, 0x00, 0xb2, 0x00, 0x4d, 0xff, 0xb2, 0x00, 0xb2, 0x00, 0xff, 0x4d, /* 30% */ + 0x00, 0xa6, 0xa6, 0x00, 0xff, 0x59, 0xa6, 0x00, 0x00, 0xa6, 0xff, 0x59, 0xa6, 0x00, 0xa6, 0x00, 0x59, 0xff, 0xa6, 0x00, 0xa6, 0x00, 0xff, 0x59, /* 35% */ + 0x00, 0x99, 0x99, 0x00, 0xff, 0x66, 0x99, 0x00, 0x00, 0x99, 0xff, 0x66, 0x99, 0x00, 0x99, 0x00, 0x66, 0xff, 0x99, 0x00, 0x99, 0x00, 0xff, 0x66, /* 40% */ + 0x00, 0x8c, 0x8c, 0x00, 0xff, 0x73, 0x8c, 0x00, 0x00, 0x8c, 0xff, 0x73, 0x8c, 0x00, 0x8c, 0x00, 0x73, 0xff, 0x8c, 0x00, 0x8c, 0x00, 0xff, 0x73, /* 45% */ + 0x00, 0x7f, 0x7f, 0x00, 0xff, 0x80, 0x7f, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x80, 0xff, 0x7f, 0x00, 0x7f, 0x00, 0xff, 0x80, /* 50% */ + 0x00, 0x73, 0x73, 0x00, 0xff, 0x8c, 0x73, 0x00, 0x00, 0x73, 0xff, 0x8c, 0x73, 0x00, 0x73, 0x00, 0x8c, 0xff, 0x73, 0x00, 0x73, 0x00, 0xff, 0x8c, /* 55% */ + 0x00, 0x66, 0x66, 0x00, 0xff, 0x99, 0x66, 0x00, 0x00, 0x66, 0xff, 0x99, 0x66, 0x00, 0x66, 0x00, 0x99, 0xff, 0x66, 0x00, 0x66, 0x00, 0xff, 0x99, /* 60% */ + + //Azure + 0x00, 0xcc, 0xe5, 0x19, 0xff, 0x33, 0xcc, 0x00, 0x19, 0xe5, 0xff, 0x33, 0xcc, 0x00, 0xe5, 0x19, 0x33, 0xff, 0xcc, 0x00, 0xe5, 0x19, 0xff, 0x33, /* 20% */ + 0x00, 0xbf, 0xdf, 0x20, 0xff, 0x40, 0xbf, 0x00, 0x20, 0xdf, 0xff, 0x40, 0xbf, 0x00, 0xdf, 0x20, 0x40, 0xff, 0xbf, 0x00, 0xdf, 0x20, 0xff, 0x40, /* 25% */ + 0x00, 0xb2, 0xd8, 0x26, 0xff, 0x4d, 0xb2, 0x00, 0x26, 0xd8, 0xff, 0x4d, 0xb2, 0x00, 0xd8, 0x26, 0x4d, 0xff, 0xb2, 0x00, 0xd8, 0x26, 0xff, 0x4d, /* 30% */ + 0x00, 0xa6, 0xd2, 0x2c, 0xff, 0x59, 0xa6, 0x00, 0x2c, 0xd2, 0xff, 0x59, 0xa6, 0x00, 0xd2, 0x2c, 0x59, 0xff, 0xa6, 0x00, 0xd2, 0x2c, 0xff, 0x59, /* 35% */ + 0x00, 0x99, 0xcc, 0x33, 0xff, 0x66, 0x99, 0x00, 0x33, 0xcc, 0xff, 0x66, 0x99, 0x00, 0xcc, 0x33, 0x66, 0xff, 0x99, 0x00, 0xcc, 0x33, 0xff, 0x66, /* 40% */ + 0x00, 0x8c, 0xc5, 0x39, 0xff, 0x73, 0x8c, 0x00, 0x39, 0xc5, 0xff, 0x73, 0x8c, 0x00, 0xc5, 0x39, 0x73, 0xff, 0x8c, 0x00, 0xc5, 0x39, 0xff, 0x73, /* 45% */ + 0x00, 0x7f, 0xbf, 0x40, 0xff, 0x80, 0x7f, 0x00, 0x40, 0xbf, 0xff, 0x80, 0x7f, 0x00, 0xbf, 0x40, 0x80, 0xff, 0x7f, 0x00, 0xbf, 0x40, 0xff, 0x80, /* 50% */ + 0x00, 0x73, 0xb9, 0x46, 0xff, 0x8c, 0x73, 0x00, 0x46, 0xb9, 0xff, 0x8c, 0x73, 0x00, 0xb9, 0x46, 0x8c, 0xff, 0x73, 0x00, 0xb9, 0x46, 0xff, 0x8c, /* 55% */ + 0x00, 0x66, 0xb2, 0x4c, 0xff, 0x99, 0x66, 0x00, 0x4c, 0xb2, 0xff, 0x99, 0x66, 0x00, 0xb2, 0x4c, 0x99, 0xff, 0x66, 0x00, 0xb2, 0x4c, 0xff, 0x99, /* 60% */ + + //Cyan + 0x00, 0xcc, 0xff, 0x33, 0xff, 0x33, 0xcc, 0x00, 0x33, 0xff, 0xff, 0x33, 0xcc, 0x00, 0xff, 0x33, 0x33, 0xff, 0xcc, 0x00, 0xff, 0x33, 0xff, 0x33, /* 20% */ + 0x00, 0xbf, 0xff, 0x40, 0xff, 0x40, 0xbf, 0x00, 0x40, 0xff, 0xff, 0x40, 0xbf, 0x00, 0xff, 0x40, 0x40, 0xff, 0xbf, 0x00, 0xff, 0x40, 0xff, 0x40, /* 25% */ + 0x00, 0xb2, 0xff, 0x4d, 0xff, 0x4d, 0xb2, 0x00, 0x4d, 0xff, 0xff, 0x4d, 0xb2, 0x00, 0xff, 0x4d, 0x4d, 0xff, 0xb2, 0x00, 0xff, 0x4d, 0xff, 0x4d, /* 30% */ + 0x00, 0xa6, 0xff, 0x59, 0xff, 0x59, 0xa6, 0x00, 0x59, 0xff, 0xff, 0x59, 0xa6, 0x00, 0xff, 0x59, 0x59, 0xff, 0xa6, 0x00, 0xff, 0x59, 0xff, 0x59, /* 35% */ + 0x00, 0x99, 0xff, 0x66, 0xff, 0x66, 0x99, 0x00, 0x66, 0xff, 0xff, 0x66, 0x99, 0x00, 0xff, 0x66, 0x66, 0xff, 0x99, 0x00, 0xff, 0x66, 0xff, 0x66, /* 40% */ + 0x00, 0x8c, 0xff, 0x73, 0xff, 0x73, 0x8c, 0x00, 0x73, 0xff, 0xff, 0x73, 0x8c, 0x00, 0xff, 0x73, 0x73, 0xff, 0x8c, 0x00, 0xff, 0x73, 0xff, 0x73, /* 45% */ + 0x00, 0x7f, 0xff, 0x80, 0xff, 0x80, 0x7f, 0x00, 0x80, 0xff, 0xff, 0x80, 0x7f, 0x00, 0xff, 0x80, 0x80, 0xff, 0x7f, 0x00, 0xff, 0x80, 0xff, 0x80, /* 50% */ + 0x00, 0x73, 0xff, 0x8c, 0xff, 0x8c, 0x73, 0x00, 0x8c, 0xff, 0xff, 0x8c, 0x73, 0x00, 0xff, 0x8c, 0x8c, 0xff, 0x73, 0x00, 0xff, 0x8c, 0xff, 0x8c, /* 55% */ + 0x00, 0x66, 0xff, 0x99, 0xff, 0x99, 0x66, 0x00, 0x99, 0xff, 0xff, 0x99, 0x66, 0x00, 0xff, 0x99, 0x99, 0xff, 0x66, 0x00, 0xff, 0x99, 0xff, 0x99, /* 60% */ + + //Spring green + 0x00, 0xcc, 0xff, 0x33, 0xe5, 0x19, 0xcc, 0x00, 0x33, 0xff, 0xe5, 0x19, 0xcc, 0x00, 0xff, 0x33, 0x19, 0xe5, 0xcc, 0x00, 0xff, 0x33, 0xe5, 0x19, /* 20% */ + 0x00, 0xbf, 0xff, 0x40, 0xdf, 0x20, 0xbf, 0x00, 0x40, 0xff, 0xdf, 0x20, 0xbf, 0x00, 0xff, 0x40, 0x20, 0xdf, 0xbf, 0x00, 0xff, 0x40, 0xdf, 0x20, /* 25% */ + 0x00, 0xb2, 0xff, 0x4d, 0xd8, 0x26, 0xb2, 0x00, 0x4d, 0xff, 0xd8, 0x26, 0xb2, 0x00, 0xff, 0x4d, 0x26, 0xd8, 0xb2, 0x00, 0xff, 0x4d, 0xd8, 0x26, /* 30% */ + 0x00, 0xa6, 0xff, 0x59, 0xd2, 0x2c, 0xa6, 0x00, 0x59, 0xff, 0xd2, 0x2c, 0xa6, 0x00, 0xff, 0x59, 0x2c, 0xd2, 0xa6, 0x00, 0xff, 0x59, 0xd2, 0x2c, /* 35% */ + 0x00, 0x99, 0xff, 0x66, 0xcc, 0x33, 0x99, 0x00, 0x66, 0xff, 0xcc, 0x33, 0x99, 0x00, 0xff, 0x66, 0x33, 0xcc, 0x99, 0x00, 0xff, 0x66, 0xcc, 0x33, /* 40% */ + 0x00, 0x8c, 0xff, 0x73, 0xc5, 0x39, 0x8c, 0x00, 0x73, 0xff, 0xc5, 0x39, 0x8c, 0x00, 0xff, 0x73, 0x39, 0xc5, 0x8c, 0x00, 0xff, 0x73, 0xc5, 0x39, /* 45% */ + 0x00, 0x7f, 0xff, 0x80, 0xbf, 0x40, 0x7f, 0x00, 0x80, 0xff, 0xbf, 0x40, 0x7f, 0x00, 0xff, 0x80, 0x40, 0xbf, 0x7f, 0x00, 0xff, 0x80, 0xbf, 0x40, /* 50% */ + 0x00, 0x73, 0xff, 0x8c, 0xb9, 0x46, 0x73, 0x00, 0x8c, 0xff, 0xb9, 0x46, 0x73, 0x00, 0xff, 0x8c, 0x46, 0xb9, 0x73, 0x00, 0xff, 0x8c, 0xb9, 0x46, /* 55% */ + 0x00, 0x66, 0xff, 0x99, 0xb2, 0x4c, 0x66, 0x00, 0x99, 0xff, 0xb2, 0x4c, 0x66, 0x00, 0xff, 0x99, 0x4c, 0xb2, 0x66, 0x00, 0xff, 0x99, 0xb2, 0x4c, /* 60% */ + + //Green + 0x00, 0xcc, 0xff, 0x33, 0xcc, 0x00, 0xcc, 0x00, 0x33, 0xff, 0xcc, 0x00, 0xcc, 0x00, 0xff, 0x33, 0x00, 0xcc, 0xcc, 0x00, 0xff, 0x33, 0xcc, 0x00, /* 20% */ + 0x00, 0xbf, 0xff, 0x40, 0xbf, 0x00, 0xbf, 0x00, 0x40, 0xff, 0xbf, 0x00, 0xbf, 0x00, 0xff, 0x40, 0x00, 0xbf, 0xbf, 0x00, 0xff, 0x40, 0xbf, 0x00, /* 25% */ + 0x00, 0xb2, 0xff, 0x4d, 0xb2, 0x00, 0xb2, 0x00, 0x4d, 0xff, 0xb2, 0x00, 0xb2, 0x00, 0xff, 0x4d, 0x00, 0xb2, 0xb2, 0x00, 0xff, 0x4d, 0xb2, 0x00, /* 30% */ + 0x00, 0xa6, 0xff, 0x59, 0xa6, 0x00, 0xa6, 0x00, 0x59, 0xff, 0xa6, 0x00, 0xa6, 0x00, 0xff, 0x59, 0x00, 0xa6, 0xa6, 0x00, 0xff, 0x59, 0xa6, 0x00, /* 35% */ + 0x00, 0x99, 0xff, 0x66, 0x99, 0x00, 0x99, 0x00, 0x66, 0xff, 0x99, 0x00, 0x99, 0x00, 0xff, 0x66, 0x00, 0x99, 0x99, 0x00, 0xff, 0x66, 0x99, 0x00, /* 40% */ + 0x00, 0x8c, 0xff, 0x73, 0x8c, 0x00, 0x8c, 0x00, 0x73, 0xff, 0x8c, 0x00, 0x8c, 0x00, 0xff, 0x73, 0x00, 0x8c, 0x8c, 0x00, 0xff, 0x73, 0x8c, 0x00, /* 45% */ + 0x00, 0x7f, 0xff, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x80, 0xff, 0x7f, 0x00, 0x7f, 0x00, 0xff, 0x80, 0x00, 0x7f, 0x7f, 0x00, 0xff, 0x80, 0x7f, 0x00, /* 50% */ + 0x00, 0x73, 0xff, 0x8c, 0x73, 0x00, 0x73, 0x00, 0x8c, 0xff, 0x73, 0x00, 0x73, 0x00, 0xff, 0x8c, 0x00, 0x73, 0x73, 0x00, 0xff, 0x8c, 0x73, 0x00, /* 55% */ + 0x00, 0x66, 0xff, 0x99, 0x66, 0x00, 0x66, 0x00, 0x99, 0xff, 0x66, 0x00, 0x66, 0x00, 0xff, 0x99, 0x00, 0x66, 0x66, 0x00, 0xff, 0x99, 0x66, 0x00, /* 60% */ + + //Chartreuse Green + 0x19, 0xe5, 0xff, 0x33, 0xcc, 0x00, 0xe5, 0x19, 0x33, 0xff, 0xcc, 0x00, 0xe5, 0x19, 0xff, 0x33, 0x00, 0xcc, 0xe5, 0x19, 0xff, 0x33, 0xcc, 0x00, /* 20% */ + 0x20, 0xdf, 0xff, 0x40, 0xbf, 0x00, 0xdf, 0x20, 0x40, 0xff, 0xbf, 0x00, 0xdf, 0x20, 0xff, 0x40, 0x00, 0xbf, 0xdf, 0x20, 0xff, 0x40, 0xbf, 0x00, /* 25% */ + 0x26, 0xd8, 0xff, 0x4d, 0xb2, 0x00, 0xd8, 0x26, 0x4d, 0xff, 0xb2, 0x00, 0xd8, 0x26, 0xff, 0x4d, 0x00, 0xb2, 0xd8, 0x26, 0xff, 0x4d, 0xb2, 0x00, /* 30% */ + 0x2c, 0xd2, 0xff, 0x59, 0xa6, 0x00, 0xd2, 0x2c, 0x59, 0xff, 0xa6, 0x00, 0xd2, 0x2c, 0xff, 0x59, 0x00, 0xa6, 0xd2, 0x2c, 0xff, 0x59, 0xa6, 0x00, /* 35% */ + 0x33, 0xcc, 0xff, 0x66, 0x99, 0x00, 0xcc, 0x33, 0x66, 0xff, 0x99, 0x00, 0xcc, 0x33, 0xff, 0x66, 0x00, 0x99, 0xcc, 0x33, 0xff, 0x66, 0x99, 0x00, /* 40% */ + 0x39, 0xc5, 0xff, 0x73, 0x8c, 0x00, 0xc5, 0x39, 0x73, 0xff, 0x8c, 0x00, 0xc5, 0x39, 0xff, 0x73, 0x00, 0x8c, 0xc5, 0x39, 0xff, 0x73, 0x8c, 0x00, /* 45% */ + 0x40, 0xbf, 0xff, 0x80, 0x7f, 0x00, 0xbf, 0x40, 0x80, 0xff, 0x7f, 0x00, 0xbf, 0x40, 0xff, 0x80, 0x00, 0x7f, 0xbf, 0x40, 0xff, 0x80, 0x7f, 0x00, /* 50% */ + 0x46, 0xb9, 0xff, 0x8c, 0x73, 0x00, 0xb9, 0x46, 0x8c, 0xff, 0x73, 0x00, 0xb9, 0x46, 0xff, 0x8c, 0x00, 0x73, 0xb9, 0x46, 0xff, 0x8c, 0x73, 0x00, /* 55% */ + 0x4c, 0xb2, 0xff, 0x99, 0x66, 0x00, 0xb2, 0x4c, 0x99, 0xff, 0x66, 0x00, 0xb2, 0x4c, 0xff, 0x99, 0x00, 0x66, 0xb2, 0x4c, 0xff, 0x99, 0x66, 0x00, /* 60% */ + + //Yellow + 0x33, 0xff, 0xff, 0x33, 0xcc, 0x00, 0xff, 0x33, 0x33, 0xff, 0xcc, 0x00, 0xff, 0x33, 0xff, 0x33, 0x00, 0xcc, 0xff, 0x33, 0xff, 0x33, 0xcc, 0x00, /* 20% */ + 0x40, 0xff, 0xff, 0x40, 0xbf, 0x00, 0xff, 0x40, 0x40, 0xff, 0xbf, 0x00, 0xff, 0x40, 0xff, 0x40, 0x00, 0xbf, 0xff, 0x40, 0xff, 0x40, 0xbf, 0x00, /* 25% */ + 0x4d, 0xff, 0xff, 0x4d, 0xb2, 0x00, 0xff, 0x4d, 0x4d, 0xff, 0xb2, 0x00, 0xff, 0x4d, 0xff, 0x4d, 0x00, 0xb2, 0xff, 0x4d, 0xff, 0x4d, 0xb2, 0x00, /* 30% */ + 0x59, 0xff, 0xff, 0x59, 0xa6, 0x00, 0xff, 0x59, 0x59, 0xff, 0xa6, 0x00, 0xff, 0x59, 0xff, 0x59, 0x00, 0xa6, 0xff, 0x59, 0xff, 0x59, 0xa6, 0x00, /* 35% */ + 0x66, 0xff, 0xff, 0x66, 0x99, 0x00, 0xff, 0x66, 0x66, 0xff, 0x99, 0x00, 0xff, 0x66, 0xff, 0x66, 0x00, 0x99, 0xff, 0x66, 0xff, 0x66, 0x99, 0x00, /* 40% */ + 0x73, 0xff, 0xff, 0x73, 0x8c, 0x00, 0xff, 0x73, 0x73, 0xff, 0x8c, 0x00, 0xff, 0x73, 0xff, 0x73, 0x00, 0x8c, 0xff, 0x73, 0xff, 0x73, 0x8c, 0x00, /* 45% */ + 0x80, 0xff, 0xff, 0x80, 0x7f, 0x00, 0xff, 0x80, 0x80, 0xff, 0x7f, 0x00, 0xff, 0x80, 0xff, 0x80, 0x00, 0x7f, 0xff, 0x80, 0xff, 0x80, 0x7f, 0x00, /* 50% */ + 0x8c, 0xff, 0xff, 0x8c, 0x73, 0x00, 0xff, 0x8c, 0x8c, 0xff, 0x73, 0x00, 0xff, 0x8c, 0xff, 0x8c, 0x00, 0x73, 0xff, 0x8c, 0xff, 0x8c, 0x73, 0x00, /* 55% */ + 0x99, 0xff, 0xff, 0x99, 0x66, 0x00, 0xff, 0x99, 0x99, 0xff, 0x66, 0x00, 0xff, 0x99, 0xff, 0x99, 0x00, 0x66, 0xff, 0x99, 0xff, 0x99, 0x66, 0x00, /* 60% */ + + //Orange + 0x33, 0xff, 0xe5, 0x19, 0xcc, 0x00, 0xff, 0x33, 0x19, 0xe5, 0xcc, 0x00, 0xff, 0x33, 0xe5, 0x19, 0x00, 0xcc, 0xff, 0x33, 0xe5, 0x19, 0xcc, 0x00, /* 20% */ + 0x40, 0xff, 0xdf, 0x20, 0xbf, 0x00, 0xff, 0x40, 0x20, 0xdf, 0xbf, 0x00, 0xff, 0x40, 0xdf, 0x20, 0x00, 0xbf, 0xff, 0x40, 0xdf, 0x20, 0xbf, 0x00, /* 25% */ + 0x4d, 0xff, 0xd8, 0x26, 0xb2, 0x00, 0xff, 0x4d, 0x26, 0xd8, 0xb2, 0x00, 0xff, 0x4d, 0xd8, 0x26, 0x00, 0xb2, 0xff, 0x4d, 0xd8, 0x26, 0xb2, 0x00, /* 30% */ + 0x59, 0xff, 0xd2, 0x2c, 0xa6, 0x00, 0xff, 0x59, 0x2c, 0xd2, 0xa6, 0x00, 0xff, 0x59, 0xd2, 0x2c, 0x00, 0xa6, 0xff, 0x59, 0xd2, 0x2c, 0xa6, 0x00, /* 35% */ + 0x66, 0xff, 0xcc, 0x33, 0x99, 0x00, 0xff, 0x66, 0x33, 0xcc, 0x99, 0x00, 0xff, 0x66, 0xcc, 0x33, 0x00, 0x99, 0xff, 0x66, 0xcc, 0x33, 0x99, 0x00, /* 40% */ + 0x73, 0xff, 0xc5, 0x39, 0x8c, 0x00, 0xff, 0x73, 0x39, 0xc5, 0x8c, 0x00, 0xff, 0x73, 0xc5, 0x39, 0x00, 0x8c, 0xff, 0x73, 0xc5, 0x39, 0x8c, 0x00, /* 45% */ + 0x80, 0xff, 0xbf, 0x40, 0x7f, 0x00, 0xff, 0x80, 0x40, 0xbf, 0x7f, 0x00, 0xff, 0x80, 0xbf, 0x40, 0x00, 0x7f, 0xff, 0x80, 0xbf, 0x40, 0x7f, 0x00, /* 50% */ + 0x8c, 0xff, 0xb9, 0x46, 0x73, 0x00, 0xff, 0x8c, 0x46, 0xb9, 0x73, 0x00, 0xff, 0x8c, 0xb9, 0x46, 0x00, 0x73, 0xff, 0x8c, 0xb9, 0x46, 0x73, 0x00, /* 55% */ + 0x99, 0xff, 0xb2, 0x4c, 0x66, 0x00, 0xff, 0x99, 0x4c, 0xb2, 0x66, 0x00, 0xff, 0x99, 0xb2, 0x4c, 0x00, 0x66, 0xff, 0x99, 0xb2, 0x4c, 0x66, 0x00, /* 60% */ + + //Red + 0x33, 0xff, 0xcc, 0x00, 0xcc, 0x00, 0xff, 0x33, 0x00, 0xcc, 0xcc, 0x00, 0xff, 0x33, 0xcc, 0x00, 0x00, 0xcc, 0xff, 0x33, 0xcc, 0x00, 0xcc, 0x00, /* 20% */ + 0x40, 0xff, 0xbf, 0x00, 0xbf, 0x00, 0xff, 0x40, 0x00, 0xbf, 0xbf, 0x00, 0xff, 0x40, 0xbf, 0x00, 0x00, 0xbf, 0xff, 0x40, 0xbf, 0x00, 0xbf, 0x00, /* 25% */ + 0x4d, 0xff, 0xb2, 0x00, 0xb2, 0x00, 0xff, 0x4d, 0x00, 0xb2, 0xb2, 0x00, 0xff, 0x4d, 0xb2, 0x00, 0x00, 0xb2, 0xff, 0x4d, 0xb2, 0x00, 0xb2, 0x00, /* 30% */ + 0x59, 0xff, 0xa6, 0x00, 0xa6, 0x00, 0xff, 0x59, 0x00, 0xa6, 0xa6, 0x00, 0xff, 0x59, 0xa6, 0x00, 0x00, 0xa6, 0xff, 0x59, 0xa6, 0x00, 0xa6, 0x00, /* 35% */ + 0x66, 0xff, 0x99, 0x00, 0x99, 0x00, 0xff, 0x66, 0x00, 0x99, 0x99, 0x00, 0xff, 0x66, 0x99, 0x00, 0x00, 0x99, 0xff, 0x66, 0x99, 0x00, 0x99, 0x00, /* 40% */ + 0x73, 0xff, 0x8c, 0x00, 0x8c, 0x00, 0xff, 0x73, 0x00, 0x8c, 0x8c, 0x00, 0xff, 0x73, 0x8c, 0x00, 0x00, 0x8c, 0xff, 0x73, 0x8c, 0x00, 0x8c, 0x00, /* 45% */ + 0x80, 0xff, 0x7f, 0x00, 0x7f, 0x00, 0xff, 0x80, 0x00, 0x7f, 0x7f, 0x00, 0xff, 0x80, 0x7f, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x7f, 0x00, 0x7f, 0x00, /* 50% */ + 0x8c, 0xff, 0x73, 0x00, 0x73, 0x00, 0xff, 0x8c, 0x00, 0x73, 0x73, 0x00, 0xff, 0x8c, 0x73, 0x00, 0x00, 0x73, 0xff, 0x8c, 0x73, 0x00, 0x73, 0x00, /* 55% */ + 0x99, 0xff, 0x66, 0x00, 0x66, 0x00, 0xff, 0x99, 0x00, 0x66, 0x66, 0x00, 0xff, 0x99, 0x66, 0x00, 0x00, 0x66, 0xff, 0x99, 0x66, 0x00, 0x66, 0x00, /* 60% */ + + //Rose + 0x33, 0xff, 0xcc, 0x00, 0xe5, 0x19, 0xff, 0x33, 0x00, 0xcc, 0xe5, 0x19, 0xff, 0x33, 0xcc, 0x00, 0x19, 0xe5, 0xff, 0x33, 0xcc, 0x00, 0xe5, 0x19, /* 20% */ + 0x40, 0xff, 0xbf, 0x00, 0xdf, 0x20, 0xff, 0x40, 0x00, 0xbf, 0xdf, 0x20, 0xff, 0x40, 0xbf, 0x00, 0x20, 0xdf, 0xff, 0x40, 0xbf, 0x00, 0xdf, 0x20, /* 25% */ + 0x4d, 0xff, 0xb2, 0x00, 0xd8, 0x26, 0xff, 0x4d, 0x00, 0xb2, 0xd8, 0x26, 0xff, 0x4d, 0xb2, 0x00, 0x26, 0xd8, 0xff, 0x4d, 0xb2, 0x00, 0xd8, 0x26, /* 30% */ + 0x59, 0xff, 0xa6, 0x00, 0xd2, 0x2c, 0xff, 0x59, 0x00, 0xa6, 0xd2, 0x2c, 0xff, 0x59, 0xa6, 0x00, 0x2c, 0xd2, 0xff, 0x59, 0xa6, 0x00, 0xd2, 0x2c, /* 35% */ + 0x66, 0xff, 0x99, 0x00, 0xcc, 0x33, 0xff, 0x66, 0x00, 0x99, 0xcc, 0x33, 0xff, 0x66, 0x99, 0x00, 0x33, 0xcc, 0xff, 0x66, 0x99, 0x00, 0xcc, 0x33, /* 40% */ + 0x73, 0xff, 0x8c, 0x00, 0xc5, 0x39, 0xff, 0x73, 0x00, 0x8c, 0xc5, 0x39, 0xff, 0x73, 0x8c, 0x00, 0x39, 0xc5, 0xff, 0x73, 0x8c, 0x00, 0xc5, 0x39, /* 45% */ + 0x80, 0xff, 0x7f, 0x00, 0xbf, 0x40, 0xff, 0x80, 0x00, 0x7f, 0xbf, 0x40, 0xff, 0x80, 0x7f, 0x00, 0x40, 0xbf, 0xff, 0x80, 0x7f, 0x00, 0xbf, 0x40, /* 50% */ + 0x8c, 0xff, 0x73, 0x00, 0xb9, 0x46, 0xff, 0x8c, 0x00, 0x73, 0xb9, 0x46, 0xff, 0x8c, 0x73, 0x00, 0x46, 0xb9, 0xff, 0x8c, 0x73, 0x00, 0xb9, 0x46, /* 55% */ + 0x99, 0xff, 0x66, 0x00, 0xb2, 0x4c, 0xff, 0x99, 0x00, 0x66, 0xb2, 0x4c, 0xff, 0x99, 0x66, 0x00, 0x4c, 0xb2, 0xff, 0x99, 0x66, 0x00, 0xb2, 0x4c, /* 60% */ + + //Magenta + 0x33, 0xff, 0xcc, 0x00, 0xff, 0x33, 0xff, 0x33, 0x00, 0xcc, 0xff, 0x33, 0xff, 0x33, 0xcc, 0x00, 0x33, 0xff, 0xff, 0x33, 0xcc, 0x00, 0xff, 0x33, /* 20% */ + 0x40, 0xff, 0xbf, 0x00, 0xff, 0x40, 0xff, 0x40, 0x00, 0xbf, 0xff, 0x40, 0xff, 0x40, 0xbf, 0x00, 0x40, 0xff, 0xff, 0x40, 0xbf, 0x00, 0xff, 0x40, /* 25% */ + 0x4d, 0xff, 0xb2, 0x00, 0xff, 0x4d, 0xff, 0x4d, 0x00, 0xb2, 0xff, 0x4d, 0xff, 0x4d, 0xb2, 0x00, 0x4d, 0xff, 0xff, 0x4d, 0xb2, 0x00, 0xff, 0x4d, /* 30% */ + 0x59, 0xff, 0xa6, 0x00, 0xff, 0x59, 0xff, 0x59, 0x00, 0xa6, 0xff, 0x59, 0xff, 0x59, 0xa6, 0x00, 0x59, 0xff, 0xff, 0x59, 0xa6, 0x00, 0xff, 0x59, /* 35% */ + 0x66, 0xff, 0x99, 0x00, 0xff, 0x66, 0xff, 0x66, 0x00, 0x99, 0xff, 0x66, 0xff, 0x66, 0x99, 0x00, 0x66, 0xff, 0xff, 0x66, 0x99, 0x00, 0xff, 0x66, /* 40% */ + 0x73, 0xff, 0x8c, 0x00, 0xff, 0x73, 0xff, 0x73, 0x00, 0x8c, 0xff, 0x73, 0xff, 0x73, 0x8c, 0x00, 0x73, 0xff, 0xff, 0x73, 0x8c, 0x00, 0xff, 0x73, /* 45% */ + 0x80, 0xff, 0x7f, 0x00, 0xff, 0x80, 0xff, 0x80, 0x00, 0x7f, 0xff, 0x80, 0xff, 0x80, 0x7f, 0x00, 0x80, 0xff, 0xff, 0x80, 0x7f, 0x00, 0xff, 0x80, /* 50% */ + 0x8c, 0xff, 0x73, 0x00, 0xff, 0x8c, 0xff, 0x8c, 0x00, 0x73, 0xff, 0x8c, 0xff, 0x8c, 0x73, 0x00, 0x8c, 0xff, 0xff, 0x8c, 0x73, 0x00, 0xff, 0x8c, /* 55% */ + 0x99, 0xff, 0x66, 0x00, 0xff, 0x99, 0xff, 0x99, 0x00, 0x66, 0xff, 0x99, 0xff, 0x99, 0x66, 0x00, 0x99, 0xff, 0xff, 0x99, 0x66, 0x00, 0xff, 0x99, /* 60% */ + + //Violet + 0x19, 0xe5, 0xcc, 0x00, 0xff, 0x33, 0xe5, 0x19, 0x00, 0xcc, 0xff, 0x33, 0xe5, 0x19, 0xcc, 0x00, 0x33, 0xff, 0xe5, 0x19, 0xcc, 0x00, 0xff, 0x33, /* 20% */ + 0x20, 0xdf, 0xbf, 0x00, 0xff, 0x40, 0xdf, 0x20, 0x00, 0xbf, 0xff, 0x40, 0xdf, 0x20, 0xbf, 0x00, 0x40, 0xff, 0xdf, 0x20, 0xbf, 0x00, 0xff, 0x40, /* 25% */ + 0x26, 0xd8, 0xb2, 0x00, 0xff, 0x4d, 0xd8, 0x26, 0x00, 0xb2, 0xff, 0x4d, 0xd8, 0x26, 0xb2, 0x00, 0x4d, 0xff, 0xd8, 0x26, 0xb2, 0x00, 0xff, 0x4d, /* 30% */ + 0x2c, 0xd2, 0xa6, 0x00, 0xff, 0x59, 0xd2, 0x2c, 0x00, 0xa6, 0xff, 0x59, 0xd2, 0x2c, 0xa6, 0x00, 0x59, 0xff, 0xd2, 0x2c, 0xa6, 0x00, 0xff, 0x59, /* 35% */ + 0x33, 0xcc, 0x99, 0x00, 0xff, 0x66, 0xcc, 0x33, 0x00, 0x99, 0xff, 0x66, 0xcc, 0x33, 0x99, 0x00, 0x66, 0xff, 0xcc, 0x33, 0x99, 0x00, 0xff, 0x66, /* 40% */ + 0x39, 0xc5, 0x8c, 0x00, 0xff, 0x73, 0xc5, 0x39, 0x00, 0x8c, 0xff, 0x73, 0xc5, 0x39, 0x8c, 0x00, 0x73, 0xff, 0xc5, 0x39, 0x8c, 0x00, 0xff, 0x73, /* 45% */ + 0x40, 0xbf, 0x7f, 0x00, 0xff, 0x80, 0xbf, 0x40, 0x00, 0x7f, 0xff, 0x80, 0xbf, 0x40, 0x7f, 0x00, 0x80, 0xff, 0xbf, 0x40, 0x7f, 0x00, 0xff, 0x80, /* 50% */ + 0x46, 0xb9, 0x73, 0x00, 0xff, 0x8c, 0xb9, 0x46, 0x00, 0x73, 0xff, 0x8c, 0xb9, 0x46, 0x73, 0x00, 0x8c, 0xff, 0xb9, 0x46, 0x73, 0x00, 0xff, 0x8c, /* 55% */ + 0x4c, 0xb2, 0x66, 0x00, 0xff, 0x99, 0xb2, 0x4c, 0x00, 0x66, 0xff, 0x99, 0xb2, 0x4c, 0x66, 0x00, 0x99, 0xff, 0xb2, 0x4c, 0x66, 0x00, 0xff, 0x99, /* 60% */ +}; + +static char DSI0_BYPASS_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_BYPASS_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x00, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_NEGATIVE_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0x00, /* ascr_Wr */ + 0xff, /* ascr_Kr */ + 0x00, /* ascr_Wg */ + 0xff, /* ascr_Kg */ + 0x00, /* ascr_Wb */ + 0xff, /* ascr_Kb */ + /* end */ +}; +static char DSI0_NEGATIVE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GRAYSCALE_MDNIE_1[] = { + /* start */ + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0xb3, /* ascr_Cr */ + 0x4c, /* ascr_Rr */ + 0xb3, /* ascr_Cg */ + 0x4c, /* ascr_Rg */ + 0xb3, /* ascr_Cb */ + 0x4c, /* ascr_Rb */ + 0x69, /* ascr_Mr */ + 0x96, /* ascr_Gr */ + 0x69, /* ascr_Mg */ + 0x96, /* ascr_Gg */ + 0x69, /* ascr_Mb */ + 0x96, /* ascr_Gb */ + 0xe2, /* ascr_Yr */ + 0x1d, /* ascr_Br */ + 0xe2, /* ascr_Yg */ + 0x1d, /* ascr_Bg */ + 0xe2, /* ascr_Yb */ + 0x1d, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_GRAYSCALE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GRAYSCALE_NEGATIVE_MDNIE_1[] = { + /* start */ + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0xb3, /* ascr_Cr */ + 0x4c, /* ascr_Rr */ + 0xb3, /* ascr_Cg */ + 0x4c, /* ascr_Rg */ + 0xb3, /* ascr_Cb */ + 0x4c, /* ascr_Rb */ + 0x69, /* ascr_Mr */ + 0x96, /* ascr_Gr */ + 0x69, /* ascr_Mg */ + 0x96, /* ascr_Gg */ + 0x69, /* ascr_Mb */ + 0x96, /* ascr_Gb */ + 0xe2, /* ascr_Yr */ + 0x1d, /* ascr_Br */ + 0xe2, /* ascr_Yg */ + 0x1d, /* ascr_Bg */ + 0xe2, /* ascr_Yb */ + 0x1d, /* ascr_Bb */ + 0x00, /* ascr_Wr */ + 0xff, /* ascr_Kr */ + 0x00, /* ascr_Wg */ + 0xff, /* ascr_Kg */ + 0x00, /* ascr_Wb */ + 0xff, /* ascr_Kb */ + /* end */ +}; +static char DSI0_GRAYSCALE_NEGATIVE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_COLOR_BLIND_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_COLOR_BLIND_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_COLOR_LENS_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_COLOR_LENS_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_NIGHT_MODE_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_NIGHT_MODE_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_LIGHT_NOTIFICATION_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x66, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xf9, /* ascr_Cg */ + 0x60, /* ascr_Rg */ + 0xac, /* ascr_Cb */ + 0x13, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x66, /* ascr_Gr */ + 0x60, /* ascr_Mg */ + 0xf9, /* ascr_Gg */ + 0xac, /* ascr_Mb */ + 0x13, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x66, /* ascr_Br */ + 0xf9, /* ascr_Yg */ + 0x60, /* ascr_Bg */ + 0x13, /* ascr_Yb */ + 0xac, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x66, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x60, /* ascr_Kg */ + 0xac, /* ascr_Wb */ + 0x13, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_LIGHT_NOTIFICATION_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_HBM_CE_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x30, /* ascr_skin_Rg */ + 0x30, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_HBM_CE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3f, /* ascr algo lce 10 10 10 */ + 0x86, /* lce_on gain 0 00 0000 */ + 0x30, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0x90, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0xbf, + 0x00, /* lce_ref_gain 9 */ + 0xb0, + 0x77, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x00, /* lce_dark_th 000 */ + 0x40, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x60, + 0x01, /* de_maxplus 11 */ + 0x00, + 0x01, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x40, + 0x00, /* curve_1_b */ + 0x6b, /* curve_1_a */ + 0x03, /* curve_2_b */ + 0x48, /* curve_2_a */ + 0x08, /* curve_3_b */ + 0x32, /* curve_3_a */ + 0x08, /* curve_4_b */ + 0x32, /* curve_4_a */ + 0x08, /* curve_5_b */ + 0x32, /* curve_5_a */ + 0x08, /* curve_6_b */ + 0x32, /* curve_6_a */ + 0x08, /* curve_7_b */ + 0x32, /* curve_7_a */ + 0x10, /* curve_8_b */ + 0x28, /* curve_8_a */ + 0x10, /* curve_9_b */ + 0x28, /* curve_9_a */ + 0x10, /* curve10_b */ + 0x28, /* curve10_a */ + 0x10, /* curve11_b */ + 0x28, /* curve11_a */ + 0x10, /* curve12_b */ + 0x28, /* curve12_a */ + 0x19, /* curve13_b */ + 0x22, /* curve13_a */ + 0x19, /* curve14_b */ + 0x22, /* curve14_a */ + 0x19, /* curve15_b */ + 0x22, /* curve15_a */ + 0x19, /* curve16_b */ + 0x22, /* curve16_a */ + 0x19, /* curve17_b */ + 0x22, /* curve17_a */ + 0x19, /* curve18_b */ + 0x22, /* curve18_a */ + 0x23, /* curve19_b */ + 0x1e, /* curve19_a */ + 0x2e, /* curve20_b */ + 0x1b, /* curve20_a */ + 0x33, /* curve21_b */ + 0x1a, /* curve21_a */ + 0x40, /* curve22_b */ + 0x18, /* curve22_a */ + 0x48, /* curve23_b */ + 0x17, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_HBM_CE_TEXT_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x56, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x67, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x17, + 0xd0, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x13, + 0xe2, + 0xff, /* ascr_skin_Rr */ + 0xa0, /* ascr_skin_Rg */ + 0xa0, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0x90, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_HBM_CE_TEXT_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3f, /* ascr algo lce 10 10 10 */ + 0x86, /* lce_on gain 0 00 0000 */ + 0x30, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0x90, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0xbf, + 0x00, /* lce_ref_gain 9 */ + 0xb0, + 0x77, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x00, /* lce_dark_th 000 */ + 0x40, /* lce_min_ref_offset */ + 0x06, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x60, + 0x01, /* de_maxplus 11 */ + 0x00, + 0x01, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x40, + 0x00, /* curve_1_b */ + 0x7b, /* curve_1_a */ + 0x03, /* curve_2_b */ + 0x48, /* curve_2_a */ + 0x08, /* curve_3_b */ + 0x32, /* curve_3_a */ + 0x08, /* curve_4_b */ + 0x32, /* curve_4_a */ + 0x08, /* curve_5_b */ + 0x32, /* curve_5_a */ + 0x08, /* curve_6_b */ + 0x32, /* curve_6_a */ + 0x08, /* curve_7_b */ + 0x32, /* curve_7_a */ + 0x10, /* curve_8_b */ + 0x28, /* curve_8_a */ + 0x10, /* curve_9_b */ + 0x28, /* curve_9_a */ + 0x10, /* curve10_b */ + 0x28, /* curve10_a */ + 0x10, /* curve11_b */ + 0x28, /* curve11_a */ + 0x10, /* curve12_b */ + 0x28, /* curve12_a */ + 0x19, /* curve13_b */ + 0x22, /* curve13_a */ + 0x70, /* curve14_b */ + 0xf7, /* curve14_a */ + 0x70, /* curve15_b */ + 0xf7, /* curve15_a */ + 0x70, /* curve16_b */ + 0xf7, /* curve16_a */ + 0x70, /* curve17_b */ + 0xf7, /* curve17_a */ + 0x66, /* curve18_b */ + 0x1a, /* curve18_a */ + 0x76, /* curve19_b */ + 0x14, /* curve19_a */ + 0x82, /* curve20_b */ + 0x11, /* curve20_a */ + 0x92, /* curve21_b */ + 0x0e, /* curve21_a */ + 0x98, /* curve22_b */ + 0x0d, /* curve22_a */ + 0x9f, /* curve23_b */ + 0x0c, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_SCREEN_CURTAIN_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0x00, /* ascr_Rr */ + 0x00, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0x00, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0x00, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0x00, /* ascr_Gg */ + 0x00, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0x00, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0x00, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0x00, /* ascr_Bb */ + 0x00, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0x00, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0x00, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_SCREEN_CURTAIN_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_HDR_VIDEO_1_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xe0, /* ascr_skin_Rr */ + 0x10, /* ascr_skin_Rg */ + 0x40, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf0, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf7, /* ascr_skin_Wg */ + 0xef, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xd0, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xf0, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x50, /* ascr_Yb */ + 0xe0, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_HDR_VIDEO_1_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x0f, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x00, /* de_maxplus 11 */ + 0x40, + 0x00, /* de_maxminus 11 */ + 0x40, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x10, + 0x00, /* fa_pcl_ppi 14 */ + 0x00, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0x67, /* fa_skin_cr */ + 0xa9, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0xd0, + 0x00, /* curve_1_b */ + 0x14, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x14, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x14, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x14, /* curve_4_a */ + 0x04, /* curve_5_b */ + 0x09, /* curve_5_a */ + 0x04, /* curve_6_b */ + 0x09, /* curve_6_a */ + 0x04, /* curve_7_b */ + 0x09, /* curve_7_a */ + 0x04, /* curve_8_b */ + 0x09, /* curve_8_a */ + 0x04, /* curve_9_b */ + 0x09, /* curve_9_a */ + 0x05, /* curve10_b */ + 0x92, /* curve10_a */ + 0x05, /* curve11_b */ + 0x92, /* curve11_a */ + 0x05, /* curve12_b */ + 0x92, /* curve12_a */ + 0x05, /* curve13_b */ + 0x92, /* curve13_a */ + 0x0c, /* curve14_b */ + 0x97, /* curve14_a */ + 0x0c, /* curve15_b */ + 0x97, /* curve15_a */ + 0x0c, /* curve16_b */ + 0x97, /* curve16_a */ + 0x2e, /* curve17_b */ + 0xa8, /* curve17_a */ + 0x2e, /* curve18_b */ + 0xa8, /* curve18_a */ + 0x2e, /* curve19_b */ + 0xa8, /* curve19_a */ + 0xda, /* curve20_b */ + 0xd4, /* curve20_a */ + 0x99, /* curve21_b */ + 0x0e, /* curve21_a */ + 0x99, /* curve22_b */ + 0x0e, /* curve22_a */ + 0xff, /* curve23_b */ + 0x00, /* curve23_a */ + 0x00, /* curve24_b */ + 0xff, /* curve24_a */ + /* end */ +}; + +static char DSI0_HDR_VIDEO_2_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xe0, /* ascr_skin_Rr */ + 0x10, /* ascr_skin_Rg */ + 0x40, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf0, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf7, /* ascr_skin_Wg */ + 0xef, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xd0, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xf0, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x50, /* ascr_Yb */ + 0xe0, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_HDR_VIDEO_2_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x0f, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x00, /* de_maxplus 11 */ + 0x40, + 0x00, /* de_maxminus 11 */ + 0x40, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x10, + 0x00, /* fa_pcl_ppi 14 */ + 0x00, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0x67, /* fa_skin_cr */ + 0xa9, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0xd0, + 0x00, /* curve_1_b */ + 0x14, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x14, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x14, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x14, /* curve_4_a */ + 0x04, /* curve_5_b */ + 0x09, /* curve_5_a */ + 0x04, /* curve_6_b */ + 0x09, /* curve_6_a */ + 0x04, /* curve_7_b */ + 0x09, /* curve_7_a */ + 0x04, /* curve_8_b */ + 0x09, /* curve_8_a */ + 0x04, /* curve_9_b */ + 0x09, /* curve_9_a */ + 0x05, /* curve10_b */ + 0x92, /* curve10_a */ + 0x05, /* curve11_b */ + 0x92, /* curve11_a */ + 0x05, /* curve12_b */ + 0x92, /* curve12_a */ + 0x05, /* curve13_b */ + 0x92, /* curve13_a */ + 0x0c, /* curve14_b */ + 0x97, /* curve14_a */ + 0x0c, /* curve15_b */ + 0x97, /* curve15_a */ + 0x0c, /* curve16_b */ + 0x97, /* curve16_a */ + 0x2e, /* curve17_b */ + 0xa8, /* curve17_a */ + 0x2e, /* curve18_b */ + 0xa8, /* curve18_a */ + 0x2e, /* curve19_b */ + 0xa8, /* curve19_a */ + 0xda, /* curve20_b */ + 0xd4, /* curve20_a */ + 0x99, /* curve21_b */ + 0x0e, /* curve21_a */ + 0x99, /* curve22_b */ + 0x0e, /* curve22_a */ + 0xff, /* curve23_b */ + 0x00, /* curve23_a */ + 0x00, /* curve24_b */ + 0xff, /* curve24_a */ + /* end */ +}; + +static char DSI0_HDR_VIDEO_3_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xe0, /* ascr_skin_Rr */ + 0x10, /* ascr_skin_Rg */ + 0x40, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf0, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf7, /* ascr_skin_Wg */ + 0xef, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xd0, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xf0, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x50, /* ascr_Yb */ + 0xe0, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_HDR_VIDEO_3_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x0f, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x00, /* de_maxplus 11 */ + 0x40, + 0x00, /* de_maxminus 11 */ + 0x40, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x10, + 0x00, /* fa_pcl_ppi 14 */ + 0x00, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0x67, /* fa_skin_cr */ + 0xa9, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0xd0, + 0x00, /* curve_1_b */ + 0x14, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x14, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x14, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x14, /* curve_4_a */ + 0x04, /* curve_5_b */ + 0x09, /* curve_5_a */ + 0x04, /* curve_6_b */ + 0x09, /* curve_6_a */ + 0x04, /* curve_7_b */ + 0x09, /* curve_7_a */ + 0x04, /* curve_8_b */ + 0x09, /* curve_8_a */ + 0x04, /* curve_9_b */ + 0x09, /* curve_9_a */ + 0x05, /* curve10_b */ + 0x92, /* curve10_a */ + 0x05, /* curve11_b */ + 0x92, /* curve11_a */ + 0x05, /* curve12_b */ + 0x92, /* curve12_a */ + 0x05, /* curve13_b */ + 0x92, /* curve13_a */ + 0x0c, /* curve14_b */ + 0x97, /* curve14_a */ + 0x0c, /* curve15_b */ + 0x97, /* curve15_a */ + 0x0c, /* curve16_b */ + 0x97, /* curve16_a */ + 0x2e, /* curve17_b */ + 0xa8, /* curve17_a */ + 0x2e, /* curve18_b */ + 0xa8, /* curve18_a */ + 0x2e, /* curve19_b */ + 0xa8, /* curve19_a */ + 0xda, /* curve20_b */ + 0xd4, /* curve20_a */ + 0x99, /* curve21_b */ + 0x0e, /* curve21_a */ + 0x99, /* curve22_b */ + 0x0e, /* curve22_a */ + 0xff, /* curve23_b */ + 0x00, /* curve23_a */ + 0x00, /* curve24_b */ + 0xff, /* curve24_a */ + /* end */ +}; + +static char DSI0_UI_DYNAMIC_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_UI_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_UI_STANDARD_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_UI_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_UI_NATURAL_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_UI_NATURAL_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_UI_MOVIE_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_UI_MOVIE_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_UI_AUTO_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x5c, /* ascr_skin_Rg */ + 0x68, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf8, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_UI_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x15, + 0x00, /* de_maxplus 11 */ + 0xa0, + 0x00, /* de_maxminus 11 */ + 0xa0, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static unsigned char DSI0_VIDEO_OUTDOOR_MDNIE_1[] = { + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static unsigned char DSI0_VIDEO_OUTDOOR_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VIDEO_DYNAMIC_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_VIDEO_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VIDEO_STANDARD_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_VIDEO_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VIDEO_NATURAL_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_VIDEO_NATURAL_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3C, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VIDEO_MOVIE_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_VIDEO_MOVIE_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VIDEO_AUTO_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xff, /* ascr_skin_Rr */ + 0x34, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf9, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xd8, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xd9, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xe0, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xf6, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x3b, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xd9, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x14, /* ascr_Br */ + 0xf9, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_VIDEO_AUTO_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x04, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x00, /* de_maxplus 11 */ + 0x40, + 0x00, /* de_maxminus 11 */ + 0x40, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x20, + 0x00, /* curve_1_b */ + 0x14, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x14, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x14, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x14, /* curve_4_a */ + 0x03, /* curve_5_b */ + 0x9a, /* curve_5_a */ + 0x03, /* curve_6_b */ + 0x9a, /* curve_6_a */ + 0x03, /* curve_7_b */ + 0x9a, /* curve_7_a */ + 0x03, /* curve_8_b */ + 0x9a, /* curve_8_a */ + 0x07, /* curve_9_b */ + 0x9e, /* curve_9_a */ + 0x07, /* curve10_b */ + 0x9e, /* curve10_a */ + 0x07, /* curve11_b */ + 0x9e, /* curve11_a */ + 0x07, /* curve12_b */ + 0x9e, /* curve12_a */ + 0x0a, /* curve13_b */ + 0xa0, /* curve13_a */ + 0x0a, /* curve14_b */ + 0xa0, /* curve14_a */ + 0x0a, /* curve15_b */ + 0xa0, /* curve15_a */ + 0x0a, /* curve16_b */ + 0xa0, /* curve16_a */ + 0x16, /* curve17_b */ + 0xa6, /* curve17_a */ + 0x16, /* curve18_b */ + 0xa6, /* curve18_a */ + 0x16, /* curve19_b */ + 0xa6, /* curve19_a */ + 0x16, /* curve20_b */ + 0xa6, /* curve20_a */ + 0x05, /* curve21_b */ + 0x21, /* curve21_a */ + 0x0b, /* curve22_b */ + 0x20, /* curve22_a */ + 0x87, /* curve23_b */ + 0x0f, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static unsigned char DSI0_CAMERA_OUTDOOR_MDNIE_1[] = { + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static unsigned char DSI0_CAMERA_OUTDOOR_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_CAMERA_DYNAMIC_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_CAMERA_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_CAMERA_STANDARD_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_CAMERA_STANDARD_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_CAMERA_NATURAL_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08a8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_CAMERA_NATURAL_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_CAMERA_MOVIE_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08a8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_CAMERA_MOVIE_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_CAMERA_AUTO_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xff, /* ascr_skin_Rr */ + 0x34, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf9, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xd8, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xd9, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xe0, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xf6, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x3b, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xd9, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x14, /* ascr_Br */ + 0xf9, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_CAMERA_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x15, + 0x00, /* de_maxplus 11 */ + 0xa0, + 0x00, /* de_maxminus 11 */ + 0xa0, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GALLERY_DYNAMIC_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x60, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_GALLERY_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GALLERY_STANDARD_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_GALLERY_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GALLERY_NATURAL_MDNIE_1[] ={ + //start + 0xB9, /* Start offset 0x08a8, base B1h */ + 0x60, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_GALLERY_NATURAL_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GALLERY_MOVIE_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08a8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_GALLERY_MOVIE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_GALLERY_AUTO_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xff, /* ascr_skin_Rr */ + 0x34, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf9, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xd8, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xd9, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xe0, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xf6, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x3b, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xd9, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x14, /* ascr_Br */ + 0xf9, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_GALLERY_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x50, + 0x00, /* de_maxplus 11 */ + 0x50, + 0x00, /* de_maxminus 11 */ + 0x50, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VT_DYNAMIC_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_VT_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VT_STANDARD_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_VT_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VT_NATURAL_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_VT_NATURAL_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VT_MOVIE_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_VT_MOVIE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_VT_AUTO_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_VT_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x15, + 0x00, /* de_maxplus 11 */ + 0xa0, + 0x00, /* de_maxminus 11 */ + 0xa0, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_BROWSER_DYNAMIC_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_BROWSER_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_BROWSER_STANDARD_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_BROWSER_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_BROWSER_NATURAL_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_BROWSER_NATURAL_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_BROWSER_MOVIE_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_BROWSER_MOVIE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x00, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_BROWSER_AUTO_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xff, /* ascr_skin_Rr */ + 0x34, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf9, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xd8, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xd9, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xe0, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xf6, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x3b, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xd9, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x14, /* ascr_Br */ + 0xf9, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_BROWSER_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x15, + 0x00, /* de_maxplus 11 */ + 0xa0, + 0x00, /* de_maxminus 11 */ + 0xa0, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_EBOOK_DYNAMIC_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_EBOOK_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_EBOOK_STANDARD_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_EBOOK_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_EBOOK_NATURAL_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_EBOOK_NATURAL_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_EBOOK_MOVIE_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xe9, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_EBOOK_MOVIE_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_EBOOK_AUTO_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf6, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xea, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_EBOOK_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x15, + 0x00, /* de_maxplus 11 */ + 0xa0, + 0x00, /* de_maxminus 11 */ + 0xa0, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_EMAIL_AUTO_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x00, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x0c, /* ascr_dist_up */ + 0x0c, /* ascr_dist_down */ + 0x0c, /* ascr_dist_right */ + 0x0c, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0xaa, + 0xab, + 0x00, /* ascr_div_down */ + 0xaa, + 0xab, + 0x00, /* ascr_div_right */ + 0xaa, + 0xab, + 0x00, /* ascr_div_left */ + 0xaa, + 0xab, + 0xd5, /* ascr_skin_Rr */ + 0x2c, /* ascr_skin_Rg */ + 0x2a, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf5, /* ascr_skin_Yg */ + 0x63, /* ascr_skin_Yb */ + 0xfe, /* ascr_skin_Mr */ + 0x4a, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf9, /* ascr_skin_Wg */ + 0xf8, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf9, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xed, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; +static char DSI0_EMAIL_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x30, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static unsigned char DSI0_TDMB_DYNAMIC_MDNIE_1[] = { + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 0 0000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x37, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x47, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x25, + 0x3d, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x1c, + 0xd8, + 0xff, /* ascr_skin_Rr */ + 0x60, /* ascr_skin_Rg */ + 0x82, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf4, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x64, /* ascr_Cr */ + 0xdc, /* ascr_Rr */ + 0xf4, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xe9, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xf2, /* ascr_Mr */ + 0x52, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xec, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xe8, /* ascr_Yr */ + 0x2a, /* ascr_Br */ + 0xe0, /* ascr_Yg */ + 0x32, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xd6, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_TDMB_DYNAMIC_MDNIE_2[] ={ + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x07, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x10, + 0x02, /* de_maxplus 11 */ + 0x00, + 0x02, /* de_maxminus 11 */ + 0x00, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x00, /* curve_5_b */ + 0x20, /* curve_5_a */ + 0x00, /* curve_6_b */ + 0x20, /* curve_6_a */ + 0x00, /* curve_7_b */ + 0x20, /* curve_7_a */ + 0x00, /* curve_8_b */ + 0x20, /* curve_8_a */ + 0x00, /* curve_9_b */ + 0x20, /* curve_9_a */ + 0x00, /* curve10_b */ + 0x20, /* curve10_a */ + 0x00, /* curve11_b */ + 0x20, /* curve11_a */ + 0x00, /* curve12_b */ + 0x20, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static unsigned char DSI0_TDMB_STANDARD_MDNIE_1[] = { + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xee, /* ascr_Rr */ + 0xec, /* ascr_Cg */ + 0x28, /* ascr_Rg */ + 0xe6, /* ascr_Cb */ + 0x2e, /* ascr_Rb */ + 0xfd, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x4a, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xe9, /* ascr_Mb */ + 0x10, /* ascr_Gb */ + 0xee, /* ascr_Yr */ + 0x2c, /* ascr_Br */ + 0xea, /* ascr_Yg */ + 0x30, /* ascr_Bg */ + 0x44, /* ascr_Yb */ + 0xd4, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static unsigned char DSI0_TDMB_STANDARD_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static unsigned char DSI0_TDMB_NATURAL_MDNIE_1[] = { + 0xB9, /* Start offset 0x08a8, base B1h */ + 0x20, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x50, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x8c, /* ascr_Cr */ + 0xc2, /* ascr_Rr */ + 0xe8, /* ascr_Cg */ + 0x1c, /* ascr_Rg */ + 0xe2, /* ascr_Cb */ + 0x24, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x88, /* ascr_Gr */ + 0x3f, /* ascr_Mg */ + 0xda, /* ascr_Gg */ + 0xda, /* ascr_Mb */ + 0x46, /* ascr_Gb */ + 0xe4, /* ascr_Yr */ + 0x26, /* ascr_Br */ + 0xde, /* ascr_Yg */ + 0x2e, /* ascr_Bg */ + 0x4a, /* ascr_Yb */ + 0xc8, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xf7, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xef, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static unsigned char DSI0_TDMB_NATURAL_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x00, + 0x07, /* de_maxplus 11 */ + 0xff, + 0x07, /* de_maxminus 11 */ + 0xff, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x0a, + 0x00, /* fa_step_n 10 */ + 0x32, + 0x01, /* fa_max_de_gain 10 */ + 0xf4, + 0x0b, /* fa_pcl_ppi 14 */ + 0x8a, + 0x20, /* fa_os_cnt_10_co */ + 0x2d, /* fa_os_cnt_20_co */ + 0x6e, /* fa_skin_cr */ + 0x99, /* fa_skin_cb */ + 0x1b, /* fa_dist_left */ + 0x17, /* fa_dist_right */ + 0x14, /* fa_dist_down */ + 0x1e, /* fa_dist_up */ + 0x02, /* fa_div_dist_left */ + 0x5f, + 0x02, /* fa_div_dist_right */ + 0xc8, + 0x03, /* fa_div_dist_down */ + 0x33, + 0x02, /* fa_div_dist_up */ + 0x22, + 0x10, /* fa_px_min_weight */ + 0x10, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; +static unsigned char DSI0_TDMB_AUTO_MDNIE_1[] = { + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x6a, /* ascr_skin_cb */ + 0x9a, /* ascr_skin_cr */ + 0x25, /* ascr_dist_up */ + 0x1a, /* ascr_dist_down */ + 0x16, /* ascr_dist_right */ + 0x2a, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x37, + 0x5a, + 0x00, /* ascr_div_down */ + 0x4e, + 0xc5, + 0x00, /* ascr_div_right */ + 0x5d, + 0x17, + 0x00, /* ascr_div_left */ + 0x30, + 0xc3, + 0xff, /* ascr_skin_Rr */ + 0x34, /* ascr_skin_Rg */ + 0x60, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xf9, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xd8, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xd9, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xff, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xe0, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xf6, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xd8, /* ascr_Mr */ + 0x3b, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xd9, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x14, /* ascr_Br */ + 0xf9, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static unsigned char DSI0_TDMB_AUTO_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x50, + 0x00, /* de_maxplus 11 */ + 0x50, + 0x00, /* de_maxminus 11 */ + 0x50, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static char DSI0_RGB_SENSOR_MDNIE_1[] = { + //start + 0xB9, /* Start offset 0x08A8, base B1h */ + 0x50, /* ascr_skin_on linear_on strength 0 0 00000 */ + 0x67, /* ascr_skin_cb */ + 0xa9, /* ascr_skin_cr */ + 0x17, /* ascr_dist_up */ + 0x29, /* ascr_dist_down */ + 0x19, /* ascr_dist_right */ + 0x27, /* ascr_dist_left */ + 0x00, /* ascr_div_up 20 */ + 0x59, + 0x0b, + 0x00, /* ascr_div_down */ + 0x31, + 0xf4, + 0x00, /* ascr_div_right */ + 0x51, + 0xec, + 0x00, /* ascr_div_left */ + 0x34, + 0x83, + 0xff, /* ascr_skin_Rr */ + 0x58, /* ascr_skin_Rg */ + 0x7b, /* ascr_skin_Rb */ + 0xff, /* ascr_skin_Yr */ + 0xff, /* ascr_skin_Yg */ + 0x00, /* ascr_skin_Yb */ + 0xff, /* ascr_skin_Mr */ + 0x00, /* ascr_skin_Mg */ + 0xff, /* ascr_skin_Mb */ + 0xff, /* ascr_skin_Wr */ + 0xf8, /* ascr_skin_Wg */ + 0xff, /* ascr_skin_Wb */ + 0x00, /* ascr_Cr */ + 0xff, /* ascr_Rr */ + 0xff, /* ascr_Cg */ + 0x00, /* ascr_Rg */ + 0xff, /* ascr_Cb */ + 0x00, /* ascr_Rb */ + 0xff, /* ascr_Mr */ + 0x00, /* ascr_Gr */ + 0x00, /* ascr_Mg */ + 0xff, /* ascr_Gg */ + 0xff, /* ascr_Mb */ + 0x00, /* ascr_Gb */ + 0xff, /* ascr_Yr */ + 0x00, /* ascr_Br */ + 0xff, /* ascr_Yg */ + 0x00, /* ascr_Bg */ + 0x00, /* ascr_Yb */ + 0xff, /* ascr_Bb */ + 0xff, /* ascr_Wr */ + 0x00, /* ascr_Kr */ + 0xff, /* ascr_Wg */ + 0x00, /* ascr_Kg */ + 0xff, /* ascr_Wb */ + 0x00, /* ascr_Kb */ + /* end */ +}; + +static char DSI0_RGB_SENSOR_MDNIE_2[] = { + /* start */ + 0xB9, /* Start offset 0x0842, base B1h */ + 0x01, /* mdnie_en */ + 0x00, /* data_width mask 00 0000 */ + 0x3c, /* ascr algo lce 10 10 10 */ + 0x98, /* lce_on gain 0 00 0000 */ + 0x24, /* lce_color_gain 00 0000 */ + 0x00, /* lce_scene_trans 0000 */ + 0xb3, /* lce_illum_gain */ + 0x01, /* lce_ref_offset 9 */ + 0x0e, + 0x01, /* lce_ref_gain 9 */ + 0x00, + 0x66, /* lce_block_size h v 000 000 */ + 0x17, /* lce_black reduct_slope 0 0000 */ + 0x03, /* lce_dark_th 000 */ + 0x96, /* lce_min_ref_offset */ + 0x00, /* nr fa de cs gamma 0 0000 */ + 0xff, /* nr_mask_th */ + 0x00, /* de_gain 10 */ + 0x15, + 0x00, /* de_maxplus 11 */ + 0xa0, + 0x00, /* de_maxminus 11 */ + 0xa0, + 0x14, /* fa_edge_th */ + 0x00, /* fa_step_p 10 */ + 0x01, + 0x00, /* fa_step_n 10 */ + 0x01, + 0x00, /* fa_max_de_gain 10 */ + 0x70, + 0x01, /* fa_pcl_ppi 14 */ + 0xc0, + 0x28, /* fa_os_cnt_10_co */ + 0x3c, /* fa_os_cnt_20_co */ + 0xa9, /* fa_skin_cr */ + 0x67, /* fa_skin_cb */ + 0x27, /* fa_dist_left */ + 0x19, /* fa_dist_right */ + 0x29, /* fa_dist_down */ + 0x17, /* fa_dist_up */ + 0x01, /* fa_div_dist_left */ + 0xa4, + 0x02, /* fa_div_dist_right */ + 0x8f, + 0x01, /* fa_div_dist_down */ + 0x90, + 0x02, /* fa_div_dist_up */ + 0xc8, + 0x20, /* fa_px_min_weight */ + 0x20, /* fa_fr_min_weight */ + 0x07, /* fa_skin_zone_w */ + 0x07, /* fa_skin_zone_h */ + 0x01, /* cs_gain 10 */ + 0x00, + 0x00, /* curve_1_b */ + 0x20, /* curve_1_a */ + 0x00, /* curve_2_b */ + 0x20, /* curve_2_a */ + 0x00, /* curve_3_b */ + 0x20, /* curve_3_a */ + 0x00, /* curve_4_b */ + 0x20, /* curve_4_a */ + 0x02, /* curve_5_b */ + 0x1b, /* curve_5_a */ + 0x02, /* curve_6_b */ + 0x1b, /* curve_6_a */ + 0x02, /* curve_7_b */ + 0x1b, /* curve_7_a */ + 0x02, /* curve_8_b */ + 0x1b, /* curve_8_a */ + 0x09, /* curve_9_b */ + 0xa6, /* curve_9_a */ + 0x09, /* curve10_b */ + 0xa6, /* curve10_a */ + 0x09, /* curve11_b */ + 0xa6, /* curve11_a */ + 0x09, /* curve12_b */ + 0xa6, /* curve12_a */ + 0x00, /* curve13_b */ + 0x20, /* curve13_a */ + 0x00, /* curve14_b */ + 0x20, /* curve14_a */ + 0x00, /* curve15_b */ + 0x20, /* curve15_a */ + 0x00, /* curve16_b */ + 0x20, /* curve16_a */ + 0x00, /* curve17_b */ + 0x20, /* curve17_a */ + 0x00, /* curve18_b */ + 0x20, /* curve18_a */ + 0x00, /* curve19_b */ + 0x20, /* curve19_a */ + 0x00, /* curve20_b */ + 0x20, /* curve20_a */ + 0x00, /* curve21_b */ + 0x20, /* curve21_a */ + 0x00, /* curve22_b */ + 0x20, /* curve22_a */ + 0x00, /* curve23_b */ + 0x20, /* curve23_a */ + 0x00, /* curve24_b */ + 0xFF, /* curve24_a */ + /* end */ +}; + +static struct dsi_cmd_desc DSI0_BYPASS_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BYPASS_MDNIE_1), DSI0_BYPASS_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BYPASS_MDNIE_2), DSI0_BYPASS_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_NEGATIVE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_NEGATIVE_MDNIE_1), DSI0_NEGATIVE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_NEGATIVE_MDNIE_2), DSI0_NEGATIVE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GRAYSCALE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GRAYSCALE_MDNIE_1), DSI0_GRAYSCALE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GRAYSCALE_MDNIE_2), DSI0_GRAYSCALE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GRAYSCALE_NEGATIVE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GRAYSCALE_NEGATIVE_MDNIE_1), DSI0_GRAYSCALE_NEGATIVE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GRAYSCALE_NEGATIVE_MDNIE_2), DSI0_GRAYSCALE_NEGATIVE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_COLOR_BLIND_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_COLOR_BLIND_MDNIE_1), DSI0_COLOR_BLIND_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_COLOR_BLIND_MDNIE_2), DSI0_COLOR_BLIND_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_NIGHT_MODE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_NIGHT_MODE_MDNIE_1), DSI0_NIGHT_MODE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_NIGHT_MODE_MDNIE_2), DSI0_NIGHT_MODE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_COLOR_LENS_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_COLOR_LENS_MDNIE_1), DSI0_COLOR_LENS_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_COLOR_LENS_MDNIE_2), DSI0_COLOR_LENS_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_LIGHT_NOTIFICATION_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_LIGHT_NOTIFICATION_MDNIE_1), DSI0_LIGHT_NOTIFICATION_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_LIGHT_NOTIFICATION_MDNIE_2), DSI0_LIGHT_NOTIFICATION_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_HBM_CE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HBM_CE_MDNIE_1), DSI0_HBM_CE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HBM_CE_MDNIE_2), DSI0_HBM_CE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_HBM_CE_TEXT_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HBM_CE_TEXT_MDNIE_1), DSI0_HBM_CE_TEXT_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HBM_CE_TEXT_MDNIE_2), DSI0_HBM_CE_TEXT_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_RGB_SENSOR_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_RGB_SENSOR_MDNIE_1), DSI0_RGB_SENSOR_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_RGB_SENSOR_MDNIE_2), DSI0_RGB_SENSOR_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CURTAIN[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_SCREEN_CURTAIN_MDNIE_1), DSI0_SCREEN_CURTAIN_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_SCREEN_CURTAIN_MDNIE_2), DSI0_SCREEN_CURTAIN_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_HDR_VIDEO_1_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HDR_VIDEO_1_MDNIE_1), DSI0_HDR_VIDEO_1_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HDR_VIDEO_1_MDNIE_2), DSI0_HDR_VIDEO_1_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_HDR_VIDEO_2_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HDR_VIDEO_2_MDNIE_1), DSI0_HDR_VIDEO_2_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HDR_VIDEO_2_MDNIE_2), DSI0_HDR_VIDEO_2_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_HDR_VIDEO_3_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HDR_VIDEO_3_MDNIE_1), DSI0_HDR_VIDEO_3_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_HDR_VIDEO_3_MDNIE_2), DSI0_HDR_VIDEO_3_MDNIE_2, 0, NULL}, false, 0}, +}; + +/////////////////////////////////////////////////////////////////////////////////// + +static struct dsi_cmd_desc DSI0_UI_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_DYNAMIC_MDNIE_1), DSI0_UI_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_DYNAMIC_MDNIE_2), DSI0_UI_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_UI_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_STANDARD_MDNIE_1), DSI0_UI_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_STANDARD_MDNIE_2), DSI0_UI_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_UI_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_NATURAL_MDNIE_1), DSI0_UI_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_NATURAL_MDNIE_2), DSI0_UI_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_UI_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_MOVIE_MDNIE_1), DSI0_UI_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_MOVIE_MDNIE_2), DSI0_UI_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_UI_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_AUTO_MDNIE_1), DSI0_UI_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_UI_AUTO_MDNIE_2), DSI0_UI_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VIDEO_OUTDOOR_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_OUTDOOR_MDNIE_1), DSI0_VIDEO_OUTDOOR_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_OUTDOOR_MDNIE_2), DSI0_VIDEO_OUTDOOR_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VIDEO_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_DYNAMIC_MDNIE_1), DSI0_VIDEO_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_DYNAMIC_MDNIE_2), DSI0_VIDEO_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VIDEO_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_STANDARD_MDNIE_1), DSI0_VIDEO_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_STANDARD_MDNIE_2), DSI0_VIDEO_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VIDEO_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_NATURAL_MDNIE_1), DSI0_VIDEO_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_NATURAL_MDNIE_2), DSI0_VIDEO_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VIDEO_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_MOVIE_MDNIE_1), DSI0_VIDEO_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_MOVIE_MDNIE_2), DSI0_VIDEO_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VIDEO_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_AUTO_MDNIE_1), DSI0_VIDEO_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VIDEO_AUTO_MDNIE_2), DSI0_VIDEO_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CAMERA_OUTDOOR_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_OUTDOOR_MDNIE_1), DSI0_CAMERA_OUTDOOR_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_OUTDOOR_MDNIE_2), DSI0_CAMERA_OUTDOOR_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CAMERA_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_DYNAMIC_MDNIE_1), DSI0_CAMERA_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_DYNAMIC_MDNIE_2), DSI0_CAMERA_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CAMERA_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_STANDARD_MDNIE_1), DSI0_CAMERA_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_STANDARD_MDNIE_2), DSI0_CAMERA_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CAMERA_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_NATURAL_MDNIE_1), DSI0_CAMERA_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_NATURAL_MDNIE_2), DSI0_CAMERA_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CAMERA_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_MOVIE_MDNIE_1), DSI0_CAMERA_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_MOVIE_MDNIE_2), DSI0_CAMERA_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_CAMERA_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_AUTO_MDNIE_1), DSI0_CAMERA_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_CAMERA_AUTO_MDNIE_2), DSI0_CAMERA_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GALLERY_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_DYNAMIC_MDNIE_1), DSI0_GALLERY_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_DYNAMIC_MDNIE_2), DSI0_GALLERY_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GALLERY_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_STANDARD_MDNIE_1), DSI0_GALLERY_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_STANDARD_MDNIE_2), DSI0_GALLERY_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GALLERY_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_NATURAL_MDNIE_1), DSI0_GALLERY_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_NATURAL_MDNIE_2), DSI0_GALLERY_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GALLERY_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_MOVIE_MDNIE_1), DSI0_GALLERY_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_MOVIE_MDNIE_2), DSI0_GALLERY_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_GALLERY_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_AUTO_MDNIE_1), DSI0_GALLERY_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_GALLERY_AUTO_MDNIE_2), DSI0_GALLERY_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VT_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_DYNAMIC_MDNIE_1), DSI0_VT_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_DYNAMIC_MDNIE_2), DSI0_VT_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VT_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_STANDARD_MDNIE_1), DSI0_VT_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_STANDARD_MDNIE_2), DSI0_VT_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VT_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_NATURAL_MDNIE_1), DSI0_VT_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_NATURAL_MDNIE_2), DSI0_VT_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VT_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_MOVIE_MDNIE_1), DSI0_VT_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_MOVIE_MDNIE_2), DSI0_VT_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_VT_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_AUTO_MDNIE_1), DSI0_VT_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_VT_AUTO_MDNIE_2), DSI0_VT_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_BROWSER_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_DYNAMIC_MDNIE_1), DSI0_BROWSER_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_DYNAMIC_MDNIE_2), DSI0_BROWSER_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_BROWSER_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_STANDARD_MDNIE_1), DSI0_BROWSER_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_STANDARD_MDNIE_2), DSI0_BROWSER_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_BROWSER_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_NATURAL_MDNIE_1), DSI0_BROWSER_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_NATURAL_MDNIE_2), DSI0_BROWSER_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_BROWSER_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_MOVIE_MDNIE_1), DSI0_BROWSER_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_MOVIE_MDNIE_2), DSI0_BROWSER_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_BROWSER_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_AUTO_MDNIE_1), DSI0_BROWSER_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_BROWSER_AUTO_MDNIE_2), DSI0_BROWSER_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_EBOOK_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_DYNAMIC_MDNIE_1), DSI0_EBOOK_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_DYNAMIC_MDNIE_2), DSI0_EBOOK_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_EBOOK_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_STANDARD_MDNIE_1), DSI0_EBOOK_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_STANDARD_MDNIE_2), DSI0_EBOOK_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_EBOOK_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_NATURAL_MDNIE_1), DSI0_EBOOK_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_NATURAL_MDNIE_2), DSI0_EBOOK_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_EBOOK_MOVIE_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_MOVIE_MDNIE_1), DSI0_EBOOK_MOVIE_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_MOVIE_MDNIE_2), DSI0_EBOOK_MOVIE_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_EBOOK_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_AUTO_MDNIE_1), DSI0_EBOOK_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EBOOK_AUTO_MDNIE_2), DSI0_EBOOK_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_EMAIL_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EMAIL_AUTO_MDNIE_1), DSI0_EMAIL_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_EMAIL_AUTO_MDNIE_2), DSI0_EMAIL_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_TDMB_DYNAMIC_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_DYNAMIC_MDNIE_1), DSI0_TDMB_DYNAMIC_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_DYNAMIC_MDNIE_2), DSI0_TDMB_DYNAMIC_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_TDMB_STANDARD_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_STANDARD_MDNIE_1), DSI0_TDMB_STANDARD_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_STANDARD_MDNIE_2), DSI0_TDMB_STANDARD_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_TDMB_NATURAL_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_NATURAL_MDNIE_1), DSI0_TDMB_NATURAL_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_NATURAL_MDNIE_2), DSI0_TDMB_NATURAL_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc DSI0_TDMB_AUTO_MDNIE[] = { + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_1), mdnie_address_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_AUTO_MDNIE_1), DSI0_TDMB_AUTO_MDNIE_1, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0, 0, 0, sizeof(mdnie_address_2), mdnie_address_2, 0, NULL}, false, 0}, + {{0, MIPI_DSI_DCS_LONG_WRITE, 0, 0, 0, sizeof(DSI0_TDMB_AUTO_MDNIE_2), DSI0_TDMB_AUTO_MDNIE_2, 0, NULL}, false, 0}, +}; + +static struct dsi_cmd_desc *mdnie_tune_value_dsi0[MAX_APP_MODE][MAX_MODE][MAX_OUTDOOR_MODE] = { + /* + DYNAMIC_MODE + STANDARD_MODE + NATURAL_MODE + MOVIE_MODE + AUTO_MODE + READING_MODE + */ + // UI_APP + { + {DSI0_UI_DYNAMIC_MDNIE, NULL}, + {DSI0_UI_STANDARD_MDNIE, NULL}, + {DSI0_UI_NATURAL_MDNIE, NULL}, + {DSI0_UI_MOVIE_MDNIE, NULL}, + {DSI0_UI_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, + // VIDEO_APP + { + {DSI0_VIDEO_DYNAMIC_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_STANDARD_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_NATURAL_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_MOVIE_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_AUTO_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_EBOOK_AUTO_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + }, + // VIDEO_WARM_APP + { + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_EBOOK_AUTO_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + }, + // VIDEO_COLD_APP + { + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_VIDEO_OUTDOOR_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + {DSI0_EBOOK_AUTO_MDNIE, DSI0_VIDEO_OUTDOOR_MDNIE}, + }, + // CAMERA_APP + { + {DSI0_CAMERA_DYNAMIC_MDNIE, DSI0_CAMERA_OUTDOOR_MDNIE}, + {DSI0_CAMERA_STANDARD_MDNIE, DSI0_CAMERA_OUTDOOR_MDNIE}, + {DSI0_CAMERA_NATURAL_MDNIE, DSI0_CAMERA_OUTDOOR_MDNIE}, + {DSI0_CAMERA_MOVIE_MDNIE, DSI0_CAMERA_OUTDOOR_MDNIE}, + {DSI0_CAMERA_AUTO_MDNIE, DSI0_CAMERA_OUTDOOR_MDNIE}, + {DSI0_EBOOK_AUTO_MDNIE, DSI0_CAMERA_OUTDOOR_MDNIE}, + }, + // NAVI_APP + { + {NULL, NULL}, + {NULL, NULL}, + {NULL, NULL}, + {NULL, NULL}, + {NULL, NULL}, + {NULL, NULL}, + }, + // GALLERY_APP + { + {DSI0_GALLERY_DYNAMIC_MDNIE, NULL}, + {DSI0_GALLERY_STANDARD_MDNIE, NULL}, + {DSI0_GALLERY_NATURAL_MDNIE, NULL}, + {DSI0_GALLERY_MOVIE_MDNIE, NULL}, + {DSI0_GALLERY_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, + // VT_APP + { + {DSI0_VT_DYNAMIC_MDNIE, NULL}, + {DSI0_VT_STANDARD_MDNIE, NULL}, + {DSI0_VT_NATURAL_MDNIE, NULL}, + {DSI0_VT_MOVIE_MDNIE, NULL}, + {DSI0_VT_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, + // BROWSER_APP + { + {DSI0_BROWSER_DYNAMIC_MDNIE, NULL}, + {DSI0_BROWSER_STANDARD_MDNIE, NULL}, + {DSI0_BROWSER_NATURAL_MDNIE, NULL}, + {DSI0_BROWSER_MOVIE_MDNIE, NULL}, + {DSI0_BROWSER_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, + // eBOOK_APP + { + {DSI0_EBOOK_DYNAMIC_MDNIE, NULL}, + {DSI0_EBOOK_STANDARD_MDNIE,NULL}, + {DSI0_EBOOK_NATURAL_MDNIE, NULL}, + {DSI0_EBOOK_MOVIE_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, + // EMAIL_APP + { + {DSI0_EMAIL_AUTO_MDNIE, NULL}, + {DSI0_EMAIL_AUTO_MDNIE, NULL}, + {DSI0_EMAIL_AUTO_MDNIE, NULL}, + {DSI0_EMAIL_AUTO_MDNIE, NULL}, + {DSI0_EMAIL_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, + // TDMB_APP + { + {DSI0_TDMB_DYNAMIC_MDNIE, NULL}, + {DSI0_TDMB_STANDARD_MDNIE, NULL}, + {DSI0_TDMB_NATURAL_MDNIE, NULL}, + {DSI0_TDMB_NATURAL_MDNIE, NULL}, + {DSI0_TDMB_AUTO_MDNIE, NULL}, + {DSI0_EBOOK_AUTO_MDNIE, NULL}, + }, +}; + +static struct dsi_cmd_desc *light_notification_tune_value_dsi0[LIGHT_NOTIFICATION_MAX] = { + NULL, + DSI0_LIGHT_NOTIFICATION_MDNIE, +}; + +static struct dsi_cmd_desc *hdr_tune_value_dsi0[HDR_MAX] = { + NULL, + DSI0_HDR_VIDEO_1_MDNIE, + DSI0_HDR_VIDEO_2_MDNIE, + DSI0_HDR_VIDEO_3_MDNIE, +}; + + +#define DSI0_RGB_SENSOR_MDNIE_1_SIZE ARRAY_SIZE(DSI0_RGB_SENSOR_MDNIE_1) +#define DSI0_RGB_SENSOR_MDNIE_2_SIZE ARRAY_SIZE(DSI0_RGB_SENSOR_MDNIE_2) + +#endif diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_panel_ANA38401_AMSA05RB01.c b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_panel_ANA38401_AMSA05RB01.c new file mode 100755 index 000000000000..908970a91c75 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_panel_ANA38401_AMSA05RB01.c @@ -0,0 +1,1583 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: wu.deng + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +Copyright (C) 2017, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * +*/ +#include "ss_dsi_panel_ANA38401_AMSA05RB01.h" +#include "ss_dsi_mdnie_ANA38401_AMSA05RB01.h" + +static char hbm_buffer1[33] = {0,}; + +static int samsung_panel_on_pre(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *on_cmds = ss_get_cmds(vdd, DSI_CMD_SET_ON); /* mdss-dsi-on-command */ + static bool check_first = true; + + LCD_INFO("++\n"); + if (check_first == true) { + LCD_INFO("Generate_gamma for max Candela\n"); + + /* BL Gamma OFFSET is Default, Kernel Gamma OFFSET is 4, so first On_PRE makes abnormal Color. */ + /* ANA38401_AMSA05RB01 must use Gamma Value after SmartDimming instead of mdss-dsi-on-command's Default Brigtness setting. */ + vdd->smart_dimming_dsi->generate_gamma( + vdd->smart_dimming_dsi, + 360, /* MAX_CANDELA */ + &on_cmds->cmds[4].msg.tx_buf[1]); + check_first = false; + } + ss_panel_attach_set(vdd, true); + LCD_INFO("--\n"); + + return true; +} + +static int samsung_panel_on_post(struct samsung_display_driver_data *vdd) +{ + return true; +} + +static char ss_panel_revision(struct samsung_display_driver_data *vdd) +{ + if (vdd->manufacture_id_dsi == PBA_ID) + ss_panel_attach_set(vdd, false); + else + ss_panel_attach_set(vdd, true); + + switch (ss_panel_rev_get(vdd)) { + case 0x00: + vdd->panel_revision = 'A'; + break; + default: + vdd->panel_revision = 'A'; + LCD_ERR("Invalid panel_rev(default rev : %c)\n", + vdd->panel_revision); + break; + } + + vdd->panel_revision -= 'A'; + + LCD_INFO_ONCE("panel_revision = %c %d \n", + vdd->panel_revision + 'A', vdd->panel_revision); + + return (vdd->panel_revision + 'A'); +} + +static int ss_manufacture_date_read(struct samsung_display_driver_data *vdd) +{ + unsigned char date[8] = {0,}; + int year, month, day; + int hour, min; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + /* Read mtp (D8h 134 ~ 140th Para) for manufacture date */ + if (ss_get_cmds(vdd, RX_MANUFACTURE_DATE)->count) { + ss_panel_data_read(vdd, RX_MANUFACTURE_DATE, date, LEVEL_KEY_NONE); + + year = date[0] & 0xf0; + year >>= 4; + year += 2011; // 0 = 2011 year + month = date[0] & 0x0f; + day = date[1] & 0x1f; + hour = date[2]& 0x0f; + min = date[3] & 0x1f; + + vdd->manufacture_date_dsi = year * 10000 + month * 100 + day; + vdd->manufacture_time_dsi = hour * 100 + min; + + LCD_ERR("manufacture_date DSI%d = (%d%04d) - year(%d) month(%d) day(%d) hour(%d) min(%d)\n", + vdd->ndx, vdd->manufacture_date_dsi, vdd->manufacture_time_dsi, + year, month, day, hour, min); + } else { + LCD_ERR("DSI%d no manufacture_date_rx_cmds cmds(%d)", vdd->ndx, vdd->panel_revision); + return false; + } + + return true; +} + +static int ss_ddi_id_read(struct samsung_display_driver_data *vdd) +{ + char ddi_id[5]; + int loop; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd); + return false; + } + + /* Read mtp (D6h 1~5th) for ddi id */ + if (ss_get_cmds(vdd, RX_DDI_ID)->count) { + ss_panel_data_read(vdd, RX_DDI_ID, ddi_id, LEVEL_KEY_NONE); + + for(loop = 0; loop < 5; loop++) + vdd->ddi_id_dsi[loop] = ddi_id[loop]; + + LCD_INFO("DSI%d : %02x %02x %02x %02x %02x\n", vdd->ndx, + vdd->ddi_id_dsi[0], vdd->ddi_id_dsi[1], + vdd->ddi_id_dsi[2], vdd->ddi_id_dsi[3], + vdd->ddi_id_dsi[4]); + } else { + LCD_ERR("DSI%d no ddi_id_rx_cmds cmds\n", vdd->ndx); + return false; + } + + return true; +} + +static int ss_cell_id_read(struct samsung_display_driver_data *vdd) +{ + char cell_id_buffer[MAX_CELL_ID] = {0,}; + int loop; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + /* Read Panel Unique Cell ID (0xD8 : 134th para ~ 140th para) */ + if (ss_get_cmds(vdd, RX_CELL_ID)->count) { + memset(cell_id_buffer, 0x00, MAX_CELL_ID); + + ss_panel_data_read(vdd, RX_CELL_ID, cell_id_buffer, LEVEL_KEY_NONE); + + for(loop = 0; loop < MAX_CELL_ID; loop++) + vdd->cell_id_dsi[loop] = cell_id_buffer[loop]; + + LCD_INFO("DSI%d: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + vdd->ndx, vdd->cell_id_dsi[0], + vdd->cell_id_dsi[1], vdd->cell_id_dsi[2], + vdd->cell_id_dsi[3], vdd->cell_id_dsi[4], + vdd->cell_id_dsi[5], vdd->cell_id_dsi[6], + vdd->cell_id_dsi[7], vdd->cell_id_dsi[8], + vdd->cell_id_dsi[9], vdd->cell_id_dsi[10]); + } else { + LCD_ERR("DSI%d no cell_id_rx_cmds cmd\n", vdd->ndx); + return false; + } + + return true; +} + +#if 0 // From : ss_dsi_panel_S6E3FA7_AMS628RF01.c +static int ss_octa_id_read(struct samsung_display_driver_data *vdd) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + /* Read Panel Unique OCTA ID (C9h 2nd~21th) */ + if (ss_get_cmds(vdd, RX_OCTA_ID)->count) { + memset(vdd->octa_id_dsi, 0x00, MAX_OCTA_ID); + + ss_panel_data_read(vdd, RX_OCTA_ID, + vdd->octa_id_dsi, LEVEL1_KEY); + + LCD_INFO("octa id: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + vdd->octa_id_dsi[0], vdd->octa_id_dsi[1], + vdd->octa_id_dsi[2], vdd->octa_id_dsi[3], + vdd->octa_id_dsi[4], vdd->octa_id_dsi[5], + vdd->octa_id_dsi[6], vdd->octa_id_dsi[7], + vdd->octa_id_dsi[8], vdd->octa_id_dsi[9], + vdd->octa_id_dsi[10], vdd->octa_id_dsi[11], + vdd->octa_id_dsi[12], vdd->octa_id_dsi[13], + vdd->octa_id_dsi[14], vdd->octa_id_dsi[15], + vdd->octa_id_dsi[16], vdd->octa_id_dsi[17], + vdd->octa_id_dsi[18], vdd->octa_id_dsi[19]); + + } else { + LCD_ERR("DSI%d no octa_id_rx_cmds cmd\n", vdd->ndx); + return false; + } + + return true; +} + +static int ss_elvss_read(struct samsung_display_driver_data *vdd) +{ + char elvss_b5[2]; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + /* Read mtp (B5h 23th,24th) for elvss*/ + ss_panel_data_read(vdd, RX_ELVSS, elvss_b5, LEVEL1_KEY); + + vdd->display_status_dsi.elvss_value1 = elvss_b5[0]; /*0xB5 23th OTP value*/ + vdd->display_status_dsi.elvss_value2 = elvss_b5[1]; /*0xB5 24th */ + + return true; +} + +static struct dsi_panel_cmd_set *ss_vint(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *vint_cmds = ss_get_cmds(vdd, TX_VINT); + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vint_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)vint_cmds); + return NULL; + } + + /* TODO: implement xtalk_mode + if (vdd->xtalk_mode) + cmd_index = 1; // VGH 6.2 V + else + cmd_index = 0; // VGH 7.0 V + */ + + *level_key = LEVEL1_KEY; + + return vint_cmds; +} +#endif + +static int ss_hbm_read(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *hbm_rx_cmds = ss_get_cmds(vdd, RX_HBM); + struct dsi_panel_cmd_set *hbm_etc_cmds = ss_get_cmds(vdd, TX_HBM_ETC); + struct dsi_panel_cmd_set *hbm_off_cmds = ss_get_cmds(vdd, TX_HBM_OFF); + + char hbm_buffer2[1] = {0,}; + char hbm_off_buffer[1] = {0,}; + char log_buf[256] = {0,}; + u32 i; + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(hbm_rx_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)hbm_rx_cmds); + return false; + } + + /* Read mtp (D4h 91~123th) for hbm gamma */ + ss_panel_data_read(vdd, RX_HBM, hbm_buffer1, LEVEL_KEY_NONE); + + hbm_rx_cmds->read_startoffset = 0x63; // 99 + ss_panel_data_read(vdd, RX_HBM, hbm_buffer1 + 8, LEVEL_KEY_NONE); + + hbm_rx_cmds->read_startoffset = 0x6B; // 107 + ss_panel_data_read(vdd, RX_HBM, hbm_buffer1 + 16, LEVEL_KEY_NONE); + + hbm_rx_cmds->read_startoffset = 0x73; // 115 + ss_panel_data_read(vdd, RX_HBM, hbm_buffer1 + 24, LEVEL_KEY_NONE); + + hbm_rx_cmds->read_startoffset = 0x7B; // 123 + hbm_rx_cmds->cmds[0].msg.rx_len = 1; + hbm_rx_cmds->cmds[0].msg.tx_buf[1] = 1; + ss_panel_data_read(vdd, RX_HBM, hbm_buffer1 + 32, LEVEL_KEY_NONE); + + memset(log_buf, 0x00, 256); + for(i = 0; i < 33; i++) + snprintf(log_buf + strnlen(log_buf, 256), 256, " %02x", hbm_buffer1[i]); + LCD_INFO("MTP data for HBM Read From D4 =%s\n", log_buf); + + /* Read mtp (D8h 129th) for HBM On elvss*/ + ss_panel_data_read(vdd, RX_HBM2, hbm_buffer2, LEVEL_KEY_NONE); + memcpy(&hbm_etc_cmds->cmds[2].msg.tx_buf[1], hbm_buffer2, 1); + + /* Read mtp (D2h 112th) for HBM Off elvss*/ + ss_panel_data_read(vdd, RX_ELVSS, hbm_off_buffer, LEVEL_KEY_NONE); + memcpy(&hbm_off_cmds->cmds[1].msg.tx_buf[1], hbm_off_buffer, 1); + + return true; +} + +static struct dsi_panel_cmd_set *ss_hbm_gamma(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *hbm_gamma_cmds = ss_get_cmds(vdd, TX_HBM_GAMMA); + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(hbm_gamma_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmd : 0x%zx", (size_t)vdd, (size_t)hbm_gamma_cmds); + return NULL; + } + + if (IS_ERR_OR_NULL(vdd->smart_dimming_dsi->generate_hbm_gamma)) { + LCD_ERR("generate_hbm_gamma is NULL error"); + return NULL; + } else { + vdd->smart_dimming_dsi->generate_hbm_gamma( + vdd->smart_dimming_dsi, + vdd->br.auto_level, + &hbm_gamma_cmds->cmds[0].msg.tx_buf[1]); + + *level_key = LEVEL_KEY_NONE; + return hbm_gamma_cmds; + } +} + +static struct dsi_panel_cmd_set *ss_hbm_etc(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *hbm_etc_cmds = ss_get_cmds(vdd, TX_HBM_ETC); + int elvss_dim_off; + int elvss_comp; + int acl_opr; + int acl_start; + int acl_percent; + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(hbm_etc_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmd : 0x%zx", (size_t)vdd, (size_t)hbm_etc_cmds); + return NULL; + } + + *level_key = LEVEL_KEY_NONE; + + /* HBM ELVSS */ + /* + * To Do : In the Rev A OP manual there are no elvss dim offset + * settings for -20 < T <= 0 and T <= -20 . In the future when new OP manual + * is released with these settings then , + * this code needs to be updated for -20 < T <= 0 and T <= -20 cases + */ + + switch(vdd->br.auto_level) { + case 6: /*378*/ + if (vdd->temperature > 0) elvss_dim_off = 0x1A; + else if (vdd->temperature > -20) elvss_dim_off = 0x0E; + else elvss_dim_off = 0x0C; + break; + case 7: /*395*/ + if (vdd->temperature > 0) elvss_dim_off = 0x18; + else if (vdd->temperature > -20) elvss_dim_off = 0x0D; + else elvss_dim_off = 0x0C; + break; + case 8: /*413*/ + if (vdd->temperature > 0) elvss_dim_off = 0x16; + else if (vdd->temperature > -20) elvss_dim_off = 0x0C; + else elvss_dim_off = 0x0C; + break; + case 9: /*430*/ + if (vdd->temperature > 0) elvss_dim_off = 0x15; + else if (vdd->temperature > -20) elvss_dim_off = 0x0C; + else elvss_dim_off = 0x0C; + break; + case 10: /*448*/ + if (vdd->temperature > 0) elvss_dim_off = 0x13; + else if (vdd->temperature > -20) elvss_dim_off = 0x0C; + else elvss_dim_off = 0x0C; + break; + case 11: /*465*/ + if (vdd->temperature > 0) elvss_dim_off = 0x12; + else if (vdd->temperature > -20) elvss_dim_off = 0x0C; + else elvss_dim_off = 0x0C; + break; + case 12: /*483*/ + if (vdd->temperature > 0) elvss_dim_off = 0x10; + else if (vdd->temperature > -20) elvss_dim_off = 0x0C; + else elvss_dim_off = 0x0C; + break; + case 13: /*500, HBM*/ + if (vdd->temperature > 0) elvss_dim_off = 0x0F; + else if (vdd->temperature > -20) elvss_dim_off = 0x0C; + else elvss_dim_off = 0x0C; + break; + + default: + LCD_INFO("err: auto_brightness=%d\n", vdd->br.auto_level); + elvss_dim_off = 0x0F; + break; + } + + hbm_etc_cmds->cmds[4].msg.tx_buf[1] = elvss_dim_off; + + /* ACL */ + if (!vdd->gradual_acl_val) { /* gallery app */ + acl_opr = 0x4; /* 16 Frame Avg at ACL off */ + acl_start = 0x99; /* Start setting: 60% start */ + acl_percent = 0x10; /* ACL off */ + } + else { /* not gallery app */ + acl_opr = 0x5; /* 32 Frame Avg at ACL on */ + acl_start = 0x99; /* Start setting: 60% start */ + acl_percent = 0x11; /* ACL 8% on */ + } + + hbm_etc_cmds->cmds[6].msg.tx_buf[1] = acl_opr; + hbm_etc_cmds->cmds[8].msg.tx_buf[1] = acl_start; + hbm_etc_cmds->cmds[10].msg.tx_buf[1] = acl_percent; + + if (vdd->dtsi_data.samsung_elvss_compensation) { + elvss_comp = hbm_etc_cmds->cmds[2].msg.tx_buf[1] + hbm_etc_cmds->cmds[4].msg.tx_buf[1]; + + hbm_etc_cmds->cmds[2].msg.tx_buf[1] = 0x00; /* B2h 0x70th */ + hbm_etc_cmds->cmds[4].msg.tx_buf[1] = elvss_comp; /* B2h 0x67th */ + } + + LCD_INFO("bl:%d can:%d elv:%x temp:%d opr:%x start:%x acl:%x\n", + vdd->br.bl_level, vdd->br.cd_level, + elvss_dim_off, vdd->temperature, acl_opr, acl_start, + acl_percent); + + return hbm_etc_cmds; +} + +static struct dsi_panel_cmd_set *ss_hbm_off(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *hbm_off_cmds = ss_get_cmds(vdd, TX_HBM_OFF); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return NULL; + } + + if (vdd->dtsi_data.samsung_elvss_compensation) { + hbm_off_cmds->cmds[1].msg.tx_buf[1] = 0x00; + } + + *level_key = LEVEL_KEY_NONE; + + return ss_get_cmds(vdd, TX_HBM_OFF); +} + + +#define COORDINATE_DATA_SIZE 6 +#define MDNIE_SCR_WR_ADDR 50 + +#define F1(x,y) ((y)-((353*(x))/326)+30) +#define F2(x,y) ((y)-((20*(x))/19)-14) +#define F3(x,y) ((y)+((185*(x))/42)-16412) +#define F4(x,y) ((y)+((337*(x))/106)-12601) + +/* Normal Mode */ +static char coordinate_data_1[][COORDINATE_DATA_SIZE] = { + {0xff, 0x00, 0xff, 0x00, 0xff, 0x00}, /* dummy */ + {0xff, 0x00, 0xfb, 0x00, 0xfb, 0x00}, /* Tune_1 */ + {0xff, 0x00, 0xfc, 0x00, 0xff, 0x00}, /* Tune_2 */ + {0xfb, 0x00, 0xf9, 0x00, 0xff, 0x00}, /* Tune_3 */ + {0xff, 0x00, 0xfe, 0x00, 0xfc, 0x00}, /* Tune_4 */ + {0xff, 0x00, 0xff, 0x00, 0xff, 0x00}, /* Tune_5 */ + {0xfb, 0x00, 0xfc, 0x00, 0xff, 0x00}, /* Tune_6 */ + {0xfd, 0x00, 0xff, 0x00, 0xfa, 0x00}, /* Tune_7 */ + {0xfc, 0x00, 0xff, 0x00, 0xfc, 0x00}, /* Tune_8 */ + {0xfb, 0x00, 0xff, 0x00, 0xff, 0x00}, /* Tune_9 */ +}; + +/* sRGB/Adobe RGB Mode */ +static char coordinate_data_2[][COORDINATE_DATA_SIZE] = { + {0xff, 0x00, 0xf7, 0x00, 0xef, 0x00}, /* dummy */ + {0xff, 0x00, 0xf4, 0x00, 0xec, 0x00}, /* Tune_1 */ + {0xff, 0x00, 0xf5, 0x00, 0xef, 0x00}, /* Tune_2 */ + {0xff, 0x00, 0xf6, 0x00, 0xf2, 0x00}, /* Tune_3 */ + {0xff, 0x00, 0xf6, 0x00, 0xed, 0x00}, /* Tune_4 */ + {0xff, 0x00, 0xf7, 0x00, 0xef, 0x00}, /* Tune_5 */ + {0xff, 0x00, 0xf8, 0x00, 0xf2, 0x00}, /* Tune_6 */ + {0xff, 0x00, 0xf9, 0x00, 0xed, 0x00}, /* Tune_7 */ + {0xff, 0x00, 0xf9, 0x00, 0xef, 0x00}, /* Tune_8 */ + {0xff, 0x00, 0xfa, 0x00, 0xf2, 0x00}, /* Tune_9 */ +}; + +static char (*coordinate_data_multi[MAX_MODE])[COORDINATE_DATA_SIZE] = { + coordinate_data_2, /* DYNAMIC - Normal */ + coordinate_data_2, /* STANDARD - sRGB/Adobe RGB */ + coordinate_data_2, /* NATURAL - sRGB/Adobe RGB */ + coordinate_data_1, /* MOVIE - Normal */ + coordinate_data_1, /* AUTO - Normal */ + coordinate_data_1, /* READING - Normal */ +}; + +static int mdnie_coordinate_index(int x, int y) +{ + int tune_number = 0; + + if (F1(x,y) > 0) { + if (F3(x,y) > 0) { + tune_number = 3; + } else { + if (F4(x,y) < 0) + tune_number = 1; + else + tune_number = 2; + } + } else { + if (F2(x,y) < 0) { + if (F3(x,y) > 0) { + tune_number = 9; + } else { + if (F4(x,y) < 0) + tune_number = 7; + else + tune_number = 8; + } + } else { + if (F3(x,y) > 0) + tune_number = 6; + else { + if (F4(x,y) < 0) + tune_number = 4; + else + tune_number = 5; + } + } + } + + return tune_number; +} + +static int ss_mdnie_read(struct samsung_display_driver_data *vdd) +{ + char x_y_location[4]; + int mdnie_tune_index = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + /* Read mtp (D8h 123~126th) for ddi id */ + if (ss_get_cmds(vdd, RX_MDNIE)->count) { + ss_panel_data_read(vdd, RX_MDNIE, x_y_location, LEVEL_KEY_NONE); + + vdd->mdnie.mdnie_x = x_y_location[0] << 8 | x_y_location[1]; /* X */ + vdd->mdnie.mdnie_y = x_y_location[2] << 8 | x_y_location[3]; /* Y */ + + mdnie_tune_index = mdnie_coordinate_index(vdd->mdnie.mdnie_x, vdd->mdnie.mdnie_y); + + coordinate_tunning_multi(vdd, coordinate_data_multi, mdnie_tune_index, + MDNIE_SCR_WR_ADDR, COORDINATE_DATA_SIZE); // JUN_TEMP : From TAB_S4 : MDNIE_SCR_WR_ADDR + + LCD_INFO("DSI%d : X-%d Y-%d \n", vdd->ndx, + vdd->mdnie.mdnie_x, vdd->mdnie.mdnie_y); + } else { + LCD_ERR("DSI%d no mdnie_read_rx_cmds cmds", vdd->ndx); + return false; + } + + return true; +} + +static int ss_smart_dimming_init(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *pcmds; + struct dsi_panel_cmd_set *hbm_gamma_cmds = ss_get_cmds(vdd, TX_HBM_GAMMA); + struct dsi_panel_cmd_set *rx_smart_dim_mtp_cmds = ss_get_cmds(vdd, RX_SMART_DIM_MTP); + + LCD_INFO("++\n"); + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + vdd->smart_dimming_dsi = vdd->panel_func.samsung_smart_get_conf(); + if (IS_ERR_OR_NULL(vdd->smart_dimming_dsi)) { + LCD_ERR("DSI%d smart_dimming_dsi is null", vdd->ndx); + return false; + } + + ss_panel_data_read(vdd, RX_SMART_DIM_MTP, vdd->smart_dimming_dsi->mtp_buffer, LEVEL_KEY_NONE); + + rx_smart_dim_mtp_cmds->read_startoffset = 0x62; + ss_panel_data_read(vdd, RX_SMART_DIM_MTP, vdd->smart_dimming_dsi->mtp_buffer + 8, LEVEL_KEY_NONE); + + rx_smart_dim_mtp_cmds->read_startoffset = 0x6A; + ss_panel_data_read(vdd, RX_SMART_DIM_MTP, vdd->smart_dimming_dsi->mtp_buffer + 16, LEVEL_KEY_NONE); + + rx_smart_dim_mtp_cmds->read_startoffset = 0x72; + ss_panel_data_read(vdd, RX_SMART_DIM_MTP, vdd->smart_dimming_dsi->mtp_buffer + 24, LEVEL_KEY_NONE); + + rx_smart_dim_mtp_cmds->read_startoffset = 0x7A; + rx_smart_dim_mtp_cmds->cmds[0].msg.rx_len = 1; + rx_smart_dim_mtp_cmds->cmds[0].msg.tx_buf[1] = 1; + ss_panel_data_read(vdd, RX_SMART_DIM_MTP, vdd->smart_dimming_dsi->mtp_buffer + 32, LEVEL_KEY_NONE); + + /* Modifying hbm gamma tx command for Gamma Offset Index 4 */ + memcpy(&hbm_gamma_cmds->cmds[0].msg.tx_buf[1], hbm_buffer1, 33); + + /* Initialize smart dimming related things here */ + /* lux_tab setting for 350cd */ + vdd->smart_dimming_dsi->lux_tab = vdd->dtsi_data.candela_map_table[NORMAL][vdd->panel_revision].cd; + vdd->smart_dimming_dsi->lux_tabsize = vdd->dtsi_data.candela_map_table[NORMAL][vdd->panel_revision].tab_size; + vdd->smart_dimming_dsi->man_id = vdd->manufacture_id_dsi; + + /* copy hbm gamma payload for hbm interpolation calc */ + pcmds = ss_get_cmds(vdd, TX_HBM_GAMMA); + vdd->smart_dimming_dsi->hbm_payload = &pcmds->cmds[0].msg.tx_buf[1]; + + /* Just a safety check to ensure smart dimming data is initialised well */ + vdd->smart_dimming_dsi->init(vdd->smart_dimming_dsi); + + vdd->temperature = 20; // default temperature + + vdd->smart_dimming_loaded_dsi = true; + + LCD_INFO("DSI%d : --\n", vdd->ndx); + LCD_INFO("--\n"); + + return true; +} + +static struct dsi_panel_cmd_set aid_cmd; +static struct dsi_panel_cmd_set *ss_aid(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *pcmds = ss_get_cmds(vdd, TX_AID_SUBDIVISION); + int cd_index = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return NULL; + } + + cd_index = vdd->br.bl_level; + + aid_cmd.count = 1; + aid_cmd.cmds = &pcmds->cmds[cd_index]; + LCD_DEBUG("[%d] level(%d), aid(%x %x)\n", + cd_index, vdd->br.bl_level, + aid_cmd.cmds->msg.tx_buf[1], + aid_cmd.cmds->msg.tx_buf[2]); + *level_key = LEVEL_KEY_NONE; + + return &aid_cmd; +} + +static struct dsi_panel_cmd_set *ss_acl_on(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *pcmds; + + LCD_INFO("++\n"); + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return NULL; + } + + *level_key = LEVEL_KEY_NONE; + + if (vdd->gradual_acl_val) { + pcmds = ss_get_cmds(vdd, TX_ACL_ON); + pcmds->cmds[5].msg.tx_buf[1] = vdd->gradual_acl_val; + } + + LCD_INFO("--\n"); + return ss_get_cmds(vdd, TX_ACL_ON); + +} + +static struct dsi_panel_cmd_set *ss_acl_off(struct samsung_display_driver_data *vdd, int *level_key) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return NULL; + } + + *level_key = LEVEL_KEY_NONE; + + return ss_get_cmds(vdd, TX_ACL_OFF); +} + +static int ss_elvss_read(struct samsung_display_driver_data *vdd) +{ + char elvss_read_CAL_OFFSET[1] = {0,}; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + if (!vdd->dtsi_data.samsung_elvss_compensation) { + LCD_INFO("Not Supported \n"); + return false; + } + + /* Read CAL_OFFSET[0x70] & Save to elvss_value2 */ + ss_panel_data_read(vdd, RX_ELVSS, elvss_read_CAL_OFFSET, LEVEL_KEY_NONE); + vdd->br.elvss_value2 = elvss_read_CAL_OFFSET[0]; + + return true; +} + +static struct dsi_panel_cmd_set *ss_pre_elvss(struct samsung_display_driver_data *vdd, int *level_key) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return NULL; + } + + *level_key = LEVEL_KEY_NONE; + return ss_get_cmds(vdd, TX_ELVSS_PRE); +} + +static struct dsi_panel_cmd_set elvss_cmd; +static struct dsi_panel_cmd_set *ss_elvss(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *elvss_cmds; + int cd_index = 0; + int cmd_idx = 0; + int candela_value = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return NULL; + } + + cd_index = vdd->br.cd_idx; + candela_value = vdd->br.cd_level; + //LCD_INFO("cd_index (%d) candela_value(%d)\n", cd_index, candela_value); + + // JUN_TEMP : To do + if (vdd->acl_status || vdd->siop_status) { + if (!vdd->dtsi_data.smart_acl_elvss_map_table[vdd->panel_revision].size || + cd_index > vdd->dtsi_data.smart_acl_elvss_map_table[vdd->panel_revision].size) + goto end; + + cmd_idx = vdd->dtsi_data.smart_acl_elvss_map_table[vdd->panel_revision].cmd_idx[cd_index]; + //LCD_INFO("ACL ELVSS : cd_index[%d] cmd_idx[%d]\n", cd_index, cmd_idx); + + if (vdd->temperature > 0) + elvss_cmds = ss_get_cmds(vdd, TX_SMART_ACL_ELVSS); + else if (vdd->temperature > -20) + elvss_cmds = ss_get_cmds(vdd, TX_SMART_ACL_ELVSS_LOWTEMP); + else + elvss_cmds = ss_get_cmds(vdd, TX_SMART_ACL_ELVSS_LOWTEMP2); + + if (IS_ERR_OR_NULL(elvss_cmds)) { + LCD_ERR("Invalid data cmds : 0x%zx \n", (size_t)elvss_cmds); + return NULL; + } + } + else { + if (!vdd->dtsi_data.elvss_map_table[vdd->panel_revision].size || + cd_index > vdd->dtsi_data.elvss_map_table[vdd->panel_revision].size) + goto end; + + cmd_idx = vdd->dtsi_data.elvss_map_table[vdd->panel_revision].cmd_idx[cd_index]; + //LCD_INFO("ELVSS : cd_index[%d] cmd_idx[%d]\n", cd_index, cmd_idx); + + if (vdd->temperature > 0) + elvss_cmds = ss_get_cmds(vdd, TX_ELVSS); + else if (vdd->temperature > -20) + elvss_cmds = ss_get_cmds(vdd, TX_ELVSS_LOWTEMP); + else + elvss_cmds = ss_get_cmds(vdd, TX_ELVSS_LOWTEMP2); + + if (IS_ERR_OR_NULL(elvss_cmds)) { + LCD_ERR("Invalid data cmds : 0x%zx \n", (size_t)elvss_cmds); + return NULL; + } + } + + elvss_cmd.cmds = &(elvss_cmds->cmds[cmd_idx]); + elvss_cmd.count = 1; + + if (vdd->dtsi_data.samsung_elvss_compensation) { + /* Save elvss_value1 to send elvss2 cmd */ + vdd->br.elvss_value1 = elvss_cmd.cmds[0].msg.tx_buf[1]; // 0x67 Value Save + } + + *level_key = LEVEL_KEY_NONE; + + return &elvss_cmd; +end : + LCD_ERR("error"); + return NULL; +} + +static struct dsi_panel_cmd_set *ss_elvss_2(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *elvss_2_cmds = ss_get_cmds(vdd, TX_ELVSS_2); + int elvss_compensation = 0; + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(elvss_2_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)elvss_2_cmds); + return NULL; + } + + if (!vdd->dtsi_data.samsung_elvss_compensation) { + LCD_INFO("Not Supported !\n"); + return NULL; + } + + elvss_compensation = vdd->br.elvss_value1 + vdd->br.elvss_value2; + elvss_2_cmds->cmds[1].msg.tx_buf[1] = elvss_compensation; + LCD_INFO("elvss_compensation [0x%x]+[0x%x]=[0x%x]\n", vdd->br.elvss_value1, vdd->br.elvss_value2, elvss_compensation); + + *level_key = LEVEL_KEY_NONE; + return elvss_2_cmds; +} + +static struct dsi_panel_cmd_set *ss_gamma(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *gamma_cmds = ss_get_cmds(vdd, TX_GAMMA); + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(gamma_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)gamma_cmds); + return NULL; + } + + LCD_INFO("bl_level : %d candela : %dCD\n", vdd->br.bl_level, vdd->br.cd_level); + + if (IS_ERR_OR_NULL(vdd->smart_dimming_dsi->generate_gamma)) { + LCD_ERR("generate_gamma is NULL error"); + return NULL; + } else { + vdd->smart_dimming_dsi->generate_gamma( + vdd->smart_dimming_dsi, + vdd->br.cd_level, + &gamma_cmds->cmds[0].msg.tx_buf[1]); + + *level_key = LEVEL_KEY_NONE; + + return gamma_cmds; + } +} + +#if 0 +/* IRC */ +#define NUM_IRC_OTP 2 +static int ss_irc_read(struct samsung_display_driver_data *vdd) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + if (!vdd->display_status_dsi.irc_otp) { + vdd->display_status_dsi.irc_otp = kzalloc(NUM_IRC_OTP, GFP_KERNEL); + if (!vdd->display_status_dsi.irc_otp) { + LCD_ERR("fail to allocate irc_otp memory\n"); + return false; + } + } + + /* Read mtp (B5h 23th,24th) for elvss*/ + ss_panel_data_read(vdd, RX_IRC, vdd->display_status_dsi.irc_otp, + LEVEL1_KEY); + + + return true; +} + +static struct dsi_panel_cmd_set irc_cmd; +static struct dsi_panel_cmd_set *ss_irc(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *irc_cmds = ss_get_cmds(vdd, TX_IRC_SUBDIVISION); + int cd_index = 0; + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(irc_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", + (size_t)vdd, (size_t)irc_cmds); + return NULL; + } + + if (IS_ERR_OR_NULL(irc_cmds->cmds)) { + LCD_ERR("No irc_subdivision_tx_cmds\n"); + return NULL; + } + + if (!vdd->samsung_support_irc) + return NULL; + + /* IRC Subdivision works like as AID Subdivision */ + if (vdd->pac) + cd_index = vdd->pac_cd_idx; + else + cd_index = vdd->br.bl_level; + + LCD_DEBUG("irc idx (%d)\n", cd_index); + + irc_cmd.cmds = &(irc_cmds->cmds[cd_index]); + irc_cmd.count = 1; + *level_key = LEVEL1_KEY; + + /* read B8 1st,2nd from MTP and write to B8 1st,2nd */ + irc_cmd.cmds->msg.tx_buf[1] = vdd->display_status_dsi.irc_otp[0]; + irc_cmd.cmds->msg.tx_buf[2] = vdd->display_status_dsi.irc_otp[1]; + + return &irc_cmd; +} + +static struct dsi_panel_cmd_set hbm_irc_cmd; +static struct dsi_panel_cmd_set *ss_hbm_irc(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *hbm_irc_cmds = ss_get_cmds(vdd, TX_HBM_IRC); + int para_idx = 0; + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(hbm_irc_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)hbm_irc_cmds); + return NULL; + } + + if (IS_ERR_OR_NULL(hbm_irc_cmds->cmds)) { + LCD_ERR("No irc_tx_cmds\n"); + return NULL; + } + + if (!vdd->samsung_support_irc) + return NULL; + + *level_key = LEVEL1_KEY; + + para_idx = vdd->auto_brightness_level - vdd->auto_brightness; + + hbm_irc_cmd.cmds = &(hbm_irc_cmds->cmds[para_idx]); + hbm_irc_cmd.count = 1; + + /* read B8 1st,2nd from MTP and write to B8 1st,2nd */ + hbm_irc_cmd.cmds->msg.tx_buf[1] = vdd->display_status_dsi.irc_otp[0]; + hbm_irc_cmd.cmds->msg.tx_buf[2] = vdd->display_status_dsi.irc_otp[1]; + + return &hbm_irc_cmd; +} + +// ======================== +// HMT +// ======================== +static struct dsi_panel_cmd_set *ss_gamma_hmt(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *hmt_gamma_cmds = ss_get_cmds(vdd, TX_HMT_GAMMA); + + if (IS_ERR_OR_NULL(hmt_gamma_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)hmt_gamma_cmds); + return NULL; + } + + LCD_DEBUG("hmt_bl_level : %d candela : %dCD\n", vdd->hmt_stat.hmt_bl_level, vdd->hmt_stat.candela_level_hmt); + + if (IS_ERR_OR_NULL(vdd->smart_dimming_dsi_hmt->generate_gamma)) { + LCD_ERR("generate_gamma is NULL"); + return NULL; + } else { + vdd->smart_dimming_dsi_hmt->generate_gamma( + vdd->smart_dimming_dsi_hmt, + vdd->hmt_stat.candela_level_hmt, + &hmt_gamma_cmds->cmds[0].msg.tx_buf[1]); + + *level_key = LEVEL1_KEY; + + return hmt_gamma_cmds; + } +} + +static struct dsi_panel_cmd_set hmt_aid_cmd; +static struct dsi_panel_cmd_set *ss_aid_hmt( + struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *hmt_aid_cmds = ss_get_cmds(vdd, TX_HMT_AID); + int cmd_idx = 0; + + if (!vdd->dtsi_data.hmt_reverse_aid_map_table[vdd->panel_revision].size || + vdd->hmt_stat.cmd_idx_hmt > vdd->dtsi_data.hmt_reverse_aid_map_table[vdd->panel_revision].size) + goto end; + + cmd_idx = vdd->dtsi_data.hmt_reverse_aid_map_table[vdd->panel_revision].cmd_idx[vdd->hmt_stat.cmd_idx_hmt]; + + hmt_aid_cmd.cmds = &hmt_aid_cmds->cmds[cmd_idx]; + hmt_aid_cmd.count = 1; + + *level_key = LEVEL1_KEY; + + return &hmt_aid_cmd; + +end: + LCD_ERR("error"); + return NULL; +} + +static struct dsi_panel_cmd_set *ss_elvss_hmt(struct samsung_display_driver_data *vdd, int *level_key) +{ + struct dsi_panel_cmd_set *elvss_cmds = ss_get_cmds(vdd, TX_HMT_ELVSS); + + if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(elvss_cmds)) { + LCD_ERR("Invalid data vdd : 0x%zx cmds : 0x%zx", (size_t)vdd, (size_t)elvss_cmds); + return NULL; + } + + /* 0xB5 1th TSET */ + elvss_cmds->cmds[0].msg.tx_buf[1] = + vdd->temperature > 0 ? vdd->temperature : 0x80|(-1*vdd->temperature); + + /* ELVSS(MPS_CON) setting condition is equal to normal birghtness */ // B5 2nd para : MPS_CON + if (vdd->hmt_stat.candela_level_hmt > 40) + elvss_cmds->cmds[0].msg.tx_buf[2] = 0xDC; + else + elvss_cmds->cmds[0].msg.tx_buf[2] = 0xCC; + + *level_key = LEVEL1_KEY; + + return elvss_cmds; +} + +static void ss_make_sdimconf_hmt(struct samsung_display_driver_data *vdd) +{ + /* Set the mtp read buffer pointer and read the NVM value*/ + ss_panel_data_read(vdd, RX_SMART_DIM_MTP, + vdd->smart_dimming_dsi_hmt->mtp_buffer, LEVEL1_KEY); + + /* Initialize smart dimming related things here */ + /* lux_tab setting for 350cd */ + vdd->smart_dimming_dsi_hmt->lux_tab = vdd->dtsi_data.hmt_candela_map_table[vdd->panel_revision].cd; + vdd->smart_dimming_dsi_hmt->lux_tabsize = vdd->dtsi_data.hmt_candela_map_table[vdd->panel_revision].tab_size; + vdd->smart_dimming_dsi_hmt->man_id = vdd->manufacture_id_dsi; + if (vdd->panel_func.samsung_panel_revision) + vdd->smart_dimming_dsi_hmt->panel_revision = vdd->panel_func.samsung_panel_revision(vdd); + + /* Just a safety check to ensure smart dimming data is initialised well */ + vdd->smart_dimming_dsi_hmt->init(vdd->smart_dimming_dsi_hmt); + + LCD_INFO("[HMT] smart dimming done!\n"); +} + +static int ss_samart_dimming_init_hmt(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("DSI%d : ++\n", vdd->ndx); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx", (size_t)vdd); + return false; + } + + vdd->smart_dimming_dsi_hmt = vdd->panel_func.samsung_smart_get_conf_hmt(); + + if (IS_ERR_OR_NULL(vdd->smart_dimming_dsi_hmt)) { + LCD_ERR("DSI%d error", vdd->ndx); + return false; + } else { + vdd->hmt_stat.hmt_on = 0; + vdd->hmt_stat.hmt_bl_level = 0; + vdd->hmt_stat.hmt_reverse = 0; + vdd->hmt_stat.hmt_is_first = 1; + + ss_make_sdimconf_hmt(vdd); + + vdd->smart_dimming_hmt_loaded_dsi = true; + } + + LCD_INFO("DSI%d : --\n", vdd->ndx); + + return true; +} + +static void ss_set_panel_lpm_brightness(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *alpm_brightness[LPM_BRIGHTNESS_MAX_IDX] = {NULL, }; + struct dsi_panel_cmd_set *alpm_ctrl[MAX_LPM_CTRL] = {NULL, }; + struct dsi_panel_cmd_set *cmd_list[2]; + + /*default*/ + int mode = ALPM_MODE_ON; + int ctrl_index = ALPM_CTRL_2NIT; + int bl_index = LPM_2NIT_IDX; + + /* + * Init reg_list and cmd list + * reg_list[X][0] is reg value + * reg_list[X][1] is offset for reg value + * cmd_list is the target cmds for searching reg value + */ + static int reg_list[2][2] = { + {ALPM_REG, -EINVAL}, + {ALPM_CTRL_REG, -EINVAL} }; + + LCD_DEBUG("%s++\n", __func__); + + cmd_list[0] = ss_get_cmds(vdd, TX_LPM_BL_CMD); + cmd_list[1] = ss_get_cmds(vdd, TX_LPM_BL_CMD); + + /* Init alpm_brightness and alpm_ctrl cmds */ + alpm_brightness[LPM_2NIT_IDX] = ss_get_cmds(vdd, TX_LPM_2NIT_CMD); + alpm_brightness[LPM_10NIT_IDX] = ss_get_cmds(vdd, TX_LPM_10NIT_CMD); + alpm_brightness[LPM_30NIT_IDX] = ss_get_cmds(vdd, TX_LPM_30NIT_CMD); + alpm_brightness[LPM_60NIT_IDX] = ss_get_cmds(vdd, TX_LPM_60NIT_CMD); + + alpm_ctrl[ALPM_CTRL_2NIT] = ss_get_cmds(vdd, TX_ALPM_2NIT_CMD); + alpm_ctrl[ALPM_CTRL_10NIT] = ss_get_cmds(vdd, TX_ALPM_10NIT_CMD); + alpm_ctrl[ALPM_CTRL_30NIT] = ss_get_cmds(vdd, TX_ALPM_30NIT_CMD); + alpm_ctrl[ALPM_CTRL_60NIT] = ss_get_cmds(vdd, TX_ALPM_60NIT_CMD); + alpm_ctrl[HLPM_CTRL_2NIT] = ss_get_cmds(vdd, TX_HLPM_2NIT_CMD); + alpm_ctrl[HLPM_CTRL_10NIT] = ss_get_cmds(vdd, TX_HLPM_10NIT_CMD); + alpm_ctrl[HLPM_CTRL_30NIT] = ss_get_cmds(vdd, TX_HLPM_30NIT_CMD); + alpm_ctrl[HLPM_CTRL_60NIT] = ss_get_cmds(vdd, TX_HLPM_60NIT_CMD); + + mode = vdd->panel_lpm.mode; + + switch (vdd->panel_lpm.lpm_bl_level) { + case LPM_10NIT: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_10NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_10NIT : ALPM_CTRL_10NIT; + bl_index = LPM_10NIT_IDX; + break; + case LPM_30NIT: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_30NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_30NIT : ALPM_CTRL_30NIT; + bl_index = LPM_30NIT_IDX; + break; + case LPM_60NIT: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_60NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_60NIT : ALPM_CTRL_60NIT; + bl_index = LPM_60NIT_IDX; + break; + case LPM_2NIT: + default: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_2NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_2NIT : ALPM_CTRL_2NIT; + bl_index = LPM_2NIT_IDX; + break; + } + + LCD_DEBUG("[Panel LPM]bl_index %d, ctrl_index %d, mode %d\n", + bl_index, ctrl_index, mode); + + /* + * Find offset for alpm_reg and alpm_ctrl_reg + * alpm_reg : Control register for ALPM/HLPM on/off + * alpm_ctrl_reg : Control register for changing ALPM/HLPM mode + */ + if (unlikely((reg_list[0][1] == -EINVAL) || + (reg_list[1][1] == -EINVAL))) + ss_find_reg_offset(reg_list, cmd_list, ARRAY_SIZE(cmd_list)); + + if (reg_list[0][1] != -EINVAL) { + /* Update parameter for ALPM_REG */ + memcpy(cmd_list[0]->cmds[reg_list[0][1]].msg.tx_buf, + alpm_brightness[bl_index]->cmds[0].msg.tx_buf, + sizeof(char) * cmd_list[0]->cmds[reg_list[0][1]].msg.tx_len); + + LCD_DEBUG("[Panel LPM] change brightness cmd : %x, %x\n", + cmd_list[0]->cmds[reg_list[0][1]].msg.tx_buf[1], + alpm_brightness[bl_index]->cmds[0].msg.tx_buf[1]); + } + + if (reg_list[1][1] != -EINVAL) { + /* Initialize ALPM/HLPM cmds */ + /* Update parameter for ALPM_CTRL_REG */ + memcpy(cmd_list[1]->cmds[reg_list[1][1]].msg.tx_buf, + alpm_ctrl[ctrl_index]->cmds[0].msg.tx_buf, + sizeof(char) * cmd_list[1]->cmds[reg_list[1][1]].msg.tx_len); + + LCD_DEBUG("[Panel LPM] update alpm ctrl reg\n"); + } + + //send lpm bl cmd + ss_send_cmd(vdd, TX_LPM_BL_CMD); + + LCD_INFO("[Panel LPM] bl_level : %s\n", + /* Check current brightness level */ + vdd->panel_lpm.lpm_bl_level == LPM_2NIT ? "2NIT" : + vdd->panel_lpm.lpm_bl_level == LPM_10NIT ? "10NIT" : + vdd->panel_lpm.lpm_bl_level == LPM_30NIT ? "30NIT" : + vdd->panel_lpm.lpm_bl_level == LPM_60NIT ? "60NIT" : "UNKNOWN"); + + LCD_DEBUG("%s--\n", __func__); +} + +/* + * This function will update parameters for ALPM_REG/ALPM_CTRL_REG + * Parameter for ALPM_REG : Control brightness for panel LPM + * Parameter for ALPM_CTRL_REG : Change panel LPM mode for ALPM/HLPM + * mode, brightness, hz are updated here. + */ +static void ss_update_panel_lpm_ctrl_cmd(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *alpm_brightness[LPM_BRIGHTNESS_MAX_IDX] = {NULL, }; + struct dsi_panel_cmd_set *alpm_ctrl[MAX_LPM_CTRL] = {NULL, }; + struct dsi_panel_cmd_set *alpm_off_ctrl[MAX_LPM_MODE] = {NULL, }; + struct dsi_panel_cmd_set *cmd_list[2]; + struct dsi_panel_cmd_set *off_cmd_list[1]; + + /*default*/ + int mode = ALPM_MODE_ON; + int ctrl_index = ALPM_CTRL_2NIT; + int bl_index = LPM_2NIT_IDX; + + /* + * Init reg_list and cmd list + * reg_list[X][0] is reg value + * reg_list[X][1] is offset for reg value + * cmd_list is the target cmds for searching reg value + */ + static int reg_list[2][2] = { + {ALPM_REG, -EINVAL}, + {ALPM_CTRL_REG, -EINVAL} }; + + static int off_reg_list[1][2] = { + {ALPM_CTRL_REG, -EINVAL} }; + + LCD_ERR("%s++\n", __func__); + + cmd_list[0] = ss_get_cmds(vdd, TX_LPM_ON); + cmd_list[1] = ss_get_cmds(vdd, TX_LPM_ON); + off_cmd_list[0] = ss_get_cmds(vdd, TX_LPM_OFF); + + /* Init alpm_brightness and alpm_ctrl cmds */ + alpm_brightness[LPM_2NIT_IDX] = ss_get_cmds(vdd, TX_LPM_2NIT_CMD); + alpm_brightness[LPM_10NIT_IDX] = ss_get_cmds(vdd, TX_LPM_10NIT_CMD); + alpm_brightness[LPM_30NIT_IDX] = ss_get_cmds(vdd, TX_LPM_30NIT_CMD); + alpm_brightness[LPM_60NIT_IDX] = ss_get_cmds(vdd, TX_LPM_60NIT_CMD); + + alpm_ctrl[ALPM_CTRL_2NIT] = ss_get_cmds(vdd, TX_ALPM_2NIT_CMD); + alpm_ctrl[ALPM_CTRL_10NIT] = ss_get_cmds(vdd, TX_ALPM_10NIT_CMD); + alpm_ctrl[ALPM_CTRL_30NIT] = ss_get_cmds(vdd, TX_ALPM_30NIT_CMD); + alpm_ctrl[ALPM_CTRL_60NIT] = ss_get_cmds(vdd, TX_ALPM_60NIT_CMD); + alpm_ctrl[HLPM_CTRL_2NIT] = ss_get_cmds(vdd, TX_HLPM_2NIT_CMD); + alpm_ctrl[HLPM_CTRL_10NIT] = ss_get_cmds(vdd, TX_HLPM_10NIT_CMD); + alpm_ctrl[HLPM_CTRL_30NIT] = ss_get_cmds(vdd, TX_HLPM_30NIT_CMD); + alpm_ctrl[HLPM_CTRL_60NIT] = ss_get_cmds(vdd, TX_HLPM_60NIT_CMD); + alpm_off_ctrl[ALPM_MODE_ON] = ss_get_cmds(vdd, TX_ALPM_OFF); + alpm_off_ctrl[HLPM_MODE_ON] = ss_get_cmds(vdd, TX_HLPM_OFF); + + mode = vdd->panel_lpm.mode; + + switch (vdd->panel_lpm.lpm_bl_level) { + case LPM_10NIT: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_10NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_10NIT : ALPM_CTRL_10NIT; + bl_index = LPM_10NIT_IDX; + break; + case LPM_30NIT: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_30NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_30NIT : ALPM_CTRL_30NIT; + bl_index = LPM_30NIT_IDX; + break; + case LPM_60NIT: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_60NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_60NIT : ALPM_CTRL_60NIT; + bl_index = LPM_60NIT_IDX; + break; + case LPM_2NIT: + default: + ctrl_index = (mode == ALPM_MODE_ON) ? ALPM_CTRL_2NIT : + (mode == HLPM_MODE_ON) ? HLPM_CTRL_2NIT : ALPM_CTRL_2NIT; + bl_index = LPM_2NIT_IDX; + break; + } + + LCD_DEBUG("[Panel LPM] change brightness cmd :%d, %d, %d\n", + bl_index, ctrl_index, mode); + + /* + * Find offset for alpm_reg and alpm_ctrl_reg + * alpm_reg : Control register for ALPM/HLPM on/off + * alpm_ctrl_reg : Control register for changing ALPM/HLPM mode + */ + if (unlikely((reg_list[0][1] == -EINVAL) || + (reg_list[1][1] == -EINVAL))) + ss_find_reg_offset(reg_list, cmd_list, ARRAY_SIZE(cmd_list)); + + if (unlikely(off_reg_list[0][1] == -EINVAL)) + ss_find_reg_offset(off_reg_list, off_cmd_list, + ARRAY_SIZE(off_cmd_list)); + + if (reg_list[0][1] != -EINVAL) { + /* Update parameter for ALPM_REG */ + memcpy(cmd_list[0]->cmds[reg_list[0][1]].msg.tx_buf, + alpm_brightness[bl_index]->cmds[0].msg.tx_buf, + sizeof(char) * cmd_list[0]->cmds[reg_list[0][1]].msg.tx_len); + + LCD_DEBUG("[Panel LPM] change brightness cmd : %x, %x\n", + cmd_list[0]->cmds[reg_list[0][1]].msg.tx_buf[1], + alpm_brightness[bl_index]->cmds[0].msg.tx_buf[1]); + } + + if (reg_list[1][1] != -EINVAL) { + /* Initialize ALPM/HLPM cmds */ + /* Update parameter for ALPM_CTRL_REG */ + memcpy(cmd_list[1]->cmds[reg_list[1][1]].msg.tx_buf, + alpm_ctrl[ctrl_index]->cmds[0].msg.tx_buf, + sizeof(char) * cmd_list[1]->cmds[reg_list[1][1]].msg.tx_len); + + LCD_DEBUG("[Panel LPM] update alpm ctrl reg\n"); + } + + if ((off_reg_list[0][1] != -EINVAL) &&\ + (mode != LPM_MODE_OFF)) { + /* Update parameter for ALPM_CTRL_REG */ + memcpy(off_cmd_list[0]->cmds[off_reg_list[0][1]].msg.tx_buf, + alpm_off_ctrl[mode]->cmds[0].msg.tx_buf, + sizeof(char) * off_cmd_list[0]->cmds[off_reg_list[0][1]].msg.tx_len); + } + + LCD_ERR("%s--\n", __func__); +} +#endif + +static int dsi_update_mdnie_data(struct samsung_display_driver_data *vdd) +{ + struct mdnie_lite_tune_data *mdnie_data; + + LCD_INFO("++\n"); + + mdnie_data = kzalloc(sizeof(struct mdnie_lite_tune_data), GFP_KERNEL); + if (!mdnie_data) { + LCD_ERR("fail to allocate mdnie_data memory\n"); + return -ENOMEM; + } + + /* Update mdnie command */ + mdnie_data->DSI_COLOR_BLIND_MDNIE_1 = DSI0_COLOR_BLIND_MDNIE_1; + mdnie_data->DSI_COLOR_BLIND_MDNIE_2 = DSI0_COLOR_BLIND_MDNIE_2; + mdnie_data->DSI_RGB_SENSOR_MDNIE_1 = DSI0_RGB_SENSOR_MDNIE_1; + mdnie_data->DSI_RGB_SENSOR_MDNIE_2 = DSI0_RGB_SENSOR_MDNIE_2; + mdnie_data->DSI_UI_DYNAMIC_MDNIE_2 = DSI0_UI_DYNAMIC_MDNIE_2; + mdnie_data->DSI_UI_STANDARD_MDNIE_2 = DSI0_UI_STANDARD_MDNIE_2; + mdnie_data->DSI_UI_AUTO_MDNIE_2 = DSI0_UI_AUTO_MDNIE_2; + mdnie_data->DSI_VIDEO_DYNAMIC_MDNIE_2 = DSI0_VIDEO_DYNAMIC_MDNIE_2; + mdnie_data->DSI_VIDEO_STANDARD_MDNIE_2 = DSI0_VIDEO_STANDARD_MDNIE_2; + mdnie_data->DSI_VIDEO_AUTO_MDNIE_2 = DSI0_VIDEO_AUTO_MDNIE_2; + mdnie_data->DSI_CAMERA_MDNIE_2 = DSI0_CAMERA_AUTO_MDNIE_2; + mdnie_data->DSI_CAMERA_AUTO_MDNIE_2 = DSI0_CAMERA_AUTO_MDNIE_2; + mdnie_data->DSI_GALLERY_DYNAMIC_MDNIE_2 = DSI0_GALLERY_DYNAMIC_MDNIE_2; + mdnie_data->DSI_GALLERY_STANDARD_MDNIE_2 = DSI0_GALLERY_STANDARD_MDNIE_2; + mdnie_data->DSI_GALLERY_AUTO_MDNIE_2 = DSI0_GALLERY_AUTO_MDNIE_2; + mdnie_data->DSI_VT_DYNAMIC_MDNIE_2 = DSI0_VT_DYNAMIC_MDNIE_2; + mdnie_data->DSI_VT_STANDARD_MDNIE_2 = DSI0_VT_STANDARD_MDNIE_2; + mdnie_data->DSI_VT_AUTO_MDNIE_2 = DSI0_VT_AUTO_MDNIE_2; + mdnie_data->DSI_BROWSER_DYNAMIC_MDNIE_2 = DSI0_BROWSER_DYNAMIC_MDNIE_2; + mdnie_data->DSI_BROWSER_STANDARD_MDNIE_2 = DSI0_BROWSER_STANDARD_MDNIE_2; + mdnie_data->DSI_BROWSER_AUTO_MDNIE_2 = DSI0_BROWSER_AUTO_MDNIE_2; + mdnie_data->DSI_EBOOK_DYNAMIC_MDNIE_2 = DSI0_EBOOK_DYNAMIC_MDNIE_2; + mdnie_data->DSI_EBOOK_STANDARD_MDNIE_2 = DSI0_EBOOK_STANDARD_MDNIE_2; + mdnie_data->DSI_EBOOK_AUTO_MDNIE_2 = DSI0_EBOOK_AUTO_MDNIE_2; + mdnie_data->DSI_TDMB_DYNAMIC_MDNIE_2 = DSI0_TDMB_DYNAMIC_MDNIE_2; + mdnie_data->DSI_TDMB_STANDARD_MDNIE_2 = DSI0_TDMB_STANDARD_MDNIE_2; + mdnie_data->DSI_TDMB_AUTO_MDNIE_2 = DSI0_TDMB_AUTO_MDNIE_2; + + mdnie_data->DSI_BYPASS_MDNIE = DSI0_BYPASS_MDNIE; + mdnie_data->DSI_NEGATIVE_MDNIE = DSI0_NEGATIVE_MDNIE; + mdnie_data->DSI_COLOR_BLIND_MDNIE = DSI0_COLOR_BLIND_MDNIE; + mdnie_data->DSI_HBM_CE_MDNIE = DSI0_HBM_CE_MDNIE; + mdnie_data->DSI_HBM_CE_TEXT_MDNIE = DSI0_HBM_CE_TEXT_MDNIE; + mdnie_data->DSI_RGB_SENSOR_MDNIE = DSI0_RGB_SENSOR_MDNIE; + mdnie_data->DSI_CURTAIN = DSI0_CURTAIN; + mdnie_data->DSI_GRAYSCALE_MDNIE = DSI0_GRAYSCALE_MDNIE; + mdnie_data->DSI_GRAYSCALE_NEGATIVE_MDNIE = DSI0_GRAYSCALE_NEGATIVE_MDNIE; + mdnie_data->DSI_UI_DYNAMIC_MDNIE = DSI0_UI_DYNAMIC_MDNIE; + mdnie_data->DSI_UI_STANDARD_MDNIE = DSI0_UI_STANDARD_MDNIE; + mdnie_data->DSI_UI_NATURAL_MDNIE = DSI0_UI_NATURAL_MDNIE; + mdnie_data->DSI_UI_MOVIE_MDNIE = DSI0_UI_MOVIE_MDNIE; + mdnie_data->DSI_UI_AUTO_MDNIE = DSI0_UI_AUTO_MDNIE; + mdnie_data->DSI_VIDEO_OUTDOOR_MDNIE = DSI0_VIDEO_OUTDOOR_MDNIE; + mdnie_data->DSI_VIDEO_DYNAMIC_MDNIE = DSI0_VIDEO_DYNAMIC_MDNIE; + mdnie_data->DSI_VIDEO_STANDARD_MDNIE = DSI0_VIDEO_STANDARD_MDNIE; + mdnie_data->DSI_VIDEO_NATURAL_MDNIE = DSI0_VIDEO_NATURAL_MDNIE; + mdnie_data->DSI_VIDEO_MOVIE_MDNIE = DSI0_VIDEO_MOVIE_MDNIE; + mdnie_data->DSI_VIDEO_AUTO_MDNIE = DSI0_VIDEO_AUTO_MDNIE; + mdnie_data->DSI_VIDEO_WARM_OUTDOOR_MDNIE = DSI0_VIDEO_OUTDOOR_MDNIE; + mdnie_data->DSI_VIDEO_WARM_MDNIE = DSI0_VIDEO_OUTDOOR_MDNIE; + mdnie_data->DSI_VIDEO_COLD_OUTDOOR_MDNIE = DSI0_VIDEO_OUTDOOR_MDNIE; + mdnie_data->DSI_VIDEO_COLD_MDNIE = DSI0_VIDEO_OUTDOOR_MDNIE; + mdnie_data->DSI_CAMERA_OUTDOOR_MDNIE = DSI0_CAMERA_OUTDOOR_MDNIE; + mdnie_data->DSI_CAMERA_MDNIE = DSI0_CAMERA_AUTO_MDNIE; + mdnie_data->DSI_CAMERA_AUTO_MDNIE = DSI0_CAMERA_AUTO_MDNIE; + mdnie_data->DSI_GALLERY_DYNAMIC_MDNIE = DSI0_GALLERY_DYNAMIC_MDNIE; + mdnie_data->DSI_GALLERY_STANDARD_MDNIE = DSI0_GALLERY_STANDARD_MDNIE; + mdnie_data->DSI_GALLERY_NATURAL_MDNIE = DSI0_GALLERY_NATURAL_MDNIE; + mdnie_data->DSI_GALLERY_MOVIE_MDNIE = DSI0_GALLERY_MOVIE_MDNIE; + mdnie_data->DSI_GALLERY_AUTO_MDNIE = DSI0_GALLERY_AUTO_MDNIE; + mdnie_data->DSI_VT_DYNAMIC_MDNIE = DSI0_VT_DYNAMIC_MDNIE; + mdnie_data->DSI_VT_STANDARD_MDNIE = DSI0_VT_STANDARD_MDNIE; + mdnie_data->DSI_VT_NATURAL_MDNIE = DSI0_VT_NATURAL_MDNIE; + mdnie_data->DSI_VT_MOVIE_MDNIE = DSI0_VT_MOVIE_MDNIE; + mdnie_data->DSI_VT_AUTO_MDNIE = DSI0_VT_AUTO_MDNIE; + mdnie_data->DSI_BROWSER_DYNAMIC_MDNIE = DSI0_BROWSER_DYNAMIC_MDNIE; + mdnie_data->DSI_BROWSER_STANDARD_MDNIE = DSI0_BROWSER_STANDARD_MDNIE; + mdnie_data->DSI_BROWSER_NATURAL_MDNIE = DSI0_BROWSER_NATURAL_MDNIE; + mdnie_data->DSI_BROWSER_MOVIE_MDNIE = DSI0_BROWSER_MOVIE_MDNIE; + mdnie_data->DSI_BROWSER_AUTO_MDNIE = DSI0_BROWSER_AUTO_MDNIE; + mdnie_data->DSI_EBOOK_DYNAMIC_MDNIE = DSI0_EBOOK_DYNAMIC_MDNIE; + mdnie_data->DSI_EBOOK_STANDARD_MDNIE = DSI0_EBOOK_STANDARD_MDNIE; + mdnie_data->DSI_EBOOK_NATURAL_MDNIE = DSI0_EBOOK_NATURAL_MDNIE; + mdnie_data->DSI_EBOOK_MOVIE_MDNIE = DSI0_EBOOK_MOVIE_MDNIE; + mdnie_data->DSI_EBOOK_AUTO_MDNIE = DSI0_EBOOK_AUTO_MDNIE; + mdnie_data->DSI_EMAIL_AUTO_MDNIE = DSI0_EMAIL_AUTO_MDNIE; + mdnie_data->DSI_TDMB_DYNAMIC_MDNIE = DSI0_TDMB_DYNAMIC_MDNIE; + mdnie_data->DSI_TDMB_STANDARD_MDNIE = DSI0_TDMB_STANDARD_MDNIE; + mdnie_data->DSI_TDMB_NATURAL_MDNIE = DSI0_TDMB_NATURAL_MDNIE; + mdnie_data->DSI_TDMB_MOVIE_MDNIE = DSI0_TDMB_NATURAL_MDNIE; + mdnie_data->DSI_TDMB_AUTO_MDNIE = DSI0_TDMB_AUTO_MDNIE; + mdnie_data->DSI_NIGHT_MODE_MDNIE = DSI0_NIGHT_MODE_MDNIE; + mdnie_data->DSI_NIGHT_MODE_MDNIE_SCR = DSI0_NIGHT_MODE_MDNIE_1; + mdnie_data->DSI_COLOR_LENS_MDNIE = DSI0_COLOR_LENS_MDNIE; + mdnie_data->DSI_COLOR_LENS_MDNIE_SCR = DSI0_COLOR_LENS_MDNIE_1; + mdnie_data->DSI_COLOR_BLIND_MDNIE_SCR = DSI0_COLOR_BLIND_MDNIE_1; + mdnie_data->DSI_RGB_SENSOR_MDNIE_SCR = DSI0_RGB_SENSOR_MDNIE_1; + + mdnie_data->mdnie_tune_value_dsi = mdnie_tune_value_dsi0; + //mdnie_data.mdnie_tune_value_dsi1 = mdnie_tune_value_dsi0; + + mdnie_data->light_notification_tune_value_dsi = light_notification_tune_value_dsi0; + //mdnie_data.light_notification_tune_value_dsi1 = light_notification_tune_value_dsi0; + + /* Update MDNIE data related with size, offset or index */ + mdnie_data->dsi_bypass_mdnie_size = ARRAY_SIZE(DSI0_BYPASS_MDNIE); + mdnie_data->mdnie_color_blinde_cmd_offset = MDNIE_COLOR_BLINDE_CMD_OFFSET; + mdnie_data->mdnie_step_index[MDNIE_STEP1] = MDNIE_STEP1_INDEX; + mdnie_data->mdnie_step_index[MDNIE_STEP2] = MDNIE_STEP2_INDEX; + mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET] = ADDRESS_SCR_WHITE_RED; + mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET] = ADDRESS_SCR_WHITE_GREEN; + mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET] = ADDRESS_SCR_WHITE_BLUE; + mdnie_data->dsi_rgb_sensor_mdnie_1_size = DSI0_RGB_SENSOR_MDNIE_1_SIZE; + mdnie_data->dsi_rgb_sensor_mdnie_2_size = DSI0_RGB_SENSOR_MDNIE_2_SIZE; + mdnie_data->hdr_tune_value_dsi = hdr_tune_value_dsi0; + //mdnie_data->hdr_tune_value_dsi1 = hdr_tune_value_dsi0; + mdnie_data->dsi_adjust_ldu_table = adjust_ldu_data; + //mdnie_data.dsi1_adjust_ldu_table = adjust_ldu_data; + mdnie_data->dsi_max_adjust_ldu = 6; + //mdnie_data.dsi1_max_adjust_ldu = 6; + mdnie_data->dsi_night_mode_table = night_mode_data; + //mdnie_data.dsi1_night_mode_table = night_mode_data; + mdnie_data->dsi_max_night_mode_index = 11; + //mdnie_data.dsi1_max_night_mode_index = 11; + mdnie_data->dsi_color_lens_table = color_lens_data; + //mdnie_data.dsi1_color_lens_table = color_lens_data; + mdnie_data->dsi_scr_step_index = MDNIE_STEP1_INDEX; + //mdnie_data.dsi1_scr_step_index = MDNIE_STEP1_INDEX; + mdnie_data->dsi_white_default_r = 0xff; + mdnie_data->dsi_white_default_g = 0xff; + mdnie_data->dsi_white_default_b = 0xff; + //mdnie_data->dsi1_white_default_r = 0xff; + //mdnie_data->dsi1_white_default_g = 0xff; + //mdnie_data->dsi1_white_default_b = 0xff; + + vdd->mdnie.mdnie_data = mdnie_data; + + LCD_INFO("--\n"); + return 0; +} + +static void samsung_panel_init(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("++\n"); + LCD_ERR("%s\n", ss_get_panel_name(vdd)); + + /* Default Panel Power Status is OFF */ + vdd->panel_state = PANEL_PWR_OFF; + + /* ON/OFF */ + vdd->panel_func.samsung_panel_on_pre = samsung_panel_on_pre; + vdd->panel_func.samsung_panel_on_post = samsung_panel_on_post; + + /* DDI RX */ + vdd->panel_func.samsung_panel_revision = ss_panel_revision; + vdd->panel_func.samsung_manufacture_date_read = ss_manufacture_date_read; + vdd->panel_func.samsung_ddi_id_read = ss_ddi_id_read; + vdd->panel_func.samsung_cell_id_read = ss_cell_id_read; + vdd->panel_func.samsung_octa_id_read = NULL; + vdd->panel_func.samsung_elvss_read = ss_elvss_read; + vdd->panel_func.samsung_hbm_read = ss_hbm_read; + vdd->panel_func.samsung_mdnie_read = ss_mdnie_read; + + vdd->panel_func.samsung_smart_dimming_init = ss_smart_dimming_init; + + vdd->panel_func.samsung_smart_get_conf = smart_get_conf_ANA38401_AMSA05RB01; + + /* Brightness */ + vdd->panel_func.samsung_brightness_hbm_off = ss_hbm_off; + vdd->panel_func.samsung_brightness_aid = ss_aid; + vdd->panel_func.samsung_brightness_acl_on = ss_acl_on; + vdd->panel_func.samsung_brightness_pre_acl_percent = NULL; + vdd->panel_func.samsung_brightness_acl_percent = NULL; + vdd->panel_func.samsung_brightness_acl_off = ss_acl_off; + vdd->panel_func.samsung_brightness_pre_elvss = ss_pre_elvss; + vdd->panel_func.samsung_brightness_elvss = ss_elvss; + vdd->panel_func.samsung_brightness_elvss_2 = ss_elvss_2; + vdd->panel_func.samsung_brightness_elvss_temperature1 = NULL; + vdd->panel_func.samsung_brightness_elvss_temperature2 = NULL; + vdd->panel_func.samsung_brightness_vint = NULL; + vdd->panel_func.samsung_brightness_gamma = ss_gamma; + + /* HBM */ + vdd->panel_func.samsung_hbm_gamma = ss_hbm_gamma; + vdd->panel_func.samsung_hbm_etc = ss_hbm_etc; + vdd->panel_func.samsung_hbm_irc = NULL; + vdd->panel_func.get_hbm_candela_value = NULL; + + /* Panel LPM */ + //vdd->panel_func.samsung_get_panel_lpm_mode = NULL; + + /* default brightness */ + vdd->br.bl_level = 255; + + /* mdnie */ + vdd->mdnie.support_mdnie = true; + vdd->mdnie.mdnie_tune_size[1]= 2; + vdd->mdnie.mdnie_tune_size[2]= 56; + vdd->mdnie.mdnie_tune_size[3]= 2; + vdd->mdnie.mdnie_tune_size[4]= 103; + + dsi_update_mdnie_data(vdd); + + /* send recovery pck before sending image date (for ESD recovery) */ + vdd->esd_recovery.send_esd_recovery = false; + vdd->br.auto_level = 12; + + /* Enable panic on first pingpong timeout */ + if (vdd->debug_data) + vdd->debug_data->panic_on_pptimeout = true; + + /* Set IRC init value */ + vdd->br.irc_mode = IRC_MODERATO_MODE; + + /* COLOR WEAKNESS */ + vdd->panel_func.color_weakness_ccb_on_off = NULL; + + /* Support DDI HW CURSOR */ + vdd->panel_func.ddi_hw_cursor = NULL; + + /* ACL default ON */ + vdd->acl_status = 1; + + LCD_INFO("--\n"); +} + +static int __init samsung_panel_initialize(void) +{ + struct samsung_display_driver_data *vdd; + enum ss_display_ndx ndx = PRIMARY_DISPLAY_NDX; + char panel_string[] = "ss_dsi_panel_ANA38401_AMSA05RB01_WQXGA"; + char panel_name[MAX_CMDLINE_PARAM_LEN]; + char panel_secondary_name[MAX_CMDLINE_PARAM_LEN]; + + LCD_INFO("++\n"); + + ss_get_primary_panel_name_cmdline(panel_name); + ss_get_secondary_panel_name_cmdline(panel_secondary_name); + + /* TODO: use component_bind with panel_func + * and match by panel_string, instead. + */ + if (!strncmp(panel_string, panel_name, strlen(panel_string))) + ndx = PRIMARY_DISPLAY_NDX; + else if (!strncmp(panel_string, panel_secondary_name, + strlen(panel_string))) + ndx = SECONDARY_DISPLAY_NDX; + else{ + LCD_ERR("string (%s) can not find given panel_name (%s, %s)\n", panel_string, panel_name, panel_secondary_name); + return 0; + } + vdd = ss_get_vdd(ndx); + vdd->panel_func.samsung_panel_init = samsung_panel_init; + + LCD_INFO("%s done.. \n", panel_string); + LCD_INFO("--\n"); + + return 0; +} + +early_initcall(samsung_panel_initialize); diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_panel_ANA38401_AMSA05RB01.h b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_panel_ANA38401_AMSA05RB01.h new file mode 100755 index 000000000000..bc97358f4320 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_panel_ANA38401_AMSA05RB01.h @@ -0,0 +1,50 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +Copyright (C) 2017, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * +*/ +#ifndef SAMSUNG_DSI_PANEL_ANA38401_AMSA05RB01_H +#define SAMSUNG_DSI_PANEL_ANA38401_AMSA05RB01_H + +#include +#include "ss_dsi_panel_common.h" + +#define HBM_INTERPOLATION_STEP 8 +#define SUPPORT_LOWTEMP_ELVSS 15 + +enum { + MID_TEMP = 0, + LOW_TEMP, + HIGH_TEMP, + MAX_TEMP +}; + +struct smartdim_conf *smart_get_conf_ANA38401_AMSA05RB01(void); +struct smartdim_conf *smart_get_conf_ANA38401_AMSA05RB01_hmt(void); +#endif diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_smart_dimming_ANA38401_AMSA05RB01.c b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_smart_dimming_ANA38401_AMSA05RB01.c new file mode 100755 index 000000000000..f06fa3cddcb5 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_smart_dimming_ANA38401_AMSA05RB01.c @@ -0,0 +1,2280 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * +*/ +#include "ss_dsi_smart_dimming_ANA38401_AMSA05RB01.h" +/*#define SMART_DIMMING_DEBUG*/ + +static char max_lux_table[GAMMA_SET_MAX]; + +static char max_lux_table2[GAMMA_SET_MAX] = { + 0x80, 0x80, /* V11[7:0], V23[7:0] Red */ + 0x80, 0x80, /* V35[7:0], V51[7:0] Red */ + 0x80, 0x80, /* V87[7:0], V151[7:0] Red */ + 0x80, 0x00, /* V203[7:0], V255[7:0] Red */ + 0x80, 0x80, /* V3[7:0], V255[8]:V0[6:0]Red */ + 0x00, /* VT[3:0] Red */ + 0x80, 0x80, /* V11[7:0], V23[7:0] Green */ + 0x80, 0x80, /* V35[7:0], V51[7:0] Green */ + 0x80, 0x80, /* V87[7:0], V151[7:0] Green */ + 0x80, 0x00, /* V203[7:0], V255[7:0] Green */ + 0x80, 0x80, /* V3[7:0], V255[8]:V0[6:0]Green */ + 0x00, /* VT[3:0] Green */ + 0x80, 0x80, /* V11[7:0], V23[7:0] Blue */ + 0x80, 0x80, /* V35[7:0], V51[7:0] Blue */ + 0x80, 0x80, /* V87[7:0], V151[7:0] Blue */ + 0x80, 0x00, /* V203[7:0], V255[7:0] Blue */ + 0x80, 0x80, /* V3[7:0], V255[8]:V0[6:0]Blue */ + 0x00 /* VT[3:0] Blue */ +}; + +/* +* To support different center cell gamma setting +*/ +static char V255_300CD_R_MSB; +static char V255_300CD_R_LSB; + +static char V255_300CD_G_MSB; +static char V255_300CD_G_LSB; + +static char V255_300CD_B_MSB; +static char V255_300CD_B_LSB; + +static char V203_300CD_R; +static char V203_300CD_G; +static char V203_300CD_B; + +static char V151_300CD_R; +static char V151_300CD_G; +static char V151_300CD_B; + +static char V87_300CD_R; +static char V87_300CD_G; +static char V87_300CD_B; + +static char V51_300CD_R; +static char V51_300CD_G; +static char V51_300CD_B; + +static char V35_300CD_R; +static char V35_300CD_G; +static char V35_300CD_B; + +static char V23_300CD_R; +static char V23_300CD_G; +static char V23_300CD_B; + +static char V11_300CD_R; +static char V11_300CD_G; +static char V11_300CD_B; + +static char V3_300CD_R; +static char V3_300CD_G; +static char V3_300CD_B; + +static char VT_300CD_R; +static char VT_300CD_G; +static char VT_300CD_B; + +static int char_to_int(char data1) +{ + int cal_data; + + if (data1 & 0x80) { + cal_data = data1 & 0x7F; + cal_data *= -1; + } else + cal_data = data1; + + return cal_data; +} + +static int char_to_int_v255(char data1, char data2) +{ + int cal_data; + + if (data1) + cal_data = data2 * -1; + else + cal_data = data2; + + return cal_data; +} + +static void print_RGB_offset(struct SMART_DIM *pSmart) +{ + LCD_INFO("MTP Offset VT R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_1), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_1), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_1)); + LCD_INFO("MTP Offset V3 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_3), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_3), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_3)); + LCD_INFO("MTP Offset V11 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_11), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_11), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_11)); + LCD_INFO("MTP Offset V23 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_23), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_23), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_23)); + LCD_INFO("MTP Offset V35 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_35), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_35), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_35)); + LCD_INFO("MTP Offset V51 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_51), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_51), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_51)); + LCD_INFO("MTP Offset V87 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_87), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_87), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_87)); + LCD_INFO("MTP Offset V151 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_151), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_151), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_151)); + LCD_INFO("MTP Offset V203 R:%d G:%d B:%d\n", + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_203), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_203), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_203)); + LCD_INFO("MTP Offset V255 R:%d G:%d B:%d\n", + char_to_int_v255(pSmart->MTP.R_OFFSET.OFFSET_255_MSB, + pSmart->MTP.R_OFFSET.OFFSET_255_LSB), + char_to_int_v255(pSmart->MTP.G_OFFSET.OFFSET_255_MSB, + pSmart->MTP.G_OFFSET.OFFSET_255_LSB), + char_to_int_v255(pSmart->MTP.B_OFFSET.OFFSET_255_MSB, + pSmart->MTP.B_OFFSET.OFFSET_255_LSB)); +} + +static void print_lux_table(struct SMART_DIM *psmart, char* type) +{ + int lux_loop; + int cnt; + char pBuffer[256]; + memset(pBuffer, 0x00, 256); + + for (lux_loop = 0; lux_loop < psmart->lux_table_max; lux_loop++) { + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) { + if (!strcmp(type,"DEC")) + snprintf(pBuffer + strnlen(pBuffer, 256), 256, " %03d", + psmart->gen_table[lux_loop].gamma_setting[cnt]); + else + snprintf(pBuffer + strnlen(pBuffer, 256), 256, " %02x", + psmart->gen_table[lux_loop].gamma_setting[cnt]); + } + LCD_INFO("lux : %3d %s\n", psmart->plux_table[lux_loop], pBuffer); + memset(pBuffer, 0x00, 256); + } +} + +static void print_hbm_lux_table(struct SMART_DIM *psmart, char* type) +{ + int i,j; + char pBuffer[256]; + + memset(pBuffer, 0x00, 256); + for (i = 0; i < HBM_INTERPOLATION_STEP; i++) { + for (j = 0; j < GAMMA_SET_MAX; j++) { + if (!strcmp(type,"DEC")) + snprintf(pBuffer + strnlen(pBuffer, 256), 256, " %3d", + psmart->hbm_interpolation_table[i].gamma_setting[j]); + else + snprintf(pBuffer + strnlen(pBuffer, 256), 256, " %02x", + psmart->hbm_interpolation_table[i].gamma_setting[j]); + } + LCD_INFO("hbm[%3d] %s\n", hbm_interpolation_candela_table[i], pBuffer); + memset(pBuffer, 0x00, 256); + } +} + +static void print_aid_log(struct smartdim_conf *conf) +{ + print_RGB_offset(conf->psmart); + + print_lux_table(conf->psmart, "DEC"); + print_lux_table(conf->psmart, "HEX"); + + print_hbm_lux_table(conf->psmart, "DEC"); + print_hbm_lux_table(conf->psmart, "HEX"); +} + +#define v255_coefficient 129 +#define v255_denominator 640 +static int v255_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + int v255_value; + + v255_value = (V255_300CD_R_MSB << 8) | (V255_300CD_R_LSB); + LSB = char_to_int_v255(pSmart->MTP.R_OFFSET.OFFSET_255_MSB, + pSmart->MTP.R_OFFSET.OFFSET_255_LSB); + add_mtp = LSB + v255_value; + result_1 = result_2 = (v255_coefficient+add_mtp) << BIT_SHIFT; + do_div(result_2, v255_denominator); + result_3 = ((ANA34801_VREG0_REF_L-ANA34801_VREG0_REF_H) * result_2) >> BIT_SHIFT; + result_4 = ANA34801_VREG0_REF_L - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_255 = result_4; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_0 = ANA34801_VREG0_REF_L; + + v255_value = (V255_300CD_G_MSB << 8) | (V255_300CD_G_LSB); + LSB = char_to_int_v255(pSmart->MTP.G_OFFSET.OFFSET_255_MSB, + pSmart->MTP.G_OFFSET.OFFSET_255_LSB); + add_mtp = LSB + v255_value; + result_1 = result_2 = (v255_coefficient+add_mtp) << BIT_SHIFT; + do_div(result_2, v255_denominator); + result_3 = ((ANA34801_VREG0_REF_L-ANA34801_VREG0_REF_H) * result_2) >> BIT_SHIFT; + result_4 = ANA34801_VREG0_REF_L - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_255 = result_4; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_0 = ANA34801_VREG0_REF_L; + + v255_value = (V255_300CD_B_MSB << 8) | (V255_300CD_B_LSB); + LSB = char_to_int_v255(pSmart->MTP.B_OFFSET.OFFSET_255_MSB, + pSmart->MTP.B_OFFSET.OFFSET_255_LSB); + add_mtp = LSB + v255_value; + result_1 = result_2 = (v255_coefficient+add_mtp) << BIT_SHIFT; + do_div(result_2, v255_denominator); + result_3 = ((ANA34801_VREG0_REF_L-ANA34801_VREG0_REF_H) * result_2) >> BIT_SHIFT; + result_4 = ANA34801_VREG0_REF_L - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_255 = result_4; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_0 = ANA34801_VREG0_REF_L; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s MTP Offset VT R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_1), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_1), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_1)); + pr_info("%s MTP Offset V3 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_3), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_3), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_3)); + pr_info("%s MTP Offset V11 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_11), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_11), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_11)); + pr_info("%s MTP Offset V23 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_23), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_23), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_23)); + pr_info("%s MTP Offset V35 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_35), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_35), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_35)); + pr_info("%s MTP Offset V51 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_51), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_51), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_51)); + pr_info("%s MTP Offset V87 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_87), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_87), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_87)); + pr_info("%s MTP Offset V151 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_151), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_151), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_151)); + pr_info("%s MTP Offset V203 R:%d G:%d B:%d\n", __func__, + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_203), + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_203), + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_203)); + pr_info("%s MTP Offset V255 R:%d G:%d B:%d\n", __func__, + char_to_int_v255(pSmart->MTP.R_OFFSET.OFFSET_255_MSB, + pSmart->MTP.R_OFFSET.OFFSET_255_LSB), + char_to_int_v255(pSmart->MTP.G_OFFSET.OFFSET_255_MSB, + pSmart->MTP.G_OFFSET.OFFSET_255_LSB), + char_to_int_v255(pSmart->MTP.B_OFFSET.OFFSET_255_MSB, + pSmart->MTP.B_OFFSET.OFFSET_255_LSB)); + + pr_info("%s V255 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_255, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_255, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_255); +#endif + + return 0; +} + +static void v255_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = ANA34801_VREG0_REF_L - + (pSmart->GRAY.TABLE[index[V255_INDEX]].R_Gray); + result_2 = result_1 * v255_denominator; + do_div(result_2, (ANA34801_VREG0_REF_L-ANA34801_VREG0_REF_H)); + result_3 = result_2 - v255_coefficient; + str[0] = (result_3 & 0xff00) >> 8; + str[1] = result_3 & 0xff; + + result_1 = ANA34801_VREG0_REF_L - + (pSmart->GRAY.TABLE[index[V255_INDEX]].G_Gray); + result_2 = result_1 * v255_denominator; + do_div(result_2, (ANA34801_VREG0_REF_L-ANA34801_VREG0_REF_H)); + result_3 = result_2 - v255_coefficient; + str[2] = (result_3 & 0xff00) >> 8; + str[3] = result_3 & 0xff; + + result_1 = ANA34801_VREG0_REF_L - + (pSmart->GRAY.TABLE[index[V255_INDEX]].B_Gray); + result_2 = result_1 * v255_denominator; + do_div(result_2, (ANA34801_VREG0_REF_L-ANA34801_VREG0_REF_H)); + result_3 = result_2 - v255_coefficient; + str[4] = (result_3 & 0xff00) >> 8; + str[5] = result_3 & 0xff; + +} + +static int vt_coefficient[] = { + 0, 12, 24, 36, 48, + 60, 72, 84, 96, 108, + 138, 148, 158, 168, + 178, 186, +}; +#define vt_denominator 600 +static int vt_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_1); + add_mtp = LSB + VT_300CD_R; + result_1 = result_2 = vt_coefficient[LSB] << BIT_SHIFT; + do_div(result_2, vt_denominator); + result_3 = (ANA34801_VREG0_REF_L * result_2) >> BIT_SHIFT; + result_4 = ANA34801_VREG0_REF_L - result_3; + pSmart->GRAY.VT_TABLE.R_Gray = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_1); + add_mtp = LSB + VT_300CD_G; + result_1 = result_2 = vt_coefficient[LSB] << BIT_SHIFT; + do_div(result_2, vt_denominator); + result_3 = (ANA34801_VREG0_REF_L * result_2) >> BIT_SHIFT; + result_4 = ANA34801_VREG0_REF_L - result_3; + pSmart->GRAY.VT_TABLE.G_Gray = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_1); + add_mtp = LSB + VT_300CD_B; + result_1 = result_2 = vt_coefficient[LSB] << BIT_SHIFT; + do_div(result_2, vt_denominator); + result_3 = (ANA34801_VREG0_REF_L * result_2) >> BIT_SHIFT; + result_4 = ANA34801_VREG0_REF_L - result_3; + pSmart->GRAY.VT_TABLE.B_Gray = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s VT RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->GRAY.VT_TABLE.R_Gray, + pSmart->GRAY.VT_TABLE.G_Gray, + pSmart->GRAY.VT_TABLE.B_Gray); +#endif + + return 0; + +} + +static void vt_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + str[30] = VT_300CD_R; + str[31] = VT_300CD_G; + str[32] = VT_300CD_B; +} + +#define v203_coefficient 64 +#define v203_denominator 320 +static int v203_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_203); + add_mtp = LSB + V203_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_255); + result_2 = (v203_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v203_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_203 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_203); + add_mtp = LSB + V203_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_255); + result_2 = (v203_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v203_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_203 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_203); + add_mtp = LSB + V203_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_255); + result_2 = (v203_coefficient+add_mtp) << BIT_SHIFT; + do_div(result_2, v203_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_203 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V203 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_203, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_203, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_203); +#endif + + return 0; + +} + +static void v203_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V203_INDEX]].R_Gray); + result_2 = result_1 * v203_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V255_INDEX]].R_Gray); + do_div(result_2, result_3); + str[6] = (result_2 - v203_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V203_INDEX]].G_Gray); + result_2 = result_1 * v203_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V255_INDEX]].G_Gray); + do_div(result_2, result_3); + str[7] = (result_2 - v203_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V203_INDEX]].B_Gray); + result_2 = result_1 * v203_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V255_INDEX]].B_Gray); + do_div(result_2, result_3); + str[8] = (result_2 - v203_coefficient) & 0xff; + +} + +#define v151_coefficient 64 +#define v151_denominator 320 +static int v151_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_151); + add_mtp = LSB + V151_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_203); + result_2 = (v151_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v151_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_151 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_151); + add_mtp = LSB + V151_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_203); + result_2 = (v151_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v151_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_151 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_151); + add_mtp = LSB + V151_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_203); + result_2 = (v151_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v151_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_151 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V151 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_151, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_151, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_151); +#endif + + return 0; + +} + +static void v151_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V151_INDEX]].R_Gray); + result_2 = result_1 * v151_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V203_INDEX]].R_Gray); + do_div(result_2, result_3); + str[9] = (result_2 - v151_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V151_INDEX]].G_Gray); + result_2 = result_1 * v151_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V203_INDEX]].G_Gray); + do_div(result_2, result_3); + str[10] = (result_2 - v151_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V151_INDEX]].B_Gray); + result_2 = result_1 * v151_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V203_INDEX]].B_Gray); + do_div(result_2, result_3); + str[11] = (result_2 - v151_coefficient) & 0xff; +} + +#define v87_coefficient 64 +#define v87_denominator 320 +static int v87_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_87); + add_mtp = LSB + V87_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_151); + result_2 = (v87_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v87_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_87 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_87); + add_mtp = LSB + V87_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_151); + result_2 = (v87_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v87_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_87 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_87); + add_mtp = LSB + V87_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_151); + result_2 = (v87_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v87_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_87 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V87 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_87, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_87, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_87); +#endif + + return 0; +} + +static void v87_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V87_INDEX]].R_Gray); + result_2 = result_1 * v87_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V151_INDEX]].R_Gray); + do_div(result_2, result_3); + str[12] = (result_2 - v87_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V87_INDEX]].G_Gray); + result_2 = result_1 * v87_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V151_INDEX]].G_Gray); + do_div(result_2, result_3); + str[13] = (result_2 - v87_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V87_INDEX]].B_Gray); + result_2 = result_1 * v87_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V151_INDEX]].B_Gray); + do_div(result_2, result_3); + str[14] = (result_2 - v87_coefficient) & 0xff; +} + +#define v51_coefficient 64 +#define v51_denominator 320 +static int v51_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_51); + add_mtp = LSB + V51_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_87); + result_2 = (v51_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v51_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_51 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_51); + add_mtp = LSB + V51_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_87); + result_2 = (v51_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v51_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_51 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_51); + add_mtp = LSB + V51_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_87); + result_2 = (v51_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v51_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_51 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V51 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_51, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_51, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_51); +#endif + + return 0; +} + +static void v51_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V51_INDEX]].R_Gray); + result_2 = result_1 * v51_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V87_INDEX]].R_Gray); + do_div(result_2, result_3); + str[15] = (result_2 - v51_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V51_INDEX]].G_Gray); + result_2 = result_1 * v51_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V87_INDEX]].G_Gray); + do_div(result_2, result_3); + str[16] = (result_2 - v51_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V51_INDEX]].B_Gray); + result_2 = result_1 * v51_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V87_INDEX]].B_Gray); + do_div(result_2, result_3); + str[17] = (result_2 - v51_coefficient) & 0xff; +} + +#define v35_coefficient 64 +#define v35_denominator 320 +static int v35_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_35); + add_mtp = LSB + V35_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_51); + result_2 = (v35_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v35_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_35 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_35); + add_mtp = LSB + V35_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_51); + result_2 = (v35_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v35_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_35 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_35); + add_mtp = LSB + V35_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_51); + result_2 = (v35_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v35_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_35 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V35 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_35, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_35, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_35); +#endif + + return 0; +} + +static void v35_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V35_INDEX]].R_Gray); + result_2 = result_1 * v35_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V51_INDEX]].R_Gray); + do_div(result_2, result_3); + str[18] = (result_2 - v35_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V35_INDEX]].G_Gray); + result_2 = result_1 * v35_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V51_INDEX]].G_Gray); + do_div(result_2, result_3); + str[19] = (result_2 - v35_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V35_INDEX]].B_Gray); + result_2 = result_1 * v35_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V51_INDEX]].B_Gray); + do_div(result_2, result_3); + str[20] = (result_2 - v35_coefficient) & 0xff; + +} + +#define v23_coefficient 64 +#define v23_denominator 320 +static int v23_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_23); + add_mtp = LSB + V23_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_35); + result_2 = (v23_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v23_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_23 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_23); + add_mtp = LSB + V23_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_35); + result_2 = (v23_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v23_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_23 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_23); + add_mtp = LSB + V23_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_35); + result_2 = (v23_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v23_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_23 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V23 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_23, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_23, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_23); +#endif + + return 0; +} + +static void v23_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V23_INDEX]].R_Gray); + result_2 = result_1 * v23_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V35_INDEX]].R_Gray); + do_div(result_2, result_3); + str[21] = (result_2 - v23_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V23_INDEX]].G_Gray); + result_2 = result_1 * v23_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V35_INDEX]].G_Gray); + do_div(result_2, result_3); + str[22] = (result_2 - v23_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V23_INDEX]].B_Gray); + result_2 = result_1 * v23_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V35_INDEX]].B_Gray); + do_div(result_2, result_3); + str[23] = (result_2 - v23_coefficient) & 0xff; +} + +#define v11_coefficient 64 +#define v11_denominator 320 +static int v11_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + unsigned long long add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_11); + add_mtp = LSB + V11_300CD_R; + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_23); + result_2 = (v11_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v11_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.R_Gray) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_11 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_11); + add_mtp = LSB + V11_300CD_G; + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_23); + result_2 = (v11_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v11_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.G_Gray) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_11 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_11); + add_mtp = LSB + V11_300CD_B; + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_23); + result_2 = (v11_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v11_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (pSmart->GRAY.VT_TABLE.B_Gray) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_11 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s V11 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_11, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_11, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_11); +#endif + + return 0; +} + +static void v11_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V11_INDEX]].R_Gray); + result_2 = result_1 * v11_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.R_Gray) + - (pSmart->GRAY.TABLE[index[V23_INDEX]].R_Gray); + do_div(result_2, result_3); + str[24] = (result_2 - v11_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V11_INDEX]].G_Gray); + result_2 = result_1 * v11_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.G_Gray) + - (pSmart->GRAY.TABLE[index[V23_INDEX]].G_Gray); + do_div(result_2, result_3); + str[25] = (result_2 - v11_coefficient) & 0xff; + + result_1 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V11_INDEX]].B_Gray); + result_2 = result_1 * v11_denominator; + result_3 = (pSmart->GRAY.VT_TABLE.B_Gray) + - (pSmart->GRAY.TABLE[index[V23_INDEX]].B_Gray); + do_div(result_2, result_3); + str[26] = (result_2 - v11_coefficient) & 0xff; + +} + +#define v3_coefficient 64 +#define v3_denominator 320 +static int v3_adjustment(struct SMART_DIM *pSmart) +{ + unsigned long long result_1, result_2, result_3, result_4; + int add_mtp; + int LSB; + + LSB = char_to_int(pSmart->MTP.R_OFFSET.OFFSET_3); + add_mtp = LSB + V3_300CD_R; + result_1 = (ANA34801_VREG0_REF_L) + - (pSmart->RGB_OUTPUT.R_VOLTAGE.level_11); + result_2 = (v3_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v3_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (ANA34801_VREG0_REF_L) - result_3; + pSmart->RGB_OUTPUT.R_VOLTAGE.level_3 = result_4; + + LSB = char_to_int(pSmart->MTP.G_OFFSET.OFFSET_3); + add_mtp = LSB + V3_300CD_G; + result_1 = (ANA34801_VREG0_REF_L) + - (pSmart->RGB_OUTPUT.G_VOLTAGE.level_11); + result_2 = (v3_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v3_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (ANA34801_VREG0_REF_L) - result_3; + pSmart->RGB_OUTPUT.G_VOLTAGE.level_3 = result_4; + + LSB = char_to_int(pSmart->MTP.B_OFFSET.OFFSET_3); + add_mtp = LSB + V3_300CD_B; + result_1 = (ANA34801_VREG0_REF_L) + - (pSmart->RGB_OUTPUT.B_VOLTAGE.level_11); + result_2 = (v3_coefficient + add_mtp) << BIT_SHIFT; + do_div(result_2, v3_denominator); + result_3 = (result_1 * result_2) >> BIT_SHIFT; + result_4 = (ANA34801_VREG0_REF_L) - result_3; + pSmart->RGB_OUTPUT.B_VOLTAGE.level_3 = result_4; + +#ifdef SMART_DIMMING_DEBUG + pr_err("%s V3 RED:%d GREEN:%d BLUE:%d\n", __func__, + pSmart->RGB_OUTPUT.R_VOLTAGE.level_3, + pSmart->RGB_OUTPUT.G_VOLTAGE.level_3, + pSmart->RGB_OUTPUT.B_VOLTAGE.level_3); +#endif + + return 0; +} + +static void v3_hexa(int *index, struct SMART_DIM *pSmart, char *str) +{ + unsigned long long result_1, result_2, result_3; + + result_1 = (ANA34801_VREG0_REF_L) + - (pSmart->GRAY.TABLE[index[V3_INDEX]].R_Gray); + result_2 = result_1 * v3_denominator; + result_3 = (ANA34801_VREG0_REF_L) + - (pSmart->GRAY.TABLE[index[V11_INDEX]].R_Gray); + do_div(result_2, result_3); + str[27] = (result_2 - v3_coefficient) & 0xff; + + result_1 = (ANA34801_VREG0_REF_L) + - (pSmart->GRAY.TABLE[index[V3_INDEX]].G_Gray); + result_2 = result_1 * v3_denominator; + result_3 = (ANA34801_VREG0_REF_L) + - (pSmart->GRAY.TABLE[index[V11_INDEX]].G_Gray); + do_div(result_2, result_3); + str[28] = (result_2 - v3_coefficient) & 0xff; + + result_1 = (ANA34801_VREG0_REF_L) + - (pSmart->GRAY.TABLE[index[V3_INDEX]].B_Gray); + result_2 = result_1 * v3_denominator; + result_3 = (ANA34801_VREG0_REF_L) + - (pSmart->GRAY.TABLE[index[V11_INDEX]].B_Gray); + do_div(result_2, result_3); + str[29] = (result_2 - v3_coefficient) & 0xff; +} + +/*V0,V1,V3,V11,V23,V35,V51,V87,V151,V203,V255*/ +static int INFLECTION_VOLTAGE_ARRAY[ARRAY_MAX] = {0, 1, 3, 11, 23, 35, 51, 87, 151, 203, 255}; + +#define V0toV3_Coefficient 2 +#define V0toV3_Multiple 1 +#define V0toV3_denominator 3 + +#define V3toV11_Coefficient 7 +#define V3toV11_Multiple 1 +#define V3toV11_denominator 8 + +#define V11toV23_Coefficient 11 +#define V11toV23_Multiple 1 +#define V11toV23_denominator 12 + +#define V23toV35_Coefficient 11 +#define V23toV35_Multiple 1 +#define V23toV35_denominator 12 + +#define V35toV51_Coefficient 15 +#define V35toV51_Multiple 1 +#define V35toV51_denominator 16 + +#define V51toV87_Coefficient 35 +#define V51toV87_Multiple 1 +#define V51toV87_denominator 36 + +#define V87toV151_Coefficient 63 +#define V87toV151_Multiple 1 +#define V87toV151_denominator 64 + +#define V151toV203_Coefficient 51 +#define V151toV203_Multiple 1 +#define V151toV203_denominator 52 + +#define V203toV255_Coefficient 51 +#define V203toV255_Multiple 1 +#define V203toV255_denominator 52 + +static int cal_gray_scale_linear(int up, int low, int coeff, +int mul, int deno, int cnt) +{ + unsigned long long result_1, result_2, result_3, result_4; + + result_1 = up - low; + result_2 = (result_1 * (coeff - (cnt * mul))) << BIT_SHIFT; + do_div(result_2, deno); + result_3 = result_2 >> BIT_SHIFT; + result_4 = low + result_3; + + return (int)result_4; +} + +static int generate_gray_scale(struct SMART_DIM *pSmart) +{ + int cnt = 0, cal_cnt = 0; + int array_index = 0; + struct GRAY_VOLTAGE *ptable = (struct GRAY_VOLTAGE *) + (&(pSmart->GRAY.TABLE)); + + for (cnt = 0; cnt < ARRAY_MAX; cnt++) { + pSmart->GRAY.TABLE[INFLECTION_VOLTAGE_ARRAY[cnt]].R_Gray = + ((int *)&(pSmart->RGB_OUTPUT.R_VOLTAGE))[cnt]; + + pSmart->GRAY.TABLE[INFLECTION_VOLTAGE_ARRAY[cnt]].G_Gray = + ((int *)&(pSmart->RGB_OUTPUT.G_VOLTAGE))[cnt]; + + pSmart->GRAY.TABLE[INFLECTION_VOLTAGE_ARRAY[cnt]].B_Gray = + ((int *)&(pSmart->RGB_OUTPUT.B_VOLTAGE))[cnt]; + } + + /* + below codes use hard coded value. + So it is possible to modify on each model. + V0,V1,V3,V11,V23,V35,V51,V87,V151,V203,V255 + */ + for (cnt = 0; cnt < GRAY_SCALE_MAX; cnt++) { + + if (cnt == INFLECTION_VOLTAGE_ARRAY[0]) { + /* 0 */ + array_index = 0; + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[0]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[2])) { + /* 1 ~ 2 */ + array_index = 2; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-2]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V0toV3_Coefficient, V0toV3_Multiple, + V0toV3_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-2]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V0toV3_Coefficient, V0toV3_Multiple, + V0toV3_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-2]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V0toV3_Coefficient, V0toV3_Multiple, + V0toV3_denominator , cal_cnt); + + cal_cnt++; + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[2]) { + /* 3 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[2]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[3])) { + /* 4 ~ 10 */ + array_index = 3; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V3toV11_Coefficient, V3toV11_Multiple, + V3toV11_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V3toV11_Coefficient, V3toV11_Multiple, + V3toV11_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V3toV11_Coefficient, V3toV11_Multiple, + V3toV11_denominator , cal_cnt); + + cal_cnt++; + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[3]) { + /* 11 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[3]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[4])) { + /* 12 ~ 22 */ + array_index = 4; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V11toV23_Coefficient, V11toV23_Multiple, + V11toV23_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V11toV23_Coefficient, V11toV23_Multiple, + V11toV23_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V11toV23_Coefficient, V11toV23_Multiple, + V11toV23_denominator , cal_cnt); + + cal_cnt++; + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[4]) { + /* 23 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[4]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[5])) { + /* 24 ~ 34 */ + array_index = 5; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V23toV35_Coefficient, V23toV35_Multiple, + V23toV35_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V23toV35_Coefficient, V23toV35_Multiple, + V23toV35_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V23toV35_Coefficient, V23toV35_Multiple, + V23toV35_denominator , cal_cnt); + + cal_cnt++; + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[5]) { + /* 35 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[5]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[6])) { + /* 36 ~ 50 */ + array_index = 6; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V35toV51_Coefficient, V35toV51_Multiple, + V35toV51_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V35toV51_Coefficient, V35toV51_Multiple, + V35toV51_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V35toV51_Coefficient, V35toV51_Multiple, + V35toV51_denominator, cal_cnt); + cal_cnt++; + + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[6]) { + /* 51 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[6]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[7])) { + /* 52 ~ 86 */ + array_index = 7; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V51toV87_Coefficient, V51toV87_Multiple, + V51toV87_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V51toV87_Coefficient, V51toV87_Multiple, + V51toV87_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V51toV87_Coefficient, V51toV87_Multiple, + V51toV87_denominator, cal_cnt); + cal_cnt++; + + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[7]) { + /* 87 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[7]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[8])) { + /* 88 ~ 150 */ + array_index = 8; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V87toV151_Coefficient, V87toV151_Multiple, + V87toV151_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V87toV151_Coefficient, V87toV151_Multiple, + V87toV151_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V87toV151_Coefficient, V87toV151_Multiple, + V87toV151_denominator, cal_cnt); + + cal_cnt++; + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[8]) { + /* 151 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[8]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[9])) { + /* 152 ~ 202 */ + array_index = 9; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V151toV203_Coefficient, V151toV203_Multiple, + V151toV203_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V151toV203_Coefficient, V151toV203_Multiple, + V151toV203_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V151toV203_Coefficient, V151toV203_Multiple, + V151toV203_denominator, cal_cnt); + + cal_cnt++; + } else if (cnt == INFLECTION_VOLTAGE_ARRAY[9]) { + /* 203 */ + cal_cnt = 0; + } else if ((cnt > INFLECTION_VOLTAGE_ARRAY[9]) && + (cnt < INFLECTION_VOLTAGE_ARRAY[10])) { + /* 204 ~ 254 */ + array_index = 10; + + pSmart->GRAY.TABLE[cnt].R_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].R_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].R_Gray, + V203toV255_Coefficient, V203toV255_Multiple, + V203toV255_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].G_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].G_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].G_Gray, + V203toV255_Coefficient, V203toV255_Multiple, + V203toV255_denominator, cal_cnt); + + pSmart->GRAY.TABLE[cnt].B_Gray = cal_gray_scale_linear( + ptable[INFLECTION_VOLTAGE_ARRAY[array_index-1]].B_Gray, + ptable[INFLECTION_VOLTAGE_ARRAY[array_index]].B_Gray, + V203toV255_Coefficient, V203toV255_Multiple, + V203toV255_denominator, cal_cnt); + + cal_cnt++; + } else { + if (cnt == INFLECTION_VOLTAGE_ARRAY[10]) { + pr_debug("%s end\n", __func__); + } else { + pr_err("%s fail cnt:%d\n", __func__, cnt); + return -EINVAL; + } + } + + } + +#ifdef SMART_DIMMING_DEBUG + for (cnt = 0; cnt < GRAY_SCALE_MAX; cnt++) { + pr_info("%s %8d %8d %8d %d\n", __func__, + pSmart->GRAY.TABLE[cnt].R_Gray, + pSmart->GRAY.TABLE[cnt].G_Gray, + pSmart->GRAY.TABLE[cnt].B_Gray, cnt); + } +#endif + return 0; +} + +#if 0 +static char offset_cal(int offset, int value) +{ + if (value - offset < 0) + return 0; + else if (value - offset > 255) + return 0xFF; + else + return value - offset; +} + +static void mtp_offset_substraction(struct SMART_DIM *pSmart, int *str) +{ + int level_255_temp = 0; + int level_255_temp_MSB = 0; + int MTP_V255; + + /*subtration MTP_OFFSET value from generated gamma table*/ + level_255_temp = (str[0] << 8) | str[1] ; + MTP_V255 = char_to_int_v255(pSmart->MTP.R_OFFSET.OFFSET_255_MSB, + pSmart->MTP.R_OFFSET.OFFSET_255_LSB); + level_255_temp -= MTP_V255; + level_255_temp_MSB = level_255_temp / 256; + str[0] = level_255_temp_MSB & 0xff; + str[1] = level_255_temp & 0xff; + + level_255_temp = (str[2] << 8) | str[3] ; + MTP_V255 = char_to_int_v255(pSmart->MTP.G_OFFSET.OFFSET_255_MSB, + pSmart->MTP.G_OFFSET.OFFSET_255_LSB); + level_255_temp -= MTP_V255; + level_255_temp_MSB = level_255_temp / 256; + str[2] = level_255_temp_MSB & 0xff; + str[3] = level_255_temp & 0xff; + + level_255_temp = (str[4] << 8) | str[5] ; + MTP_V255 = char_to_int_v255(pSmart->MTP.B_OFFSET.OFFSET_255_MSB, + pSmart->MTP.B_OFFSET.OFFSET_255_LSB); + level_255_temp -= MTP_V255; + level_255_temp_MSB = level_255_temp / 256; + str[4] = level_255_temp_MSB & 0xff; + str[5] = level_255_temp & 0xff; + + str[6] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_203), str[6]); + str[7] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_203), str[7]); + str[8] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_203), str[8]); + + str[9] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_151), str[9]); + str[10] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_151), str[10]); + str[11] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_151), str[11]); + + str[12] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_87), str[12]); + str[13] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_87), str[13]); + str[14] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_87), str[14]); + + str[15] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_51), str[15]); + str[16] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_51), str[16]); + str[17] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_51), str[17]); + + str[18] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_35), str[18]); + str[19] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_35), str[19]); + str[20] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_35), str[20]); + + str[21] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_23), str[21]); + str[22] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_23), str[22]); + str[23] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_23), str[23]); + + str[24] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_11), str[24]); + str[25] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_11), str[25]); + str[26] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_11), str[26]); + + str[27] = offset_cal(char_to_int(pSmart->MTP.R_OFFSET.OFFSET_3), str[27]); + str[28] = offset_cal(char_to_int(pSmart->MTP.G_OFFSET.OFFSET_3), str[28]); + str[29] = offset_cal(char_to_int(pSmart->MTP.B_OFFSET.OFFSET_3), str[29]); +} +#endif + +static int searching_function(long long candela, int *index, int gamma_curve) +{ + long long delta_1 = 0, delta_2 = 0; + int cnt; + + /* + * This searching_functin should be changed with improved + searcing algorithm to reduce searching time. + */ + *index = -1; + + for (cnt = 0; cnt < (GRAY_SCALE_MAX-1); cnt++) { + if (gamma_curve == GAMMA_CURVE_1P9) { + delta_1 = candela - curve_1p9_360[cnt]; + delta_2 = candela - curve_1p9_360[cnt+1]; + } else if (gamma_curve == GAMMA_CURVE_2P15) { + delta_1 = candela - curve_2p15_360[cnt]; + delta_2 = candela - curve_2p15_360[cnt+1]; + } else if (gamma_curve == GAMMA_CURVE_2P2) { + delta_1 = candela - curve_2p2_360[cnt]; + delta_2 = candela - curve_2p2_360[cnt+1]; + } else { + delta_1 = candela - curve_2p2_360[cnt]; + delta_2 = candela - curve_2p2_360[cnt+1]; + } + + if (delta_2 < 0) { + *index = (delta_1 + delta_2) <= 0 ? cnt : cnt+1; + break; + } + + if (delta_1 == 0) { + *index = cnt; + break; + } + + if (delta_2 == 0) { + *index = cnt+1; + break; + } + } + + if (*index == -1) + return -EINVAL; + else + return 0; +} + +/* -1 means V1 */ +#define TABLE_MAX (ARRAY_MAX-1) +static void(*Make_hexa[TABLE_MAX])(int*, struct SMART_DIM*, char*) = { + v255_hexa, + v203_hexa, + v151_hexa, + v87_hexa, + v51_hexa, + v35_hexa, + v23_hexa, + v11_hexa, + v3_hexa, + vt_hexa, +}; + +static int get_base_luminance(struct SMART_DIM *pSmart) +{ + int cnt; + int base_luminance[LUMINANCE_MAX][2]; + + memcpy(base_luminance, base_luminance_revA, sizeof(base_luminance_revA)); + + for (cnt = 0; cnt < LUMINANCE_MAX; cnt++) + if (base_luminance[cnt][0] == pSmart->brightness_level) + return base_luminance[cnt][1]; + + return -1; +} + +static int find_cadela_table(int brightness) +{ + int loop; + int err = -1; + + for(loop = 0; loop <= CANDELA_MAX_TABLE; loop++) + if (candela_table[loop][0] == brightness) + return candela_table[loop][1]; + + return err; +} + +static void gamma_init_revA(struct SMART_DIM *pSmart, char *str, int size) +{ + long long candela_level[TABLE_MAX] = {-1, }; + int bl_index[TABLE_MAX] = {-1, }; + int gamma_setting[GAMMA_SET_MAX]; + + long long temp_cal_data = 0; + int bl_level = 0; + + int level_255_temp_MSB = 0; + int level_V255 = 0; + + int point_index; + int cnt; + int table_index; + + pr_debug("%s : start !!\n",__func__); + + bl_level = get_base_luminance(pSmart); + if (bl_level < 0) + pr_err("%s : can not find base luminance!!\n", __func__); + + if (pSmart->brightness_level < 360) { + for (cnt = 0; cnt < TABLE_MAX; cnt++) { + point_index = INFLECTION_VOLTAGE_ARRAY[cnt+1]; + temp_cal_data = + ((long long)(candela_coeff_2p15[point_index])) * + ((long long)(bl_level)); + candela_level[cnt] = temp_cal_data; + } + } else { + for (cnt = 0; cnt < TABLE_MAX; cnt++) { + point_index = INFLECTION_VOLTAGE_ARRAY[cnt+1]; + temp_cal_data = + ((long long)(candela_coeff_2p2[point_index])) * + ((long long)(bl_level)); + candela_level[cnt] = temp_cal_data; + } + } + +#ifdef SMART_DIMMING_DEBUG + pr_info("\n candela_1:%llu candela_3:%llu candela_11:%llu \n" + "candela_23:%llu candela_35:%llu candela_51:%llu \n" + "candela_87:%llu candela_151:%llu candela_203:%llu \n" + "candela_255:%llu brightness_level %d \n", + candela_level[0], candela_level[1], candela_level[2], + candela_level[3], candela_level[4], candela_level[5], + candela_level[6], candela_level[7], candela_level[8], + candela_level[9], pSmart->brightness_level); +#endif + + for (cnt = 0; cnt < TABLE_MAX; cnt++) { + if (searching_function(candela_level[cnt], + &(bl_index[cnt]), GAMMA_CURVE_2P2)) { + pr_info("%s searching functioin error cnt:%d\n", + __func__, cnt); + } + } + + /* + * Candela compensation + */ + for (cnt = 1; cnt < TABLE_MAX; cnt++) { + table_index = find_cadela_table(pSmart->brightness_level); + + if (table_index == -1) { + table_index = CANDELA_MAX_TABLE; + pr_info("%s fail candela table_index cnt : %d brightness %d\n", + __func__, cnt, pSmart->brightness_level); + } + + bl_index[TABLE_MAX - cnt] += + gradation_offset_revA[table_index][cnt - 1]; + + /* THERE IS M-GRAY0 target */ + if (bl_index[TABLE_MAX - cnt] == 0) + bl_index[TABLE_MAX - cnt] = 1; + } + +#ifdef SMART_DIMMING_DEBUG + pr_info("\n bl_index_1:%d bl_index_3:%d bl_index_11:%d \n" + "bl_index_23:%d bl_index_35:%d bl_index_51:%d \n" + "bl_index_87:%d bl_index_151:%d bl_index_203:%d \n" + "bl_index_255:%d brightness_level %d \n", + bl_index[0], bl_index[1], bl_index[2], + bl_index[3], bl_index[4], bl_index[5], + bl_index[6], bl_index[7], bl_index[8], + bl_index[9], pSmart->brightness_level); +#endif + /*Generate Gamma table*/ + for (cnt = 0; cnt < TABLE_MAX; cnt++) + (void)Make_hexa[cnt](bl_index , pSmart, str); + + /* To avoid overflow */ + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) + gamma_setting[cnt] = str[cnt]; + + /* + * RGB compensation + */ + for (cnt = 0; cnt < RGB_COMPENSATION; cnt++) { + table_index = find_cadela_table(pSmart->brightness_level); + + if (table_index == -1) { + table_index = CANDELA_MAX_TABLE; + pr_info("%s fail RGB table_index cnt : %d brightness %d", + __func__, cnt, pSmart->brightness_level); + } + + if (cnt < 3) { + level_V255 = str[cnt * 2] << 8 | str[(cnt * 2) + 1]; + level_V255 += + rgb_offset_revA[table_index][cnt]; + level_255_temp_MSB = level_V255 / 256; + + gamma_setting[cnt * 2] = level_255_temp_MSB & 0xff; + gamma_setting[(cnt * 2) + 1] = level_V255 & 0xff; + } else { + gamma_setting[cnt+3] += rgb_offset_revA[table_index][cnt]; + } + } + + /*subtration MTP_OFFSET value from generated gamma table*/ + /* mtp_offset_substraction is not need because Change Gamma Offset Index [4] is used. */ + /* mtp_offset_substraction(pSmart, gamma_setting); */ + + /* To avoid overflow */ + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) + str[cnt] = gamma_setting[cnt]; +} + +static void generate_hbm_gamma(struct SMART_DIM *psmart, char *str, int size) +{ +#ifdef SMART_DIMMING_DEBUG + int cnt; +#endif + struct illuminance_table *ptable = (struct illuminance_table *) + (&(psmart->hbm_interpolation_table)); + memcpy(str, &(ptable[psmart->hbm_brightness_level].gamma_setting), size); + +#ifdef SMART_DIMMING_DEBUG + pr_info("%s\n",__func__); + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) + pr_info("0x%x ", str[cnt]); + pr_info("\n"); +#endif +} + +static void generate_gamma(struct SMART_DIM *psmart, char *str, int size) +{ + int lux_loop; + struct illuminance_table *ptable = (struct illuminance_table *) + (&(psmart->gen_table)); + + /* searching already generated gamma table */ + for (lux_loop = 0; lux_loop < psmart->lux_table_max; lux_loop++) { + if (ptable[lux_loop].lux == psmart->brightness_level) { + memcpy(str, &(ptable[lux_loop].gamma_setting), size); + break; + } + } + + /* searching fail... Setting 300CD value on gamma table */ + if (lux_loop == psmart->lux_table_max) { + pr_info("%s searching fail lux : %d\n", __func__, + psmart->brightness_level); + memcpy(str, max_lux_table, size); + } + +#ifdef SMART_DIMMING_DEBUG + if (lux_loop != psmart->lux_table_max) + pr_info("%s searching ok index : %d lux : %d\n", __func__, + lux_loop, ptable[lux_loop].lux); +#endif +} + +static void mtp_sorting(struct SMART_DIM *psmart) +{ + int sorting[GAMMA_SET_MAX] = { + 9, 7, 6, 5, 4, 3, 2, 1, 0, 8, 10, /* R*/ + 20, 18, 17, 16, 15, 14, 13, 12, 11, 19, 21, /* G */ + 31, 29, 28, 27, 26, 25, 24, 23, 22, 30, 32, /* B */ + }; + int loop; + char *pfrom, *pdest; + + pfrom = (char *)&(psmart->MTP_ORIGN); + pdest = (char *)&(psmart->MTP); + + for (loop = 0; loop < GAMMA_SET_MAX; loop++) + pdest[loop] = pfrom[sorting[loop]]; + + for (loop = 0; loop < 3 ; loop++) { + if(pdest[loop*11]) + pdest[loop * 11] *= -1; + } +} + +static void gamma_cell_determine(int ldi_revision) +{ + pr_info("%s ldi_revision: 0x%x", __func__, ldi_revision); + + max_lux_table[0] = V255_300CD_R_MSB = V255_300CD_R_MSB_20; + max_lux_table[1] = V255_300CD_R_LSB = V255_300CD_R_LSB_20; + + max_lux_table[2] = V255_300CD_G_MSB = V255_300CD_G_MSB_20; + max_lux_table[3] = V255_300CD_G_LSB = V255_300CD_G_LSB_20; + + max_lux_table[4] = V255_300CD_B_MSB = V255_300CD_B_MSB_20; + max_lux_table[5] = V255_300CD_B_LSB = V255_300CD_B_LSB_20; + + max_lux_table[6] = V203_300CD_R = V203_300CD_R_20; + max_lux_table[7] = V203_300CD_G = V203_300CD_G_20; + max_lux_table[8] = V203_300CD_B = V203_300CD_B_20; + + max_lux_table[9] = V151_300CD_R = V151_300CD_R_20; + max_lux_table[10] = V151_300CD_G = V151_300CD_G_20; + max_lux_table[11] = V151_300CD_B = V151_300CD_B_20; + + max_lux_table[12] = V87_300CD_R = V87_300CD_R_20; + max_lux_table[13] = V87_300CD_G = V87_300CD_G_20; + max_lux_table[14] = V87_300CD_B = V87_300CD_B_20; + + max_lux_table[15] = V51_300CD_R = V51_300CD_R_20; + max_lux_table[16] = V51_300CD_G = V51_300CD_G_20; + max_lux_table[17] = V51_300CD_B = V51_300CD_B_20; + + max_lux_table[18] = V35_300CD_R = V35_300CD_R_20; + max_lux_table[19] = V35_300CD_G = V35_300CD_G_20; + max_lux_table[20] = V35_300CD_B = V35_300CD_B_20; + + max_lux_table[21] = V23_300CD_R = V23_300CD_R_20; + max_lux_table[22] = V23_300CD_G = V23_300CD_G_20; + max_lux_table[23] = V23_300CD_B = V23_300CD_B_20; + + max_lux_table[24] = V11_300CD_R = V11_300CD_R_20; + max_lux_table[25] = V11_300CD_G = V11_300CD_G_20; + max_lux_table[26] = V11_300CD_B = V11_300CD_B_20; + + max_lux_table[27] = V3_300CD_R = V3_300CD_R_20; + max_lux_table[28] = V3_300CD_G = V3_300CD_G_20; + max_lux_table[29] = V3_300CD_B = V3_300CD_B_20; + + max_lux_table[30] = VT_300CD_R = VT_300CD_R_20; + max_lux_table[31] = VT_300CD_G = VT_300CD_G_20; + max_lux_table[32] = VT_300CD_B = VT_300CD_B_20; +} + +static void gamma_cell_determine_max(struct SMART_DIM *pSmart) +{ +#ifdef SMART_DIMMING_DEBUG + int i; + char log_buf[256]; +#endif + int temp = 0; /* MAX_V255 */ + + /* RED */ + max_lux_table2[0] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_11); + max_lux_table2[1] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_23); + max_lux_table2[2] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_35); + max_lux_table2[3] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_51); + max_lux_table2[4] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_87); + max_lux_table2[5] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_151); + max_lux_table2[6] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_203); + + temp = 256 + char_to_int_v255(pSmart->MTP.R_OFFSET.OFFSET_255_MSB, pSmart->MTP.R_OFFSET.OFFSET_255_LSB); + if (temp >= 256) + max_lux_table2[7] = temp - 256; + else + max_lux_table2[7] = temp; + max_lux_table2[8] = 128 + char_to_int(pSmart->MTP.R_OFFSET.OFFSET_3); + + if (temp >= 256) + max_lux_table2[9] = 0x80; /* V255[8] bit */ + else + max_lux_table2[9] = 0; + max_lux_table2[10] = 0; + + /* GREEN */ + max_lux_table2[11] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_11); + max_lux_table2[12] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_23); + max_lux_table2[13] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_35); + max_lux_table2[14] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_51); + max_lux_table2[15] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_87); + max_lux_table2[16] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_151); + max_lux_table2[17] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_203); + + temp = 256 + char_to_int_v255(pSmart->MTP.G_OFFSET.OFFSET_255_MSB, pSmart->MTP.G_OFFSET.OFFSET_255_LSB); + if (temp >= 256) + max_lux_table2[18] = temp - 256; + else + max_lux_table2[18] = temp; + max_lux_table2[19] = 128 + char_to_int(pSmart->MTP.G_OFFSET.OFFSET_3); + + if (temp >= 256) + max_lux_table2[20] = 0x80; + else + max_lux_table2[20] = 0; + max_lux_table2[21] = 0; + + /* BLUE */ + max_lux_table2[22] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_11); + max_lux_table2[23] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_23); + max_lux_table2[24] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_35); + max_lux_table2[25] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_51); + max_lux_table2[26] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_87); + max_lux_table2[27] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_151); + max_lux_table2[28] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_203); + + temp = 256 + char_to_int_v255(pSmart->MTP.B_OFFSET.OFFSET_255_MSB, pSmart->MTP.B_OFFSET.OFFSET_255_LSB); + if (temp >= 256) + max_lux_table2[29] = temp - 256; + else + max_lux_table2[29] = temp; + max_lux_table2[30] = 128 + char_to_int(pSmart->MTP.B_OFFSET.OFFSET_3); + + if (temp >= 256) + max_lux_table2[31] = 0x80; + else + max_lux_table2[31] = 0; + max_lux_table2[32] = 0; + +#ifdef SMART_DIMMING_DEBUG + memset(log_buf, 0x00, 256); + for (i = 0; i < GAMMA_SET_MAX; i++) + snprintf(log_buf + strnlen(log_buf, 256), 256, " %03d", max_lux_table2[i]); + LCD_INFO(" %s\n", log_buf); +#endif +} + +static char offeset_add(int offset, int value) +{ + if (value + offset < 0) + return 0; + else if (value + offset > 255) + return 0x80; + else + return value + offset; +} + +static void gamma_command_sorting_post(struct SMART_DIM *psmart) +{ + int sorting[GAMMA_SET_MAX] = { + 24, 21, 18, 15, 12, 9, 6, 1, 27, 0, 30, /* R*/ + 25, 22, 19, 16, 13, 10, 7, 3, 28, 2, 31, /* G */ + 26, 23, 20, 17, 14, 11, 8, 5, 29, 4, 32, /* B */ + }; + int loop, loop2; + char gamma_temp[GAMMA_SET_MAX]; + char *pfrom; + + pfrom = (char *)&(psmart->MTP_ORIGN); + + for (loop = 0; loop < LUMINANCE_MAX; loop++) { + /* sorting */ + for (loop2 = 0; loop2 < GAMMA_SET_MAX; loop2++) { + if(loop == LUMINANCE_MAX-1 ) { + if(psmart->gen_table[loop].gamma_setting[sorting[loop2]] == 0x01) + gamma_temp[loop2] = + offeset_add(psmart->gen_table[loop].gamma_setting[sorting[loop2]], pfrom[loop2] + 0x7F); + else + gamma_temp[loop2] = + offeset_add(psmart->gen_table[loop].gamma_setting[sorting[loop2]], pfrom[loop2]); + } else { + gamma_temp[loop2] = psmart->gen_table[loop].gamma_setting[sorting[loop2]]; + } + } + + for (loop2 = 0; loop2 < 3; loop2++) { + if(gamma_temp[(loop2*11)+9] == 0x01) + gamma_temp[(loop2*11)+9] = + gamma_temp[(loop2*11)+9] << 7; + } + + /* Copy */ + memcpy(psmart->gen_table[loop].gamma_setting, + gamma_temp, GAMMA_SET_MAX); + } +} + +#define HBM_CANDELA 500 +#define HBM_GAMMA_SET_CNT 33 +#define V255_RGB_MSB_CNT 3 +static void hbm_interpolation_init(struct SMART_DIM *pSmart) +{ + int loop, gamma_index, loop2; + int rate; + int hbm_gamma[HBM_GAMMA_SET_CNT]; + int max_gamma[HBM_GAMMA_SET_CNT]; + char *hbm_payload; + int i; + char log_buf[256]; + char V255_R_MSB; + char V255_R_LSB; + int V255_R; /* V255_RED [8:0]*/ + char V255_G_MSB; + char V255_G_LSB; + int V255_G; /* V255_GREEN [8:0]*/ + char V255_B_MSB; + char V255_B_LSB; + int V255_B; /* V255_BLUE [8:0]*/ + int hbm_interpolation_gamma[HBM_INTERPOLATION_STEP][HBM_GAMMA_SET_CNT]; + int normal_max_candela = pSmart->gen_table[LUMINANCE_MAX-1].lux; + + hbm_payload = pSmart->hbm_payload; + if (!hbm_payload) { + LCD_ERR("no hbm_payload..\n"); + return; + } + + /* 1. Generate HBM GAMMA */ + /* 1-1. Read From D4 Register */ + for (loop = 0, gamma_index = 0; gamma_index < HBM_GAMMA_SET_CNT;) { + hbm_gamma[loop++] = hbm_payload[gamma_index]; + gamma_index++; + } +#ifdef SMART_DIMMING_DEBUG + memset(log_buf, 0x00, 256); + for (i = 0; i < GAMMA_SET_MAX; i++) + snprintf(log_buf + strnlen(log_buf, 256), 256, " %02x", hbm_gamma[i]); + LCD_INFO("BEFORE Calculate hbm_gamma HEX : %s\n", log_buf); +#endif + memset(log_buf, 0x00, 256); + for (i = 0; i < GAMMA_SET_MAX; i++) + snprintf(log_buf + strnlen(log_buf, 256), 256, " %03d", hbm_gamma[i]); + LCD_INFO("BEFORE Calculate hbm_gamma DEC: %s\n", log_buf); + + /* 1-2. Calculate : D4 + OFFSET */ + /* RED */ + V255_R_MSB = hbm_gamma[9] >> 7; + V255_R_LSB = hbm_gamma[7]; + V255_R = char_to_int(V255_R_MSB << 8 | V255_R_LSB) + + char_to_int_v255(pSmart->MTP.R_OFFSET.OFFSET_255_MSB, pSmart->MTP.R_OFFSET.OFFSET_255_LSB); + if (V255_R_MSB >= 1 ) + V255_R = V255_R + 256; /* V255_RED [8:0] */ + + LCD_INFO("V255_R_MSB=0x(%x) V255_R_LSB=0x(%x) V255_R=0x(%x)(%d)\n", V255_R_MSB, V255_R_LSB, V255_R, V255_R); + hbm_gamma[0] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_11); + hbm_gamma[1] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_23); + hbm_gamma[2] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_35); + hbm_gamma[3] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_51); + hbm_gamma[4] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_87); + hbm_gamma[5] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_151); + hbm_gamma[6] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_203); + hbm_gamma[7] = V255_R; + hbm_gamma[8] += char_to_int(pSmart->MTP.R_OFFSET.OFFSET_3); + + if ( V255_R_MSB >= 1 ) + hbm_gamma[9] = 0x80; + else + hbm_gamma[9] = 0x00; + hbm_gamma[10] = 0x00; + + /* GREEN */ + V255_G_MSB = hbm_gamma[20] >> 7; + V255_G_LSB = hbm_gamma[18]; + V255_G = char_to_int(V255_G_MSB << 8 | V255_G_LSB) + + char_to_int_v255(pSmart->MTP.G_OFFSET.OFFSET_255_MSB, pSmart->MTP.G_OFFSET.OFFSET_255_LSB); + if (V255_G_MSB >= 1 ) + V255_G = V255_G + 256; /* V255_GREEN [8:0] */ + + LCD_INFO("V255_G_MSB=0x(%x) V255_G_LSB=0x(%x) V255_G=0x(%x)(%d)\n", V255_G_MSB, V255_G_LSB, V255_G, V255_G); + hbm_gamma[11] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_11); + hbm_gamma[12] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_23); + hbm_gamma[13] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_35); + hbm_gamma[14] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_51); + hbm_gamma[15] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_87); + hbm_gamma[16] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_151); + hbm_gamma[17] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_203); + hbm_gamma[18] = V255_G; + hbm_gamma[19] += char_to_int(pSmart->MTP.G_OFFSET.OFFSET_3); + + if ( V255_G_MSB >= 1 ) + hbm_gamma[20] = 0x80; + else + hbm_gamma[20] = 0x00; + hbm_gamma[21] = 0x00; + + /* BLUE */ + V255_B_MSB = hbm_gamma[31] >> 7; + V255_B_LSB = hbm_gamma[29]; + V255_B = char_to_int(V255_B_MSB << 8 | V255_B_LSB) + + char_to_int_v255(pSmart->MTP.B_OFFSET.OFFSET_255_MSB, pSmart->MTP.B_OFFSET.OFFSET_255_LSB); + if (V255_B_MSB >= 1 ) + V255_B = V255_B + 256; /* V255_BLUE [8:0] */ + + LCD_INFO("V255_B_MSB=0x(%x) V255_B_LSB=0x(%x) V255_B=0x(%x)(%d)\n", V255_B_MSB, V255_B_LSB, V255_B, V255_B); + hbm_gamma[22] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_11); + hbm_gamma[23] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_23); + hbm_gamma[24] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_35); + hbm_gamma[25] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_51); + hbm_gamma[26] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_87); + hbm_gamma[27] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_151); + hbm_gamma[28] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_203); + hbm_gamma[29] = V255_B; + hbm_gamma[30] += char_to_int(pSmart->MTP.B_OFFSET.OFFSET_3); + + if ( V255_B_MSB >= 1 ) + hbm_gamma[31] = 0x80; + else + hbm_gamma[31] = 0x00; + hbm_gamma[32] = 0x00; + +#ifdef SMART_DIMMING_DEBUG + memset(log_buf, 0x00, 256); + for (i = 0; i < GAMMA_SET_MAX; i++) + snprintf(log_buf + strnlen(log_buf, 256), 256, " %02x", hbm_gamma[i]); + LCD_INFO("AFTER Calculate hbm_gamma HEX : %s\n", log_buf); +#endif + memset(log_buf, 0x00, 256); + for (i = 0; i < GAMMA_SET_MAX; i++) + snprintf(log_buf + strnlen(log_buf, 256), 256, " %03d", hbm_gamma[i]); + LCD_INFO("AFTER Calculate hbm_gamma DEC : %s\n", log_buf); + + /* 2. Generate MAX GAMMA */ + /* 2-1. Set Max gamma from gamma_setting Table */ + for (loop = 0, gamma_index = 0; gamma_index < HBM_GAMMA_SET_CNT;) { + max_gamma[loop++] = pSmart->gen_table[LUMINANCE_MAX-1].gamma_setting[gamma_index]; + gamma_index++; + } + + /* 2-2. MAX GAMMA V255 process : max_gamma = V255[8] | V255[7:0] */ + if ( max_gamma[9] >= 0x80 ) + max_gamma[7] += 256; /* V255_RED [8:0] */ + + if ( max_gamma[20] >= 0x80 ) + max_gamma[18] += 256; /* V255_GREEN [8:0] */ + + if ( max_gamma[31] >= 0x80 ) + max_gamma[29] += 256; /* V255_BLUE [8:0] */ + + /* 3. Generate interpolation hbm gamma */ + /* 3-1. Divide 8 Step */ + for (loop = 0 ; loop < HBM_INTERPOLATION_STEP; loop++) { + rate = ((hbm_interpolation_candela_table[loop] - normal_max_candela) * BIT_SHFIT_MUL) / (HBM_CANDELA - normal_max_candela); + for (gamma_index = 0; gamma_index < HBM_GAMMA_SET_CNT; gamma_index++) { + hbm_interpolation_gamma[loop][gamma_index] = max_gamma[gamma_index] + + ((hbm_gamma[gamma_index] - max_gamma[gamma_index]) * rate) / BIT_SHFIT_MUL; + + /* 3-2. Addtional process : hbm_interpolation_gamma (After Divide 8 Step) is more than 256 */ + if ( gamma_index == 9 || gamma_index == 20 || gamma_index == 31 ) { + if (hbm_interpolation_gamma[loop][gamma_index - 2] >= 256 ) /* V255 Check */ + hbm_interpolation_gamma[loop][gamma_index] = 0x80; /* V255[8] | V0[6:0] */ + else + hbm_interpolation_gamma[loop][gamma_index] = 0x00; + } + } + } + + /* 4. Generate hbm_interpolation_table */ + for (loop = 0; loop < HBM_INTERPOLATION_STEP; loop++) { + for (gamma_index = 0; gamma_index < HBM_GAMMA_SET_CNT; gamma_index++) { + pSmart->hbm_interpolation_table[loop].gamma_setting[gamma_index] = + hbm_interpolation_gamma[loop][gamma_index]; + } + for (loop2 = 0; loop2 < 3; loop2++) { + pSmart->hbm_interpolation_table[loop].gamma_setting[(loop2*11)+10] = 0x00; + } + } + +#ifdef SMART_DIMMING_DEBUG + print_hbm_lux_table(pSmart, "DEC"); + print_hbm_lux_table(pSmart, "HEX"); +#endif + return; +} + +char pBuffer[256]; + +static int smart_dimming_init(struct SMART_DIM *psmart) +{ + int lux_loop; + int id1, id2, id3; + int cnt; + id1 = (psmart->ldi_revision & 0x00FF0000) >> 16; + id2 = (psmart->ldi_revision & 0x0000FF00) >> 8; + id3 = psmart->ldi_revision & 0xFF; + + LCD_INFO("++\n"); + + mtp_sorting(psmart); + gamma_cell_determine(psmart->ldi_revision); +#ifdef SMART_DIMMING_DEBUG + print_RGB_offset(psmart); +#endif + v255_adjustment(psmart); + vt_adjustment(psmart); + v203_adjustment(psmart); + v151_adjustment(psmart); + v87_adjustment(psmart); + v51_adjustment(psmart); + v35_adjustment(psmart); + v23_adjustment(psmart); + v11_adjustment(psmart); + v3_adjustment(psmart); + + if (generate_gray_scale(psmart)) { + pr_info(KERN_ERR "lcd smart dimming fail generate_gray_scale\n"); + return -EINVAL; + } + + /*Generating lux_table*/ + for (lux_loop = 0; lux_loop < psmart->lux_table_max; lux_loop++) { + /* To set brightness value */ + psmart->brightness_level = psmart->plux_table[lux_loop]; + /* To make lux table index*/ + psmart->gen_table[lux_loop].lux = psmart->plux_table[lux_loop]; + + gamma_init_revA(psmart, + (char *)(&(psmart->gen_table[lux_loop].gamma_setting)), + GAMMA_SET_MAX); + } + + /* set 300CD max gamma table */ + memcpy(&(psmart->gen_table[lux_loop-1].gamma_setting), + max_lux_table, GAMMA_SET_MAX); + +#ifdef SMART_DIMMING_DEBUG + for (lux_loop = 0; lux_loop < psmart->lux_table_max; lux_loop++) { + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) + snprintf(pBuffer + strnlen(pBuffer, 256), 256, " %3d", + psmart->gen_table[lux_loop].gamma_setting[cnt]); + + pr_info("lux : %3d %s\n", psmart->plux_table[lux_loop], pBuffer); + memset(pBuffer, 0x00, 256); + } +#endif + gamma_command_sorting_post(psmart); + gamma_cell_determine_max(psmart); + + /* Set 360CD max gamma table */ + memcpy(&(psmart->gen_table[LUMINANCE_MAX-1].gamma_setting), + max_lux_table2, GAMMA_SET_MAX); + + memset(pBuffer, 0x00, 256); + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) + snprintf(pBuffer + strnlen(pBuffer, 256), 256, " %02x", max_lux_table2[cnt]); + LCD_INFO("MAX GAMMA : %s\n", pBuffer); + + hbm_interpolation_init(psmart); + +#ifdef SMART_DIMMING_DEBUG + for (lux_loop = 0; lux_loop < psmart->lux_table_max; lux_loop++) { + for (cnt = 0; cnt < GAMMA_SET_MAX; cnt++) + snprintf(pBuffer + strnlen(pBuffer, 256), 256, + " %02X", + psmart->gen_table[lux_loop].gamma_setting[cnt]); + + pr_info("lux : %3d %s\n", psmart->plux_table[lux_loop], pBuffer); + memset(pBuffer, 0x00, 256); + } +#endif + LCD_INFO(" done\n"); + return 0; +} + +static void wrap_generate_hbm_gamma(struct smartdim_conf * conf, int hbm_brightness_level, char *cmd_str) { + + struct SMART_DIM *smart = conf->psmart; + + if (!smart) { + pr_info("%s fail", __func__); + return ; + } + + smart->hbm_brightness_level = hbm_brightness_level - 6; + generate_hbm_gamma(conf->psmart, cmd_str, GAMMA_SET_MAX); +} + +/* ---------------------------------------------------------------------------- + * Wrapper functions for smart dimming + * ---------------------------------------------------------------------------- + */ +static void wrap_generate_gamma(struct smartdim_conf * conf, int cd, char *cmd_str) { + + struct SMART_DIM *smart = conf->psmart; + + if (!smart) { + pr_info("%s fail", __func__); + return ; + } + + smart->brightness_level = cd; + generate_gamma(conf->psmart, cmd_str, GAMMA_SET_MAX); +} + +static void wrap_smart_dimming_init(struct smartdim_conf * conf) { + + struct SMART_DIM *smart = conf->psmart; + + if (!smart) { + pr_info("%s fail", __func__); + return ; + } + + smart->plux_table = conf->lux_tab; + smart->lux_table_max = conf->lux_tabsize; + smart->ldi_revision = conf->man_id; + smart->hbm_payload = conf->hbm_payload; + + smart_dimming_init(smart); +} + +struct smartdim_conf *smart_get_conf_ANA38401_AMSA05RB01(void) { + + struct smartdim_conf * smartdim_conf; + struct SMART_DIM *smart; + + smartdim_conf = kzalloc(sizeof(struct smartdim_conf), GFP_KERNEL); + if (!smartdim_conf) { + pr_info("%s allocation fail", __func__); + goto out2; + } + + smart = kzalloc(sizeof(struct SMART_DIM), GFP_KERNEL); + if (!smart) { + pr_info("%s allocation fail", __func__); + goto out1; + } + + smartdim_conf->psmart = smart; + smartdim_conf->generate_gamma = wrap_generate_gamma; + smartdim_conf->generate_hbm_gamma = wrap_generate_hbm_gamma; + smartdim_conf->init = wrap_smart_dimming_init; + smartdim_conf->get_min_lux_table = NULL; + smartdim_conf->mtp_buffer = (char *)(&smart->MTP_ORIGN); + smartdim_conf->print_aid_log = print_aid_log; + + return smartdim_conf; + +out1: + kfree(smartdim_conf); +out2: + return NULL; +} + +/* ---------------------------------------------------------------------------- + * END - Wrapper + * ---------------------------------------------------------------------------- + */ diff --git a/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_smart_dimming_ANA38401_AMSA05RB01.h b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_smart_dimming_ANA38401_AMSA05RB01.h new file mode 100755 index 000000000000..3b1f7a40e3a1 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ANA38401_AMSA05RB01/ss_dsi_smart_dimming_ANA38401_AMSA05RB01.h @@ -0,0 +1,493 @@ +/* + * ================================================================= + * + * Filename: smart_mtp_s6e3.h + * + * Description: Smart dimming algorithm implementation + * + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * +*/ +#ifndef _SS_DSI_SMART_DIMMING_ANA38401_AMSA05RB01_H_ +#define _SS_DSI_SMART_DIMMING_ANA38401_AMSA05RB01_H_ + +#include "ss_dsi_panel_common.h" +#include "ss_dsi_smart_dimming_common.h" + +enum { + GAMMA_CURVE_1P9 = 0, + GAMMA_CURVE_2P15, + GAMMA_CURVE_2P2, +}; + +#define LUMINANCE_MAX 65 +#define GAMMA_SET_MAX 33 +#define BIT_SHIFT 22 +/* + it means BIT_SHIFT is 22. pow(2,BIT_SHIFT) is 4194304. + BIT_SHIFT is used for right bit shfit +*/ +#define BIT_SHFIT_MUL 4194304 + +#define GRAY_SCALE_MAX 256 + +/* 6.8 * 4194304 */ +#define ANA34801_VREG0_REF_L 28521267 +/* 1.5 * 4194304 */ +#define ANA34801_VREG0_REF_H 6291456 + + +/*VT V0,V3,V11,V23,V35,V51,V87,V151,V203,V255*/ +#define ARRAY_MAX 11 + +/* PANEL DEPENDENT THINGS */ +#define MAX_CANDELA 360 +#define MIN_CANDELA 2 + +/* +* ID 0x20 +*/ +#define V255_300CD_R_MSB_20 0x01 +#define V255_300CD_R_LSB_20 0x00 + +#define V255_300CD_G_MSB_20 0x01 +#define V255_300CD_G_LSB_20 0x00 + +#define V255_300CD_B_MSB_20 0x01 +#define V255_300CD_B_LSB_20 0x00 + +#define V203_300CD_R_20 0x80 +#define V203_300CD_G_20 0x80 +#define V203_300CD_B_20 0x80 + +#define V151_300CD_R_20 0x80 +#define V151_300CD_G_20 0x80 +#define V151_300CD_B_20 0x80 + +#define V87_300CD_R_20 0x80 +#define V87_300CD_G_20 0x80 +#define V87_300CD_B_20 0x80 + +#define V51_300CD_R_20 0x80 +#define V51_300CD_G_20 0x80 +#define V51_300CD_B_20 0x80 + +#define V35_300CD_R_20 0x80 +#define V35_300CD_G_20 0x80 +#define V35_300CD_B_20 0x80 + +#define V23_300CD_R_20 0x80 +#define V23_300CD_G_20 0x80 +#define V23_300CD_B_20 0x80 + +#define V11_300CD_R_20 0x80 +#define V11_300CD_G_20 0x80 +#define V11_300CD_B_20 0x80 + +#define V3_300CD_R_20 0x80 +#define V3_300CD_G_20 0x80 +#define V3_300CD_B_20 0x80 + +#define VT_300CD_R_20 0x00 +#define VT_300CD_G_20 0x00 +#define VT_300CD_B_20 0x00 + +#define HBM_INTERPOLATION_STEP 8 + +enum { + V1_INDEX = 0, + V3_INDEX = 1, + V11_INDEX = 2, + V23_INDEX = 3, + V35_INDEX = 4, + V51_INDEX = 5, + V87_INDEX = 6, + V151_INDEX = 7, + V203_INDEX = 8, + V255_INDEX = 9, +}; + +struct GAMMA_LEVEL { + int level_0; + int level_1; + int level_3; + int level_11; + int level_23; + int level_35; + int level_51; + int level_87; + int level_151; + int level_203; + int level_255; +} __packed; + +struct RGB_OUTPUT_VOLTARE { + struct GAMMA_LEVEL R_VOLTAGE; + struct GAMMA_LEVEL G_VOLTAGE; + struct GAMMA_LEVEL B_VOLTAGE; +} __packed; + +struct GRAY_VOLTAGE { + /* + This voltage value use 14bit right shit + it means voltage is divied by 16384. + */ + int R_Gray; + int G_Gray; + int B_Gray; +} __packed; + +struct GRAY_SCALE { + struct GRAY_VOLTAGE TABLE[GRAY_SCALE_MAX]; + struct GRAY_VOLTAGE VT_TABLE; +} __packed; + +/*V0,V1,V3,V11,V23,V35,V51,V87,V151,V203,V255*/ + +struct MTP_SET { + char OFFSET_255_MSB; + char OFFSET_255_LSB; + char OFFSET_203; + char OFFSET_151; + char OFFSET_87; + char OFFSET_51; + char OFFSET_35; + char OFFSET_23; + char OFFSET_11; + char OFFSET_3; + char OFFSET_1; +} __packed; + +struct MTP_OFFSET { + struct MTP_SET R_OFFSET; + struct MTP_SET G_OFFSET; + struct MTP_SET B_OFFSET; +} __packed; + +struct illuminance_table { + int lux; + unsigned char gamma_setting[GAMMA_SET_MAX]; +} __packed; + +struct SMART_DIM { + struct MTP_OFFSET MTP_ORIGN; + struct MTP_OFFSET MTP; + struct RGB_OUTPUT_VOLTARE RGB_OUTPUT; + struct GRAY_SCALE GRAY; + + /* Because of AID funtion, below members are added*/ + int lux_table_max; + int *plux_table; + struct illuminance_table gen_table[LUMINANCE_MAX]; + + int brightness_level; + int ldi_revision; + /* HBM interpolation */ + struct illuminance_table hbm_interpolation_table[HBM_INTERPOLATION_STEP]; + char *hbm_payload; + int hbm_brightness_level; +} __packed; + +static int hbm_interpolation_candela_table[HBM_INTERPOLATION_STEP] = {378, 395, 413, 430, 448, 465, 483, 500}; + +#define CANDELA_MAX_TABLE 64 +#define RGB_COMPENSATION 24 + +static int candela_table[][2] = { + {2, 0}, + {3, 1}, + {4, 2}, + {5, 3}, + {6, 4}, + {7, 5}, + {8, 6}, + {9, 7}, + {10, 8}, + {11, 9}, + {12, 10}, + {13, 11}, + {14, 12}, + {15, 13}, + {16, 14}, + {17, 15}, + {19, 16}, + {20, 17}, + {21, 18}, + {22, 19}, + {24, 20}, + {25, 21}, + {27, 22}, + {29, 23}, + {30, 24}, + {32, 25}, + {34, 26}, + {37, 27}, + {39, 28}, + {41, 29}, + {44, 30}, + {47, 31}, + {50, 32}, + {53, 33}, + {56, 34}, + {60, 35}, + {64, 36}, + {68, 37}, + {72, 38}, + {77, 39}, + {82, 40}, + {87, 41}, + {93, 42}, + {98, 43}, + {105, 44}, + {111, 45}, + {119, 46}, + {126, 47}, + {134, 48}, + {143, 49}, + {152, 50}, + {162, 51}, + {172, 52}, + {183, 53}, + {195, 54}, + {207, 55}, + {220, 56}, + {234, 57}, + {249, 58}, + {265, 59}, + {282, 60}, + {300, 61}, + {316, 62}, + {333, 63}, + {360, 64}, +}; + +static int base_luminance_revA[][2] = { + {2, 112}, + {3, 112}, + {4, 112}, + {5, 112}, + {6, 112}, + {7, 112}, + {8, 112}, + {9, 112}, + {10, 112}, + {11, 112}, + {12, 112}, + {13, 112}, + {14, 112}, + {15, 112}, + {16, 112}, + {17, 112}, + {19, 112}, + {20, 112}, + {21, 112}, + {22, 112}, + {24, 112}, + {25, 112}, + {27, 112}, + {29, 112}, + {30, 112}, + {32, 112}, + {34, 112}, + {37, 112}, + {39, 112}, + {41, 112}, + {44, 112}, + {47, 112}, + {50, 112}, + {53, 112}, + {56, 112}, + {60, 112}, + {64, 112}, + {68, 112}, + {72, 116}, + {77, 121}, + {82, 131}, + {87, 140}, + {93, 150}, + {98, 158}, + {105, 170}, + {111, 178}, + {119, 191}, + {126, 200}, + {134, 214}, + {143, 227}, + {152, 238}, + {162, 254}, + {172, 254}, + {183, 254}, + {195, 254}, + {207, 254}, + {220, 254}, + {234, 254}, + {249, 254}, + {265, 272}, + {282, 289}, + {300, 306}, + {316, 320}, + {333, 335}, + {360, 360}, +}; + +static int gradation_offset_revA[][9] = { +/* V255 V203 V151 V87 V51 V35 V23 V11 V3 */ + {0, 6, 9, 17, 20, 24, 28, 31, 31}, + {0, 5, 6, 11, 14, 17, 21, 25, 25}, + {0, 5, 6, 10, 13, 16, 19, 23, 23}, + {0, 4, 4, 9, 12, 13, 16, 20, 21}, + {0, 4, 4, 7, 9, 12, 15, 19, 19}, + {0, 4, 3, 6, 8, 9, 13, 16, 17}, + {0, 3, 2, 5, 7, 9, 13, 15, 16}, + {0, 3, 2, 5, 7, 9, 13, 15, 16}, + {0, 3, 2, 4, 6, 7, 11, 13, 14}, + {0, 3, 2, 4, 6, 7, 11, 13, 14}, + {0, 3, 2, 4, 5, 7, 10, 13, 13}, + {0, 3, 2, 4, 5, 7, 10, 12, 13}, + {0, 3, 2, 3, 4, 5, 8, 11, 10}, + {0, 3, 2, 3, 4, 5, 8, 11, 10}, + {0, 3, 2, 3, 4, 5, 8, 11, 10}, + {0, 3, 2, 3, 3, 4, 7, 10, 9}, + {0, 3, 2, 3, 3, 4, 7, 10, 9}, + {0, 3, 2, 3, 3, 4, 7, 9, 8}, + {0, 3, 2, 2, 2, 3, 6, 9, 8}, + {0, 3, 2, 2, 2, 3, 6, 9, 8}, + {0, 3, 2, 2, 2, 3, 6, 9, 8}, + {0, 3, 2, 2, 2, 3, 6, 9, 8}, + {0, 3, 2, 2, 2, 3, 6, 8, 8}, + {0, 3, 2, 2, 2, 3, 6, 8, 8}, + {0, 3, 2, 2, 2, 3, 5, 7, 7}, + {0, 3, 2, 2, 2, 2, 5, 7, 7}, + {0, 2, 2, 2, 2, 2, 4, 6, 7}, + {0, 2, 2, 2, 1, 1, 4, 6, 6}, + {0, 2, 2, 2, 1, 1, 4, 6, 6}, + {0, 2, 2, 2, 1, 1, 3, 5, 6}, + {0, 2, 2, 2, 1, 1, 3, 5, 6}, + {0, 2, 2, 2, 1, 1, 3, 4, 5}, + {0, 2, 2, 2, 1, 1, 3, 4, 5}, + {0, 2, 2, 2, 1, 1, 3, 4, 5}, + {0, 2, 2, 2, 1, 1, 3, 4, 5}, + {0, 2, 2, 2, 1, 1, 3, 4, 5}, + {0, 2, 1, 1, 1, 1, 3, 4, 5}, + {0, 2, 1, 1, 1, 1, 3, 4, 5}, + {0, 2, 2, 2, 1, 1, 2, 4, 5}, + {0, 2, 2, 2, 1, 1, 2, 4, 6}, + {0, 2, 2, 1, 1, 1, 3, 4, 6}, + {0, 1, 2, 1, 1, 1, 2, 3, 6}, + {0, 1, 2, 1, 0, 1, 2, 4, 6}, + {0, 1, 2, 2, 1, 1, 2, 4, 6}, + {0, 0, 2, 2, 1, 2, 3, 4, 6}, + {0, 0, 2, 1, 1, 1, 2, 3, 6}, + {0, 0, 2, 1, 1, 2, 2, 3, 6}, + {0, 1, 3, 2, 1, 1, 2, 3, 5}, + {0, 1, 3, 2, 1, 1, 2, 3, 4}, + {0, 2, 3, 1, 1, 1, 1, 2, 4}, + {0, 1, 3, 2, 0, 0, 1, 2, 4}, + {0, 1, 3, 1, 0, 0, 0, 2, 4}, + {0, 1, 3, 1, 0, 0, 0, 1, 3}, + {0, 1, 3, 1, 0, 0, 0, 1, 3}, + {0, 1, 2, 1, 0, 0, 0, 1, 2}, + {0, 1, 2, 1, 0, 0, 0, 1, 2}, + {0, 1, 2, 1, 0, 0, 0, 1, 2}, + {0, 1, 2, 1, 0, 0, 0, 1, 2}, + {0, 1, 2, 1, 1, 0, 0, 1, 1}, + {0, -1, 0, 0, -1, 0, 0, 1, 1}, + {0, 0, 1, 0, 0, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 0, -1, 0, 1}, + {0, 1, 0, -1, 0, -1, 0, 0, 0}, + {0, 0, 0, -1, 0, 0, -1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +static int rgb_offset_revA[][RGB_COMPENSATION] = { +/* R255 G255 B255 R203 G203 B203 R151 G151 B151 + R87 G87 B87 R51 G51 B51 R35 G35 B35 + R23 G23 B23 R11 G11 B11 +*/ + {-7, 1, -1, -3, 1, -1, -6, 1, -1, -18, 3, -6, -20, 8, -1, -8, 3, -5, -3, 1, -5, -9, 1, -10}, + {-4, 0, -1, -4, 0, -1, -4, 2, -1, -14, 4, -3, -19, 6, -3, -12, 1, -6, -4, 0, -6, -9, 1, -10}, + {-4, 0, -1, -3, 0, -1, -4, 1, -1, -12, 4, -3, -19, 3, -5, -15, 1, -6, -4, 3, -4, -9, 1, -10}, + {-4, 0, -1, -1, 0, 0, -3, 1, -1, -12, 2, -4, -19, 3, -7, -12, 3, -5, -6, 4, -5, -7, 4, -8}, + {-3, 0, -1, -2, 0, 0, -2, 1, -1, -9, 3, -2, -20, 0, -10, -15, 1, -6, -6, 3, -5, -7, 2, -6}, + {-3, 0, -1, -1, 0, 0, -1, 2, 0, -8, 3, -2, -15, 5, -5, -12, 2, -4, -6, 2, -4, -6, 0, -7}, + {-3, 0, -1, -1, 0, 0, 0, 2, 0, -7, 3, -1, -15, 2, -6, -15, 1, -6, -10, 1, -6, -6, 4, -7}, + {-3, 0, -1, -1, 0, 0, 0, 2, 0, -7, 2, -2, -14, 2, -5, -14, 1, -5, -12, 1, -8, -6, 4, -7}, + {-2, 0, -1, -1, 0, 0, -1, 2, 0, -4, 3, 0, -14, 2, -5, -15, 3, -4, -8, 3, -3, -4, 2, -6}, + {-2, 0, -1, -1, 0, 0, 0, 2, 0, -5, 2, -1, -14, 2, -5, -15, 2, -5, -8, 2, -4, -2, 3, -5}, + {-2, 0, -1, -1, 0, 0, 0, 2, 0, -5, 2, -1, -12, 3, -4, -15, 1, -5, -7, 4, -4, -4, 3, -3}, + {-2, 0, -1, 0, 0, 1, -1, 1, -1, -5, 2, -1, -11, 3, -4, -14, 1, -5, -8, 2, -5, 0, 5, -3}, + {-2, 0, -1, 0, 0, 1, -1, 1, -1, -3, 3, 0, -10, 3, -4, -8, 4, -2, -10, 3, -5, -4, 1, -6}, + {-2, 0, -1, 0, 0, 1, -1, 1, -1, -3, 2, 0, -11, 3, -5, -7, 4, -2, -9, 3, -4, -4, 1, -6}, + {-2, 0, -1, 0, 0, 1, -1, 1, -1, -4, 2, 0, -10, 2, -6, -7, 4, -2, -9, 3, -4, -5, 1, -7}, + {-2, 0, -1, 0, 0, 1, 0, 1, -1, -3, 2, 0, -9, 4, -3, -9, 4, -3, -8, 3, -4, -4, 1, -7}, + {-2, 0, -1, 0, 0, 1, 0, 1, -1, -3, 1, 0, -9, 4, -3, -10, 3, -5, -8, 2, -4, -8, 0, -9}, + {-2, 0, -1, 0, 0, 1, 0, 1, -1, -3, 1, 0, -8, 3, -3, -11, 2, -6, -8, 2, -4, -4, 5, -6}, + {-2, 0, -1, 1, 0, 1, -1, 1, -1, -2, 1, 0, -7, 5, -1, -7, 3, -3, -7, 3, -3, -5, 2, -7}, + {-2, 0, -1, 1, 0, 1, -1, 1, -1, -2, 1, 0, -6, 5, -1, -8, 2, -4, -7, 3, -3, -7, 1, -10}, + {-2, 0, -1, 1, 0, 1, -1, 1, -1, -2, 1, 0, -7, 3, -2, -8, 2, -4, -8, 2, -4, -7, 0, -9}, + {-2, 0, -1, 1, 0, 1, -1, 1, -1, -2, 1, 0, -6, 3, -2, -8, 1, -4, -11, 1, -6, -7, 0, -9}, + {-1, 0, -1, 0, 0, 1, -1, 1, -1, -1, 1, 1, -6, 3, -2, -10, 0, -6, -9, 1, -5, -5, 6, -7}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -2, 1, 1, -6, 2, -3, -9, 0, -5, -11, 0, -7, -7, 6, -8}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -1, 1, 1, -7, 2, -3, -9, 0, -5, -4, 5, -1, -7, 6, -8}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -1, 1, 1, -5, 2, -2, -7, 4, -3, -6, 1, -4, -7, 4, -9}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -2, 0, 0, -5, 2, -2, -2, 4, -2, -6, 4, -1, -7, 4, -9}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -2, 0, 0, -2, 5, 0, -3, 4, 0, -4, 1, -3, -13, 1, -12}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -1, 0, 0, -2, 4, 0, -4, 4, -1, -5, 0, -4, -12, 0, -12}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -1, 0, 0, -2, 4, 0, -4, 3, -1, -1, 4, 0, -10, 1, -12}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, -1, 0, 0, -2, 4, 0, -4, 2, -2, -2, 3, -1, -11, 1, -12}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, 0, 0, 1, -3, 3, -2, -4, 2, -1, -2, 3, -1, -8, 5, -8}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, 0, 0, 1, -3, 2, -2, -4, 2, -2, -3, 3, -2, -8, 5, -8}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, 0, 0, 1, -2, 3, -1, -4, 1, -2, -1, 3, -1, -9, 3, -9}, + {-1, 0, -1, 0, 0, 1, -1, 0, -2, 0, 0, 1, -1, 3, -1, -5, 1, -2, -1, 2, -1, -9, 4, -10}, + {-1, 0, -1, 0, 0, 1, 0, 0, -1, -1, 0, 0, -2, 2, -2, -4, 0, -2, -3, 2, -2, -9, 3, -9}, + {-1, 0, -1, 0, 0, 1, 0, 0, -1, 0, 1, 1, -3, 1, -1, -4, 0, -3, -3, 1, -3, -9, 2, -10}, + {-1, 0, -1, 1, 1, 1, 0, 0, 0, 0, 0, 1, -4, 0, -3, -3, 0, -2, -5, 0, -4, -10, 2, -10}, + {-1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 1, -3, 0, -2, -1, 2, -1, -5, 0, -4, -13, 1, -14}, + {0, 1, 0, 0, 0, 1, -1, 0, -2, -1, -1, 0, -2, 1, -1, 0, 3, 0, -2, 1, -2, -14, 0, -13}, + {0, 0, 0, 0, 0, -1, 0, 0, -1, 1, 1, 2, -3, 0, -3, -2, 2, -1, -7, -2, -6, -7, 5, -9}, + {0, 0, -1, 0, 0, 0, 1, 1, 2, -1, 0, 0, -2, 0, -2, 0, 2, 0, -4, 0, -4, -9, 6, -8}, + {-1, 0, 0, 0, 0, 0, -1, 0, -1, 1, 1, 1, -2, 0, -1, -4, 0, -3, -2, 2, -2, -14, 2, -12}, + {0, 1, 0, -1, 0, -1, 1, 1, 0, -1, -1, 0, -2, 0, -2, -2, 1, -2, -4, 0, -3, -12, 3, -11}, + {-1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, -3, -1, -2, -3, 0, -3, -5, -1, -4, -9, 7, -5}, + {-1, 0, -1, 1, 0, 1, 0, 1, 0, 0, 0, 1, -2, 0, -2, -3, 0, -3, -4, 0, -3, -10, 5, -8}, + {0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, -2, -1, -2, -2, 0, -2, -3, 1, -2, -9, 6, -7}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, -2, 0, -2, -3, 0, -3, -9, 5, -7}, + {0, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, 0, -2, 0, -1, -2, 0, -2, -3, 0, -3, -7, 6, -5}, + {0, 0, 0, -1, -1, 0, -1, 0, -1, 1, 1, 2, -2, -1, -1, -2, -1, -3, -1, 3, 0, -6, 5, -4}, + {0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 1, 1, 2, 2, 1, 1, -1, -3, 0, -2, -9, 3, -6}, + {-1, 0, -1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 2, 2, 0, 2, 0, 0, 2, 0, -10, 2, -6}, + {0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 1, 1, 0, 1, 1, -1, 2, -1, -1, 1, -1, -3, 7, 0}, + {0, 0, 0, 0, 0, -1, -1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 2, 1, 0, 1, -1, -7, 4, -4}, + {0, 0, 0, 0, 0, -1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 1, 2, 0, -1, 1, -1, -6, 4, -2}, + {0, 0, 0, 0, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, -1, 0, 1, 0, -7, 2, -4}, + {0, 0, 0, 0, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, -1, 0, -2, 0, 1, 0, -7, 1, -3}, + {-1, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, -1, -1, 0, -1, -7, 0, -3}, + {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, -1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, -1}, + {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, -1, 0, -1}, + {0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, -1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, -1, 0, -1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; +#endif diff --git a/drivers/gpu/drm/msm/samsung/Kconfig b/drivers/gpu/drm/msm/samsung/Kconfig new file mode 100755 index 000000000000..efb1cb57826f --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/Kconfig @@ -0,0 +1,63 @@ +config SAMSUNG_DEBUG_DISPLAY + bool "Add Samsung Debugging code" + default n + +config DISPLAY_SAMSUNG + depends on DRM_MSM + bool "mdss config for samsung product" + default n + ---help--- + Mdss config signature for samsung product + +# +# PANEL UNIQUE FEATURE +# +config DUAL_PANEL + depends on DISPLAY_SAMSUNG + bool "dual paneloperation" + default n + ---help--- + dual panel operation support + +config PARTIAL_UPDATE + depends on DISPLAY_SAMSUNG + bool "partial update operation" + default n + ---help--- + partial update operation support + +config CABC_FUNCTION + depends on DISPLAY_SAMSUNG + bool "DDI cabc operation" + default n + ---help--- + DDI cabc operation support + +config SUPPORT_POC_FLASH + depends on DISPLAY_SAMSUNG + bool "SUPPORT POC FLASH" + default n + ---help--- + SUPPORT_POC_FLASH FOR DREAM2 ONLY + +# +# HOW TO MAKE DEF-CONFIG NAME FOR PANEL +# +# PANEL_DDI-model_PANEL-model_RESOLUTION +# PANEL_S6E3FA2 _AMS510CV01_FHD +# PANEL_S6E3FA2_AMS510CV01_FHD +# + +config PANEL_ANA38401_AMSA05RB01_WQXGA + depends on DISPLAY_SAMSUNG + bool "LSI ANA38401 LDI" + default n + ---help--- + LSI ANA38401 LDI 2560_1600 TAB S4 LV + +config PLL_SSC_DISABLED + depends on DISPLAY_SAMSUNG + bool "PLL_SSC_DISABLED" + default n + ---help--- + PLL_SSC_DISABLED diff --git a/drivers/gpu/drm/msm/samsung/Makefile b/drivers/gpu/drm/msm/samsung/Makefile new file mode 100755 index 000000000000..c01c67554490 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/Makefile @@ -0,0 +1,35 @@ +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -Idrivers/gpu/drm/msm/dsi-staging +ccflags-y += -Idrivers/gpu/drm/msm/sde +ccflags-y += -Idrivers/gpu/drm/msm/samsung + +obj-y += ss_dsi_panel_sysfs.o +obj-y += ss_dsi_panel_debug.o +obj-y += ss_dsi_panel_common.o +obj-y += ss_dsi_mdnie_lite_common.o +obj-y += ss_dsi_smart_dimming_common.o +obj-y += ss_interpolation_common.o +obj-y += ss_flash_table_data_common.o +obj-y += ss_dpui_common.o + +# obj-$(CONFIG_SAMSUNG_DEBUG_DISPLAY) += dlog.o +obj-$(CONFIG_SUPPORT_POC_FLASH) += ss_ddi_poc_common.o + +# TO SUPPORT SPI INTERFACE +obj-y += ss_ddi_spi_common.o + +# COPR +obj-y += ss_copr_common.o + +# Self Display +obj-y += SELF_DISPLAY/ + +# +# panel directory make file +# + +# TO SUPPORT PBA BOOINT +obj-y += PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.o +obj-y += PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.o + +# TAB S4 LV Panel +obj-$(CONFIG_PANEL_ANA38401_AMSA05RB01_WQXGA) += ANA38401_AMSA05RB01/ diff --git a/drivers/gpu/drm/msm/samsung/PBA_BOOTING/dsi_panel_PBA_BOOTING_fhd_video.dtsi b/drivers/gpu/drm/msm/samsung/PBA_BOOTING/dsi_panel_PBA_BOOTING_fhd_video.dtsi new file mode 100644 index 000000000000..0245dfd09ee0 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/PBA_BOOTING/dsi_panel_PBA_BOOTING_fhd_video.dtsi @@ -0,0 +1,235 @@ +/* Copyright (c) 2013-2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *--------------------------------------------------------------------------- + + */ +&mdss_mdp { + ss_dsi_panel_PBA_BOOTING_FHD: ss_dsi_panel_PBA_BOOTING_FHD { + qcom,mdss-dsi-panel-name = "ss_dsi_panel_PBA_BOOTING_FHD"; + label = "ss_dsi_panel_PBA_BOOTING_FHD"; + + /* SDM845 drm nodes */ + qcom,display-topology = <1 1 1>, <2 2 1>; + qcom,default-topology-index = <0>; + + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-pulse-width = <12>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-front-porch = <144>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-pulse-width = <4>; + qcom,mdss-dsi-v-back-porch = <3>; + qcom,mdss-dsi-v-front-porch = <9>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xFF>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + /*qcom,cmd-sync-wait-broadcast = */ + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 78 00 02 11 00 /* sleep out. It use last packet*/ + ]; + qcom,mdss-dsi-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 24 00 02 28 00 /* wait 40ms(0x24) */ + 05 01 00 00 78 00 02 10 00 /* wait 120ms(0x78) */ + ]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + /*qcom,dynamic-mode-switch-enabled = */ + /*qcom,dynamic-mode-switch-type = */ + /*qcom,video-to-cmd-mode-switch-commands = */ + /*qcom,cmd-to-video-mode-switch-commands = */ + + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-destination = "display_1"; + + /*qcom,cmd-sync-wait-trigger = */ + /*qcom,mdss-dsi-fbc-enable = */ + /*qcom,mdss-dsi-fbc-slice-height = */ + /*qcom,mdss-dsi-fbc-2d-pred-mode = */ + /*qcom,mdss-dsi-fbc-ver2-mode = */ + /*qcom,mdss-dsi-fbc-bpp = */ + /*qcom,mdss-dsi-fbc-packing = */ + /*qcom,mdss-dsi-fbc-quant-error = */ + /*qcom,mdss-dsi-fbc-bias = */ + /*qcom,mdss-dsi-fbc-pat-mode = */ + /*qcom,mdss-dsi-fbc-vlc-mode = */ + /*qcom,mdss-dsi-fbc-bflc-mode = */ + /*qcom,mdss-dsi-fbc-h-line-budget = */ + /*qcom,mdss-dsi-fbc-budget-ctrl = */ + /*qcom,mdss-dsi-fbc-block-budget = */ + /*qcom,mdss-dsi-fbc-lossless-threshold = */ + /*qcom,mdss-dsi-fbc-lossy-threshold = */ + /*qcom,mdss-dsi-fbc-rgb-threshold = */ + /*qcom,mdss-dsi-fbc-lossy-mode-idx = */ + /*qcom,mdss-dsi-fbc-max-pred-err = */ + /*qcom,mdss-dsi-pan-enable-dynamic-fps = */ + /*qcom,mdss-dsi-pan-fps-update = */ + /*qcom,min-refresh-rate = */ + /*qcom,max-refresh-rate = */ + /*qcom,mdss-dsi-bl-pwm-pmi = */ + /*qcom,mdss-dsi-bl-pmic-bank-select = */ + /*qcom,mdss-dsi-bl-pmic-pwm-frequency = */ + /*qcom,mdss-dsi-pwm-gpio = */ + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-brightness-max-level = <255>; + qcom,mdss-dsi-interleave-mode = <0>; + /*qcom,5v-boost-gpio = */ + /*qcom,mdss-dsi-te-check-enable = */ + /*qcom,mdss-dsi-te-using-te-pin = */ + /*qcom,mdss-dsi-te-pin-select = */ + /*qcom,mdss-dsi-te-dcs-command = */ + /*qcom,mdss-dsi-wr-mem-start = */ + /*qcom,mdss-dsi-wr-mem-continue = */ + /*qcom,mdss-dsi-hfp-power-mode = */ + /*qcom,mdss-dsi-hbp-power-mode = */ + /*qcom,mdss-dsi-hsa-power-mode = */ + /*qcom,mdss-dsi-last-line-interleave = */ + qcom,mdss-dsi-pixel-packing = "loose"; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + + /*qcom,mdss-dsi-panel-clockrate = <1020250000>;*/ + qcom,mdss-pan-physical-width-dimension = <60>; + qcom,mdss-pan-physical-height-dimension = <106>; + qcom,mdss-dsi-panel-mode-gpio-state = "invalid"; + /*qcom,mdss-tear-check-disable; */ + /*qcom,mdss-tear-check-sync-cfg-height = */ + /*qcom,mdss-tear-check-sync-init-val = */ + /*qcom,mdss-tear-check-sync-threshold-start = */ + /*qcom,mdss-tear-check-sync-threshold-continue = */ + /*qcom,mdss-tear-check-start-pos = */ + /*qcom,mdss-tear-check-rd-ptr-trigger-intr = */ + /*qcom,mdss-tear-check-frame-rate = */ + /*qcom,partial-update-enabled = */ + /*qcom,mdss-dsi-horizontal-line-idle = */ + /*qcom,partial-update-roi-merge = */ + /*qcom,dcs-cmd-by-left = */ + /*qcom,mdss-dsi-lp11-init;*/ + /*qcom,mdss-dsi-init-delay-us = <1000>;*/ + qcom,mdss-dsi-rx-eot-ignore; + qcom,mdss-dsi-tx-eot-append; + /*qcom,ulps-enabled = */ + /*qcom,suspend-ulps-enabled = */ + /*qcom,panel-roi-alignment = */ + /*qcom,esd-check-enabled = */ + /*qcom,mdss-dsi-panel-status-command = */ + /*qcom,mdss-dsi-panel-status-command-mode = */ + /*qcom,mdss-dsi-panel-status-check-mode = */ + /*qcom,mdss-dsi-panel-status-read-length = */ + /*qcom,mdss-dsi-panel-status-value = */ + /*qcom,mdss-dsi-panel-max-error-count = */ + /*qcom,mdss-dsi-panel-orientation = */ + /*qcom,panel-ack-disabled = */ + qcom,mdss-dsi-force-clock-lane-hs; + + + /* + * ************************************************************************************************************************ + * candela to index mappings + * ************************************************************************************************************************ + */ + samsung,candela_map_table_revA = < + /* */ + 0 0 2 5 + 1 2 2 6 + 2 3 3 7 + 3 4 4 8 + 4 5 5 9 + 5 6 6 10 + 6 7 7 11 + 7 8 8 12 + 8 9 9 13 + 9 10 10 14 + 10 11 11 15 + 11 12 12 16 + 12 13 13 17 + 13 14 14 19 + 14 15 15 20 + 15 16 16 21 + 16 17 17 22 + 17 18 18 24 + 18 19 19 25 + 19 20 20 27 + 20 21 21 29 + 21 22 22 30 + 22 23 24 32 + 23 25 26 34 + 24 27 28 37 + 25 29 29 39 + 26 30 32 41 + 27 33 34 44 + 28 35 36 47 + 29 37 38 50 + 30 39 40 53 + 31 41 43 56 + 32 44 46 60 + 33 47 49 64 + 34 50 52 68 + 35 53 56 72 + 36 57 59 77 + 37 60 63 82 + 38 64 67 87 + 39 68 71 93 + 40 72 76 98 + 41 77 80 105 + 42 81 86 111 + 43 87 91 119 + 44 92 97 126 + 45 98 104 134 + 46 105 110 143 + 47 111 118 152 + 48 119 125 162 + 49 126 133 172 + 50 134 142 183 + 51 143 150 195 + 52 151 160 207 + 53 161 170 220 + 54 171 181 234 + 55 182 193 249 + 56 194 205 265 + 57 206 218 282 + 58 219 230 300 + 59 231 242 316 + 60 243 254 333 + 61 255 255 360 + >; + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.c b/drivers/gpu/drm/msm/samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.c new file mode 100644 index 000000000000..333e410e704c --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.c @@ -0,0 +1,77 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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" + +static void samsung_pba_config(struct samsung_display_driver_data *vdd, void *arg) +{ + struct sde_mdss_cfg *sde_cfg = (struct sde_mdss_cfg *)arg; + + if (!IS_ERR_OR_NULL(sde_cfg)) { + LCD_INFO("Disable source split\n"); + sde_cfg->has_src_split = false; + } +} + +static void samsung_panel_init(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("%s\n", ss_get_panel_name(vdd)); + + vdd->mdnie.support_mdnie = false; + vdd->dtsi_data.tft_common_support = true; + vdd->panel_func.samsung_pba_config = samsung_pba_config; +} + +static int __init samsung_panel_initialize(void) +{ + struct samsung_display_driver_data *vdd; + enum ss_display_ndx ndx; + char panel_string[] = "ss_dsi_panel_PBA_BOOTING_FHD"; + char panel_name[MAX_CMDLINE_PARAM_LEN]; + char panel_secondary_name[MAX_CMDLINE_PARAM_LEN]; + + ss_get_primary_panel_name_cmdline(panel_name); + ss_get_secondary_panel_name_cmdline(panel_secondary_name); + + /* TODO: use component_bind with panel_func + * and match by panel_string, instead. + */ + if (!strncmp(panel_string, panel_name, strlen(panel_string))) + ndx = PRIMARY_DISPLAY_NDX; + else if (!strncmp(panel_string, panel_secondary_name, + strlen(panel_string))) + ndx = SECONDARY_DISPLAY_NDX; + else + return 0; + + vdd = ss_get_vdd(ndx); + vdd->panel_func.samsung_panel_init = samsung_panel_init; + + return 0; + + +} +early_initcall(samsung_panel_initialize); diff --git a/drivers/gpu/drm/msm/samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.h b/drivers/gpu/drm/msm/samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.h new file mode 100644 index 000000000000..9c0494668edf --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/PBA_BOOTING/ss_dsi_panel_PBA_BOOTING_fhd.h @@ -0,0 +1,33 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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" + +struct smartdim_conf *smart_get_conf_EA8061V_AMS391DT01(void); +#endif diff --git a/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/dsi_panel_PBA_BOOTING_fhd_video_dsi1.dtsi b/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/dsi_panel_PBA_BOOTING_fhd_video_dsi1.dtsi new file mode 100755 index 000000000000..cb849db21092 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/dsi_panel_PBA_BOOTING_fhd_video_dsi1.dtsi @@ -0,0 +1,235 @@ +/* Copyright (c) 2013-2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *--------------------------------------------------------------------------- + + */ +&mdss_mdp { + ss_dsi_panel_PBA_BOOTING_FHD_DSI1: ss_dsi_panel_PBA_BOOTING_FHD_DSI1 { + qcom,mdss-dsi-panel-name = "ss_dsi_panel_PBA_BOOTING_FHD_DSI1"; + label = "ss_dsi_panel_PBA_BOOTING_FHD_DSI1"; + + /* SDM845 drm nodes */ + qcom,display-topology = <1 1 1>, <2 2 1>; + qcom,default-topology-index = <0>; + + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-pulse-width = <12>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-front-porch = <144>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-pulse-width = <4>; + qcom,mdss-dsi-v-back-porch = <3>; + qcom,mdss-dsi-v-front-porch = <9>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xFF>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + /*qcom,cmd-sync-wait-broadcast = */ + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 78 00 02 11 00 /* sleep out. It use last packet*/ + ]; + qcom,mdss-dsi-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 24 00 02 28 00 /* wait 40ms(0x24) */ + 05 01 00 00 78 00 02 10 00 /* wait 120ms(0x78) */ + ]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + /*qcom,dynamic-mode-switch-enabled = */ + /*qcom,dynamic-mode-switch-type = */ + /*qcom,video-to-cmd-mode-switch-commands = */ + /*qcom,cmd-to-video-mode-switch-commands = */ + + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-destination = "display_1"; + + /*qcom,cmd-sync-wait-trigger = */ + /*qcom,mdss-dsi-fbc-enable = */ + /*qcom,mdss-dsi-fbc-slice-height = */ + /*qcom,mdss-dsi-fbc-2d-pred-mode = */ + /*qcom,mdss-dsi-fbc-ver2-mode = */ + /*qcom,mdss-dsi-fbc-bpp = */ + /*qcom,mdss-dsi-fbc-packing = */ + /*qcom,mdss-dsi-fbc-quant-error = */ + /*qcom,mdss-dsi-fbc-bias = */ + /*qcom,mdss-dsi-fbc-pat-mode = */ + /*qcom,mdss-dsi-fbc-vlc-mode = */ + /*qcom,mdss-dsi-fbc-bflc-mode = */ + /*qcom,mdss-dsi-fbc-h-line-budget = */ + /*qcom,mdss-dsi-fbc-budget-ctrl = */ + /*qcom,mdss-dsi-fbc-block-budget = */ + /*qcom,mdss-dsi-fbc-lossless-threshold = */ + /*qcom,mdss-dsi-fbc-lossy-threshold = */ + /*qcom,mdss-dsi-fbc-rgb-threshold = */ + /*qcom,mdss-dsi-fbc-lossy-mode-idx = */ + /*qcom,mdss-dsi-fbc-max-pred-err = */ + /*qcom,mdss-dsi-pan-enable-dynamic-fps = */ + /*qcom,mdss-dsi-pan-fps-update = */ + /*qcom,min-refresh-rate = */ + /*qcom,max-refresh-rate = */ + /*qcom,mdss-dsi-bl-pwm-pmi = */ + /*qcom,mdss-dsi-bl-pmic-bank-select = */ + /*qcom,mdss-dsi-bl-pmic-pwm-frequency = */ + /*qcom,mdss-dsi-pwm-gpio = */ + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-brightness-max-level = <255>; + qcom,mdss-dsi-interleave-mode = <0>; + /*qcom,5v-boost-gpio = */ + /*qcom,mdss-dsi-te-check-enable = */ + /*qcom,mdss-dsi-te-using-te-pin = */ + /*qcom,mdss-dsi-te-pin-select = */ + /*qcom,mdss-dsi-te-dcs-command = */ + /*qcom,mdss-dsi-wr-mem-start = */ + /*qcom,mdss-dsi-wr-mem-continue = */ + /*qcom,mdss-dsi-hfp-power-mode = */ + /*qcom,mdss-dsi-hbp-power-mode = */ + /*qcom,mdss-dsi-hsa-power-mode = */ + /*qcom,mdss-dsi-last-line-interleave = */ + qcom,mdss-dsi-pixel-packing = "loose"; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + + /*qcom,mdss-dsi-panel-clockrate = <1020250000>;*/ + qcom,mdss-pan-physical-width-dimension = <60>; + qcom,mdss-pan-physical-height-dimension = <106>; + qcom,mdss-dsi-panel-mode-gpio-state = "invalid"; + /*qcom,mdss-tear-check-disable; */ + /*qcom,mdss-tear-check-sync-cfg-height = */ + /*qcom,mdss-tear-check-sync-init-val = */ + /*qcom,mdss-tear-check-sync-threshold-start = */ + /*qcom,mdss-tear-check-sync-threshold-continue = */ + /*qcom,mdss-tear-check-start-pos = */ + /*qcom,mdss-tear-check-rd-ptr-trigger-intr = */ + /*qcom,mdss-tear-check-frame-rate = */ + /*qcom,partial-update-enabled = */ + /*qcom,mdss-dsi-horizontal-line-idle = */ + /*qcom,partial-update-roi-merge = */ + /*qcom,dcs-cmd-by-left = */ + /*qcom,mdss-dsi-lp11-init;*/ + /*qcom,mdss-dsi-init-delay-us = <1000>;*/ + qcom,mdss-dsi-rx-eot-ignore; + qcom,mdss-dsi-tx-eot-append; + /*qcom,ulps-enabled = */ + /*qcom,suspend-ulps-enabled = */ + /*qcom,panel-roi-alignment = */ + /*qcom,esd-check-enabled = */ + /*qcom,mdss-dsi-panel-status-command = */ + /*qcom,mdss-dsi-panel-status-command-mode = */ + /*qcom,mdss-dsi-panel-status-check-mode = */ + /*qcom,mdss-dsi-panel-status-read-length = */ + /*qcom,mdss-dsi-panel-status-value = */ + /*qcom,mdss-dsi-panel-max-error-count = */ + /*qcom,mdss-dsi-panel-orientation = */ + /*qcom,panel-ack-disabled = */ + qcom,mdss-dsi-force-clock-lane-hs; + + + /* + * ************************************************************************************************************************ + * candela to index mappings + * ************************************************************************************************************************ + */ + samsung,candela_map_table_revA = < + /* */ + 0 0 2 5 + 1 2 2 6 + 2 3 3 7 + 3 4 4 8 + 4 5 5 9 + 5 6 6 10 + 6 7 7 11 + 7 8 8 12 + 8 9 9 13 + 9 10 10 14 + 10 11 11 15 + 11 12 12 16 + 12 13 13 17 + 13 14 14 19 + 14 15 15 20 + 15 16 16 21 + 16 17 17 22 + 17 18 18 24 + 18 19 19 25 + 19 20 20 27 + 20 21 21 29 + 21 22 22 30 + 22 23 24 32 + 23 25 26 34 + 24 27 28 37 + 25 29 29 39 + 26 30 32 41 + 27 33 34 44 + 28 35 36 47 + 29 37 38 50 + 30 39 40 53 + 31 41 43 56 + 32 44 46 60 + 33 47 49 64 + 34 50 52 68 + 35 53 56 72 + 36 57 59 77 + 37 60 63 82 + 38 64 67 87 + 39 68 71 93 + 40 72 76 98 + 41 77 80 105 + 42 81 86 111 + 43 87 91 119 + 44 92 97 126 + 45 98 104 134 + 46 105 110 143 + 47 111 118 152 + 48 119 125 162 + 49 126 133 172 + 50 134 142 183 + 51 143 150 195 + 52 151 160 207 + 53 161 170 220 + 54 171 181 234 + 55 182 193 249 + 56 194 205 265 + 57 206 218 282 + 58 219 230 300 + 59 231 242 316 + 60 243 254 333 + 61 255 255 360 + >; + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.c b/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.c new file mode 100755 index 000000000000..b5171d182c9a --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.c @@ -0,0 +1,77 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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_dsi1.h" + +static void samsung_pba_config(struct samsung_display_driver_data *vdd, void *arg) +{ + struct sde_mdss_cfg *sde_cfg = (struct sde_mdss_cfg *)arg; + + if (!IS_ERR_OR_NULL(sde_cfg)) { + LCD_INFO("Disable source split\n"); + sde_cfg->has_src_split = false; + } +} + +static void samsung_panel_init(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("%s\n", ss_get_panel_name(vdd)); + + vdd->mdnie.support_mdnie = false; + vdd->dtsi_data.tft_common_support = true; + vdd->panel_func.samsung_pba_config = samsung_pba_config; +} + +static int __init samsung_panel_initialize(void) +{ + struct samsung_display_driver_data *vdd; + enum ss_display_ndx ndx; + char panel_string[] = "ss_dsi_panel_PBA_BOOTING_FHD_DSI1"; + char panel_name[MAX_CMDLINE_PARAM_LEN]; + char panel_secondary_name[MAX_CMDLINE_PARAM_LEN]; + + ss_get_primary_panel_name_cmdline(panel_name); + ss_get_secondary_panel_name_cmdline(panel_secondary_name); + + /* TODO: use component_bind with panel_func + * and match by panel_string, instead. + */ + if (!strncmp(panel_string, panel_name, strlen(panel_string))) + ndx = PRIMARY_DISPLAY_NDX; + else if (!strncmp(panel_string, panel_secondary_name, + strlen(panel_string))) + ndx = SECONDARY_DISPLAY_NDX; + else + return 0; + + vdd = ss_get_vdd(ndx); + vdd->panel_func.samsung_panel_init = samsung_panel_init; + + return 0; + + +} +early_initcall(samsung_panel_initialize); diff --git a/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.h b/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.h new file mode 100755 index 000000000000..9c0494668edf --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/PBA_BOOTING_DSI1/ss_dsi_panel_PBA_BOOTING_fhd_dsi1.h @@ -0,0 +1,33 @@ +/* + * ================================================================= + * + * + * Description: samsung display panel file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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" + +struct smartdim_conf *smart_get_conf_EA8061V_AMS391DT01(void); +#endif diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/Makefile b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/Makefile new file mode 100755 index 000000000000..260d8011ae2b --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/Makefile @@ -0,0 +1,13 @@ +# +# Self Display Object +# namd : ss_dsi_panel_"DDI NAME"_"PANEL NAME".c +# + +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -Idrivers/gpu/drm/msm/dsi-staging +ccflags-y += -Idrivers/gpu/drm/msm/sde +ccflags-y += -Idrivers/gpu/drm/msm/samsung + +obj-y += self_display_FA7.o +obj-y += self_display_HA8.o +obj-y += self_display_HA9.o +obj-y += self_display_XA0.o diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_FA7.c b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_FA7.c new file mode 100755 index 000000000000..a5dec8b64e83 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_FA7.c @@ -0,0 +1,1257 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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_FA7.h" + +/* #define SELF_DISPLAY_TEST */ + +/* + * make dsi_panel_cmds using image data + */ +void make_self_dispaly_img_cmds_FA7(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op) +{ + struct dsi_cmd_desc *tcmds; + struct dsi_panel_cmd_set *pcmds; + + u32 data_size = vdd->self_disp.operation[op].img_size; + char *data = vdd->self_disp.operation[op].img_buf; + int i, j; + int data_idx = 0; + + u32 p_size = CMD_ALIGN; + u32 paylod_size = 0; + u32 cmd_size = 0; + + if (!data) { + LCD_ERR("data is null..\n"); + return; + } + + if (!data_size) { + LCD_ERR("data size is zero..\n"); + return; + } + + /* msg.tx_buf size */ + while (p_size < MAX_PAYLOAD_SIZE) { + if (data_size % p_size == 0) { + paylod_size = p_size; + } + p_size += CMD_ALIGN; + } + /* cmd size */ + cmd_size = data_size / paylod_size; + + LCD_INFO("[%d] total data size [%d]\n", cmd, data_size); + LCD_INFO("cmd size [%d] msg.tx_buf size [%d]\n", cmd_size, paylod_size); + + pcmds = ss_get_cmds(vdd, cmd); + if (pcmds->cmds == NULL) { + pcmds->cmds = kzalloc(cmd_size * sizeof(struct dsi_cmd_desc), GFP_KERNEL); + if (pcmds->cmds == NULL) { + LCD_ERR("fail to kzalloc for self_mask cmds \n"); + return; + } + } + + pcmds->state = DSI_CMD_SET_STATE_HS; + pcmds->count = cmd_size; + + tcmds = pcmds->cmds; + if (tcmds == NULL) { + LCD_ERR("tcmds is NULL \n"); + return; + } + + for (i = 0; i < pcmds->count; i++) { + tcmds[i].msg.type = MIPI_DSI_GENERIC_LONG_WRITE; + tcmds[i].last_command = 1; + + /* fill image data */ + if (tcmds[i].msg.tx_buf == NULL) { + /* +1 means HEADER TYPE 0x4C or 0x5C */ + tcmds[i].msg.tx_buf = kzalloc(paylod_size + 1, GFP_KERNEL); + if (tcmds[i].msg.tx_buf == NULL) { + LCD_ERR("fail to kzalloc for self_mask cmds msg.tx_buf \n"); + return; + } + } + + tcmds[i].msg.tx_buf[0] = (i == 0) ? 0x4C : 0x5C; + + for (j = 1; (j <= paylod_size) && (data_idx < data_size); j++) + tcmds[i].msg.tx_buf[j] = data[data_idx++]; + + tcmds[i].msg.tx_len = j; + + LCD_DEBUG("dlen (%d), data_idx (%d)\n", j, data_idx); + } + + return; +} + +static int self_time_set(struct samsung_display_driver_data *vdd, int from_self_move) +{ + u8 *cmd_pload; + struct self_time_info st_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + st_info = vdd->self_disp.st_info; + + LCD_INFO("Self Time Set h(%d):m(%d):s(%d).ms(%d) / 24h(%d) / Interval(%d) / Time Set(%d)\n", + st_info.cur_h, st_info.cur_m, st_info.cur_s, + st_info.cur_ms, st_info.disp_24h, st_info.interval, vdd->self_disp.time_set); + + pcmds = ss_get_cmds(vdd, TX_SELF_TIME_SET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_TIME_SET..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + cmd_pload[2] |= BIT(2); /* SC_TIME_EN */ + + if (vdd->self_disp.st_info.disp_24h) + cmd_pload[2] |= BIT(5); + else + cmd_pload[2] &= ~(BIT(5)); + + /* Analog Clock Should be enabled before Analog Clock Enable IOCTL */ + cmd_pload[2] |= BIT(0); /* SC_A_CLOCK_EN */ + cmd_pload[2] &= ~(BIT(1)); /* SC_DISP_ON */ + + if (vdd->self_disp.sd_info.en) { + LCD_INFO("Digital Clock Enabled\n"); + cmd_pload[2] &= ~(BIT(0)); /* SC_A_CLOCK_EN */ + cmd_pload[2] |= BIT(1); /* SC_DISP_ON */ + cmd_pload[2] |= BIT(4); /* SC_D_CLOCK_EN */ + + /* SC_D_EN_MM & SC_D_EN_HH */ + if (vdd->self_disp.sd_info.en_hh) { + /* Clear EN_HH First */ + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + cmd_pload[3] |= (vdd->self_disp.sd_info.en_hh & 0x3) << 2; + } else { + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + } + + if (vdd->self_disp.sd_info.en_mm) { + /* Clear EN_MM First */ + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + cmd_pload[3] |= (vdd->self_disp.sd_info.en_mm & 0x3); + } else { + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + } + } else if (vdd->self_disp.sa_info.en) { + LCD_INFO("Analog Clock Enabled\n"); + cmd_pload[2] |= BIT(1); /* SC_DISP_ON */ + } else if (from_self_move) { + LCD_INFO("Self Move Without Any Clock Enabled\n"); + cmd_pload[2] &= ~(BIT(0)); /* SC_A_CLOCK_EN */ + cmd_pload[2] &= ~(BIT(4)); /* SC_D_CLOCK_EN */ + } + + cmd_pload[4] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[5] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[6] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[8] = 0x03; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x10; + else + cmd_pload[9] = 0x00; + break; + case INTERVAL_200: + cmd_pload[8] = 0x06; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x11; + else + cmd_pload[9] = 0x01; + break; + case INTERVAL_500: + cmd_pload[8] = 0x0f; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x12; + else + cmd_pload[9] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[8] = 0x01; + cmd_pload[9] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + ss_send_cmd(vdd, TX_SELF_TIME_SET); + + LCD_ERR("--\n"); + + return 0; +} + +static void self_move_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ Enable(%d), Interval(%d)\n", enable, vdd->self_disp.st_info.interval); + + mutex_lock(&vdd->self_disp.vdd_self_move_lock); + + if (enable) { + if (!vdd->self_disp.sa_info.en && !vdd->self_disp.sd_info.en) + self_time_set(vdd, true); + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_100); + break; + case INTERVAL_200: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_200); + break; + case INTERVAL_500: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_500); + break; + case INTERVAL_1000: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + break; + case INTERVAL_DEBUG: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_DEBUG); + break; + default: /* Default Interval is 1000 */ + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + } else { + /* S6E3FA7 does not support self move index reset */ + ss_send_cmd(vdd, TX_SELF_MOVE_2C_SYNC_OFF); + } + + mutex_unlock(&vdd->self_disp.vdd_self_move_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_icon_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ICON_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_ICON_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static int self_icon_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_icon_info si_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + si_info = vdd->self_disp.si_info; + + LCD_INFO("Self Icon Enable(%d), x(%d), y(%d), w(%d), h(%d)\n", + si_info.en, si_info.pos_x, si_info.pos_y, + si_info.width, si_info.height); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_GRID); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_GRID..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = si_info.pos_x & 0xFF; + + cmd_pload[5] = (si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = si_info.pos_y & 0xFF; + + cmd_pload[7] = (si_info.width & 0x700) >> 8; + cmd_pload[8] = si_info.width & 0xFF; + + cmd_pload[9] = (si_info.height & 0xF00) >> 8; + cmd_pload[10] = si_info.height & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + /* Self Grid setting from last stored information */ + if (vdd->self_disp.sg_info.en) { + cmd_pload[2] |= BIT(4); /* SG_GRID_EN */ + + cmd_pload[3] = (vdd->self_disp.sg_info.s_pos_x & 0x700) >> 8; + cmd_pload[4] = vdd->self_disp.sg_info.s_pos_x & 0xFF; + + cmd_pload[5] = (vdd->self_disp.sg_info.s_pos_y & 0xF00) >> 8; + cmd_pload[6] = vdd->self_disp.sg_info.s_pos_y & 0xFF; + + cmd_pload[7] = (vdd->self_disp.sg_info.e_pos_x & 0x700) >> 8; + cmd_pload[8] = vdd->self_disp.sg_info.e_pos_x & 0xFF; + + cmd_pload[9] = (vdd->self_disp.sg_info.e_pos_y & 0xF00) >> 8; + cmd_pload[10] = vdd->self_disp.sg_info.e_pos_y & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); + } + + ss_send_cmd(vdd, TX_SELF_ICON_GRID); + + LCD_ERR("--\n"); + + return 0; +} + +static int self_grid_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_grid_info sg_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sg_info = vdd->self_disp.sg_info; + + LCD_INFO("Self Grid Enable(%d), s_x(%d), s_y(%d), e_x(%d), e_y(%d)\n", + sg_info.en, sg_info.s_pos_x, sg_info.s_pos_y, + sg_info.e_pos_x, sg_info.e_pos_y); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_GRID); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_GRID..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (sg_info.en) { + cmd_pload[2] |= BIT(4); /* SG_GRID_EN */ + + cmd_pload[3] = (sg_info.s_pos_x & 0x700) >> 8; + cmd_pload[4] = sg_info.s_pos_x & 0xFF; + + cmd_pload[5] = (sg_info.s_pos_y & 0xF00) >> 8; + cmd_pload[6] = sg_info.s_pos_y & 0xFF; + + cmd_pload[7] = (sg_info.e_pos_x & 0x700) >> 8; + cmd_pload[8] = sg_info.e_pos_x & 0xFF; + + cmd_pload[9] = (sg_info.e_pos_y & 0xF00) >> 8; + cmd_pload[10] = sg_info.e_pos_y & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); /* SG_GRID_EN */ + } + + if (vdd->self_disp.si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (vdd->self_disp.si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = vdd->self_disp.si_info.pos_x & 0xFF; + + cmd_pload[5] = (vdd->self_disp.si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = vdd->self_disp.si_info.pos_y & 0xFF; + + cmd_pload[7] = (vdd->self_disp.si_info.width & 0x700) >> 8; + cmd_pload[8] = vdd->self_disp.si_info.width & 0xFF; + + cmd_pload[9] = (vdd->self_disp.si_info.height & 0xF00) >> 8; + cmd_pload[10] = vdd->self_disp.si_info.height & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + ss_send_cmd(vdd, TX_SELF_ICON_GRID); + + LCD_ERR("--\n"); + + return 0; +} +static void self_aclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_ACLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_ACLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_aclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_ACLOCK_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + LCD_ERR("--\n"); +} + +static int self_aclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_analog_clk_info sa_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sa_info = vdd->self_disp.sa_info; + + LCD_INFO("Self Analog Clock Enable(%d), x(%d), y(%d), rot(%d)\n", + sa_info.en, sa_info.pos_x, sa_info.pos_y, sa_info.rotate); + + if (!sa_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_ACLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ACLOCK_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* Time Update from Last Time Info */ + cmd_pload[4] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[5] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[6] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[8] = 0x03; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x10; + else + cmd_pload[9] = 0x00; + break; + case INTERVAL_200: + cmd_pload[8] = 0x06; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x11; + else + cmd_pload[9] = 0x01; + break; + case INTERVAL_500: + cmd_pload[8] = 0x0f; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x12; + else + cmd_pload[9] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[8] = 0x01; + cmd_pload[9] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + /* Self Analog Clock Position Update */ + cmd_pload[10] = (sa_info.pos_x & 0x700) >> 8; + cmd_pload[11] = sa_info.pos_x & 0xFF; + cmd_pload[12] = (sa_info.pos_y & 0xF00) >> 8; + cmd_pload[13] = sa_info.pos_y & 0xFF; + + /* Self Analog Clock Rotation Setting */ + switch (sa_info.rotate) { + case ROTATE_0: + cmd_pload[14] &= ~(BIT(0)); + cmd_pload[14] &= ~(BIT(1)); + break; + case ROTATE_90: + cmd_pload[14] |= BIT(0); + cmd_pload[14] &= ~(BIT(1)); + break; + case ROTATE_180: + cmd_pload[14] &= ~(BIT(0)); + cmd_pload[14] |= BIT(1); + break; + case ROTATE_270: + cmd_pload[14] |= BIT(0); + cmd_pload[14] |= BIT(1); + break; + default: + LCD_ERR("Invalid Rotation Setting, (%d)\n", sa_info.rotate); + } + + vdd->self_disp.time_set = true; + +skip_update: + self_aclock_on(vdd, sa_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_dclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_DCLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_DCLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_dclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_DCLOCK_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + LCD_ERR("--\n"); +} + +static int self_dclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_digital_clk_info sd_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sd_info = vdd->self_disp.sd_info; + + LCD_INFO("Self Digital Clock Enable(%d), 24H(%d), EN_HH(%d), EN_MM(%d), POS_1(%d,%d), POS_2(%d,%d), POS_3(%d,%d), POS_4(%d,%d), W(%d), H(%d)\n", + sd_info.en, vdd->self_disp.st_info.disp_24h, + sd_info.en_hh, sd_info.en_mm, + sd_info.pos1_x, sd_info.pos1_y, + sd_info.pos2_x, sd_info.pos2_y, + sd_info.pos3_x, sd_info.pos3_y, + sd_info.pos4_x, sd_info.pos4_y, + sd_info.img_width, sd_info.img_height); + + if (!sd_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_DCLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_DCLOCK_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (vdd->self_disp.st_info.disp_24h) + cmd_pload[2] |= BIT(5); + else + cmd_pload[2] &= ~(BIT(5)); + + /* Time Update from Last Time Info */ + cmd_pload[4] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[5] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[6] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[8] = 0x03; + cmd_pload[9] = 0x00; + break; + case INTERVAL_200: + cmd_pload[8] = 0x06; + cmd_pload[9] = 0x01; + break; + case INTERVAL_500: + cmd_pload[8] = 0x0f; + cmd_pload[9] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[8] = 0x1E; + cmd_pload[9] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[8] = 0x01; + cmd_pload[9] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + if (sd_info.en_hh) { + /* Clear EN_HH First */ + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + cmd_pload[3] |= (sd_info.en_hh & 0x3) << 2; + } else { + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + } + + if (sd_info.en_mm) { + /* Clear EN_MM First */ + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + cmd_pload[3] |= (sd_info.en_mm & 0x3); + } else { + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + } + + cmd_pload[22] = (sd_info.pos1_x & 0x700) >> 8; + cmd_pload[23] = sd_info.pos1_x & 0xFF; + + cmd_pload[24] = (sd_info.pos1_y & 0xF00) >> 8; + cmd_pload[25] = sd_info.pos1_y & 0xFF; + + cmd_pload[26] = (sd_info.pos2_x & 0x700) >> 8; + cmd_pload[27] = sd_info.pos2_x & 0xFF; + + cmd_pload[28] = (sd_info.pos2_y & 0xF00) >> 8; + cmd_pload[29] = sd_info.pos2_y & 0xFF; + + cmd_pload[30] = (sd_info.pos3_x & 0x700) >> 8; + cmd_pload[31] = sd_info.pos3_x & 0xFF; + + cmd_pload[32] = (sd_info.pos3_y & 0xF00) >> 8; + cmd_pload[33] = sd_info.pos3_y & 0xFF; + + cmd_pload[34] = (sd_info.pos4_x & 0x700) >> 8; + cmd_pload[35] = sd_info.pos4_x & 0xFF; + + cmd_pload[36] = (sd_info.pos4_y & 0xF00) >> 8; + cmd_pload[37] = sd_info.pos4_y & 0xFF; + + cmd_pload[38] = (sd_info.img_width & 0x700) >> 8; + cmd_pload[39] = sd_info.img_width & 0xFF; + + cmd_pload[40] = (sd_info.img_height & 0xF00) >> 8; + cmd_pload[41] = sd_info.img_height & 0xFF; + + vdd->self_disp.time_set = true; + +skip_update: + self_dclock_on(vdd, sd_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_blinking_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_ON); + } else { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_OFF); + } + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_mask_img_write(struct samsung_display_driver_data *vdd) +{ + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + return; + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_MASK_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_MASK_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static void self_mask_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + return; + + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_mask_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_mask_lock); + + LCD_ERR("-- \n"); + + return; +} + +static int self_display_debug(struct samsung_display_driver_data *vdd) +{ + char buf[32]; + + if (ss_get_cmds(vdd, RX_SELF_DISP_DEBUG)->count) { + + ss_panel_data_read(vdd, RX_SELF_DISP_DEBUG, buf, LEVEL1_KEY); + + vdd->self_disp.debug.SI_X_O = ((buf[14] & 0x07) << 8); + vdd->self_disp.debug.SI_X_O |= (buf[15] & 0xFF); + + vdd->self_disp.debug.SI_Y_O = ((buf[16] & 0x0F) << 8); + vdd->self_disp.debug.SI_Y_O |= (buf[17] & 0xFF); + + vdd->self_disp.debug.SM_SUM_O = ((buf[6] & 0xFF) << 24); + vdd->self_disp.debug.SM_SUM_O |= ((buf[7] & 0xFF) << 16); + vdd->self_disp.debug.SM_SUM_O |= ((buf[8] & 0xFF) << 8); + vdd->self_disp.debug.SM_SUM_O |= (buf[9] & 0xFF); + + vdd->self_disp.debug.MEM_SUM_O = ((buf[10] & 0xFF) << 24); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[11] & 0xFF) << 16); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[12] & 0xFF) << 8); + vdd->self_disp.debug.MEM_SUM_O |= (buf[13] & 0xFF); + + LCD_INFO("SI_X_O(%u) SI_Y_O(%u) MEM_SUM_O(%X) SM_SUM_O(%X)\n", + vdd->self_disp.debug.SI_X_O, + vdd->self_disp.debug.SI_Y_O, + vdd->self_disp.debug.MEM_SUM_O, + vdd->self_disp.debug.SM_SUM_O); + + if (vdd->self_disp.operation[FLAG_SELF_MASK].img_checksum != + vdd->self_disp.debug.SM_SUM_O) { + LCD_ERR("self mask img checksum fail!!\n"); + return -1; + } + } + + 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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + if (!vdd->self_disp.on) { + /* Self Icon */ + self_icon_img_write(vdd); + + if (vdd->self_disp.operation[FLAG_SELF_ACLK].select) + self_aclock_img_write(vdd); + if (vdd->self_disp.operation[FLAG_SELF_DCLK].select) + self_dclock_img_write(vdd); + + /* Self display on */ + ss_send_cmd(vdd, TX_SELF_DISP_ON); + + self_mask_on(vdd, false); +#ifdef SELF_DISPLAY_TEST + //self_dclock_img_write(vdd); + self_aclock_img_write(vdd); +#endif + } + + vdd->self_disp.on = true; + + LCD_INFO("--\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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + /* self display off */ + ss_send_cmd(vdd, TX_SELF_DISP_OFF); + + self_mask_on(vdd, true); + + 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; + LCD_INFO("--\n"); + + return ret; +} + +/* + * 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 is null or error\n"); + return -ENODEV; + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return -ENODEV; + } + + if (!vdd->self_disp.on) { + LCD_ERR("self_display was turned off\n"); + return -EPERM; + } + + if ((_IOC_TYPE(cmd) != SELF_DISPLAY_IOCTL_MAGIC) || + (_IOC_NR(cmd) >= IOCTL_SELF_MAX)) { + LCD_ERR("TYPE(%u) NR(%u) is wrong..\n", + _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -EINVAL; + } + + LCD_INFO("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" : "IOCTL_ERR"); + + switch (cmd) { + case IOCTL_SELF_MOVE_EN: + self_move_on(vdd, true); + break; + case IOCTL_SELF_MOVE_OFF: + case IOCTL_SELF_MOVE_RESET: + /* FA7 does not support move reset, use move off only */ + self_move_on(vdd, false); + break; + case IOCTL_SET_ICON: + ret = copy_from_user(&vdd->self_disp.si_info, argp, + sizeof(vdd->self_disp.si_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + self_icon_set(vdd); + break; + case IOCTL_SET_GRID: + ret = copy_from_user(&vdd->self_disp.sg_info, argp, + sizeof(vdd->self_disp.sg_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_grid_set(vdd); + break; + case IOCTL_SET_ANALOG_CLK: + ret = copy_from_user(&vdd->self_disp.sa_info, argp, + sizeof(vdd->self_disp.sa_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_aclock_set(vdd); + break; + case IOCTL_SET_DIGITAL_CLK: + ret = copy_from_user(&vdd->self_disp.sd_info, argp, + sizeof(vdd->self_disp.sd_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_dclock_set(vdd); + break; + case IOCTL_SET_TIME: + ret = copy_from_user(&vdd->self_disp.st_info, argp, + sizeof(vdd->self_disp.st_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_time_set(vdd, false); + break; + default: + LCD_ERR("invalid cmd : %u \n", cmd); + break; + } +error: + + 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) +{ + 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; + + char op_buf[IMAGE_HEADER_SIZE]; + u32 op = 0; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if (unlikely(!buf)) { + LCD_ERR("invalid read buffer\n"); + return -EINVAL; + } + + /* + * get 2byte flas to distinguish what operation is passing + */ + ret = copy_from_user(op_buf, buf, IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (header)\n"); + return ret; + } + + LCD_INFO("Header Buffer = %c%c\n", op_buf[0], op_buf[1]); + + if (op_buf[0] == 'I' && op_buf[1] == 'C') + op = FLAG_SELF_ICON; + else if (op_buf[0] == 'A' && op_buf[1] == 'C') + op = FLAG_SELF_ACLK; + else if (op_buf[0] == 'D' && op_buf[1] == 'C') + op = FLAG_SELF_DCLK; + else { + LCD_ERR("Invalid Header, (%c%c)\n", op_buf[0], op_buf[1]); + return -EINVAL; + } + + LCD_INFO("flag (%d) \n", op); + + if (op >= FLAG_SELF_DISP_MAX) { + LCD_ERR("invalid data flag : %d \n", op); + return -EINVAL; + } + + if (count > vdd->self_disp.operation[op].img_size+IMAGE_HEADER_SIZE) { + LCD_ERR("Buffer OverFlow Detected!! Buffer_Size(%d) Write_Size(%d)\n", + vdd->self_disp.operation[op].img_size, (int)count); + return -EINVAL; + } + + vdd->self_disp.operation[op].wpos = *ppos; + vdd->self_disp.operation[op].wsize = count; + + ret = copy_from_user(vdd->self_disp.operation[op].img_buf, buf+IMAGE_HEADER_SIZE, count-IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (data)\n"); + return ret; + } + + switch (op) { + case FLAG_SELF_MOVE: + // MOVE has no image data.. + break; + case FLAG_SELF_MASK: + make_self_dispaly_img_cmds_FA7(vdd, TX_SELF_MASK_IMAGE, op); + vdd->self_disp.operation[FLAG_SELF_MASK].select = true; + break; + case FLAG_SELF_ICON: + make_self_dispaly_img_cmds_FA7(vdd, TX_SELF_ICON_IMAGE, op); + vdd->self_disp.operation[FLAG_SELF_ICON].select = true; + break; + case FLAG_SELF_GRID: + // GRID has no image data.. + break; + case FLAG_SELF_ACLK: + make_self_dispaly_img_cmds_FA7(vdd, TX_SELF_ACLOCK_IMAGE, op); + vdd->self_disp.operation[FLAG_SELF_ACLK].select = true; + vdd->self_disp.operation[FLAG_SELF_DCLK].select = false; + break; + case FLAG_SELF_DCLK: + make_self_dispaly_img_cmds_FA7(vdd, TX_SELF_DCLOCK_IMAGE, op); + vdd->self_disp.operation[FLAG_SELF_DCLK].select = true; + vdd->self_disp.operation[FLAG_SELF_ACLK].select = false; + break; + default: + LCD_ERR("invalid data flag %d \n", op); + break; + } + + return ret; +} + +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 is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 1; + + LCD_DEBUG("[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 is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 0; + + LCD_DEBUG("[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, +}; + +int self_display_init_FA7(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + char devname[15]; + + 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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("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_move_lock); + mutex_init(&vdd->self_disp.vdd_self_mask_lock); + mutex_init(&vdd->self_disp.vdd_self_aclock_lock); + mutex_init(&vdd->self_disp.vdd_self_dclock_lock); + mutex_init(&vdd->self_disp.vdd_self_icon_grid_lock); + + if (vdd->ndx == PRIMARY_DISPLAY_NDX) + sprintf(devname, "self_display"); + else + sprintf(devname, "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.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_blinking_on = self_blinking_on; + vdd->self_disp.self_display_debug = self_display_debug; + + ret = misc_register(&vdd->self_disp.dev); + if (ret) { + LCD_ERR("failed to register driver : %d\n", ret); + vdd->self_disp.is_support = false; + return -ENODEV; + } + + LCD_INFO("Success to register self_disp device..(%d)\n", ret); + + return ret; +} + +MODULE_DESCRIPTION("Self Display driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_FA7.h b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_FA7.h new file mode 100755 index 000000000000..f5cbde27d3a9 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_FA7.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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 __SELF_DISPLAY_FA7_H__ +#define __SELF_DISPLAY_FA7_H__ + +#include +#include +#include +#include + +int self_display_init_FA7(struct samsung_display_driver_data *vdd); +void make_self_dispaly_img_cmds_FA7(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op); + +#endif // __SELF_DISPLAY_FA7_H__ \ No newline at end of file diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA8.c b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA8.c new file mode 100755 index 000000000000..d02d50394b3f --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA8.c @@ -0,0 +1,1246 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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_HA8.h" + +/* #define SELF_DISPLAY_TEST */ + +/* + * make dsi_panel_cmds using image data + */ +void make_self_dispaly_img_cmds_HA8(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op) +{ + struct dsi_cmd_desc *tcmds; + struct dsi_panel_cmd_set *pcmds; + + u32 data_size = vdd->self_disp.operation[op].img_size; + char *data = vdd->self_disp.operation[op].img_buf; + int i, j; + int data_idx = 0; + + u32 p_size = CMD_ALIGN; + u32 paylod_size = 0; + u32 cmd_size = 0; + + if (!data) { + LCD_ERR("data is null..\n"); + return; + } + + if (!data_size) { + LCD_ERR("data size is zero..\n"); + return; + } + + /* msg.tx_buf size */ + while (p_size < MAX_PAYLOAD_SIZE) { + if (data_size % p_size == 0) { + paylod_size = p_size; + } + p_size += CMD_ALIGN; + } + /* cmd size */ + cmd_size = data_size / paylod_size; + + LCD_INFO("[%d] total data size [%d]\n", cmd, data_size); + LCD_INFO("cmd size [%d] msg.tx_buf size [%d]\n", cmd_size, paylod_size); + + pcmds = ss_get_cmds(vdd, cmd); + if (SS_IS_CMDS_NULL(pcmds)) { + pcmds->cmds = kzalloc(cmd_size * sizeof(struct dsi_cmd_desc), GFP_KERNEL); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("fail to kzalloc for self_mask cmds \n"); + return; + } + } + + pcmds->state = DSI_CMD_SET_STATE_HS; + pcmds->count = cmd_size; + + tcmds = pcmds->cmds; + if (tcmds == NULL) { + LCD_ERR("tcmds is NULL \n"); + return; + } + + for (i = 0; i < pcmds->count; i++) { + tcmds[i].msg.type = MIPI_DSI_GENERIC_LONG_WRITE; + tcmds[i].last_command = 1; + + /* fill image data */ + if (tcmds[i].msg.tx_buf == NULL) { + /* +1 means HEADER TYPE 0x4C or 0x5C */ + tcmds[i].msg.tx_buf = kzalloc(paylod_size + 1, GFP_KERNEL); + if (tcmds[i].msg.tx_buf == NULL) { + LCD_ERR("fail to kzalloc for self_mask cmds msg.tx_buf \n"); + return; + } + } + + tcmds[i].msg.tx_buf[0] = (i == 0) ? 0x4C : 0x5C; + + for (j = 1; (j <= paylod_size) && (data_idx < data_size); j++) + tcmds[i].msg.tx_buf[j] = data[data_idx++]; + + tcmds[i].msg.tx_len = j; + + LCD_DEBUG("dlen (%d), data_idx (%d)\n", j, data_idx); + } + + return; +} + +static int self_time_set(struct samsung_display_driver_data *vdd, int from_self_move) +{ + u8 *cmd_pload; + struct self_time_info st_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + st_info = vdd->self_disp.st_info; + + LCD_INFO("Self Time Set h(%d):m(%d):s(%d).ms(%d) / 24h(%d) / Interval(%d) / Time Set(%d)\n", + st_info.cur_h, st_info.cur_m, st_info.cur_s, + st_info.cur_ms, st_info.disp_24h, st_info.interval, vdd->self_disp.time_set); + + pcmds = ss_get_cmds(vdd, TX_SELF_TIME_SET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_TIME_SET..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + cmd_pload[2] |= BIT(2); /* SC_TIME_EN */ + + if (vdd->self_disp.st_info.disp_24h) + cmd_pload[2] |= BIT(5); + else + cmd_pload[2] &= ~(BIT(5)); + + /* Analog Clock Should be enabled before Analog Clock Enable IOCTL */ + cmd_pload[2] |= BIT(0); /* SC_A_CLOCK_EN */ + cmd_pload[2] &= ~(BIT(1)); /* SC_DISP_ON */ + + if (vdd->self_disp.sd_info.en) { + LCD_INFO("Digital Clock Enabled\n"); + cmd_pload[2] &= ~(BIT(0)); /* SC_A_CLOCK_EN */ + cmd_pload[2] |= BIT(1); /* SC_DISP_ON */ + cmd_pload[2] |= BIT(4); /* SC_D_CLOCK_EN */ + + /* SC_D_EN_MM & SC_D_EN_HH */ + if (vdd->self_disp.sd_info.en_hh) { + /* Clear EN_HH First */ + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + cmd_pload[3] |= (vdd->self_disp.sd_info.en_hh & 0x3) << 2; + } else { + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + } + + if (vdd->self_disp.sd_info.en_mm) { + /* Clear EN_MM First */ + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + cmd_pload[3] |= (vdd->self_disp.sd_info.en_mm & 0x3); + } else { + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + } + } else if (vdd->self_disp.sa_info.en) { + LCD_INFO("Analog Clock Enabled\n"); + cmd_pload[2] |= BIT(1); /* SC_DISP_ON */ + } else if (from_self_move) { + LCD_INFO("Self Move Without Any Clock Enabled\n"); + cmd_pload[2] &= ~(BIT(0)); /* SC_A_CLOCK_EN */ + cmd_pload[2] &= ~(BIT(4)); /* SC_D_CLOCK_EN */ + } + + cmd_pload[4] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[5] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[6] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[8] = 0x03; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x10; + else + cmd_pload[9] = 0x00; + break; + case INTERVAL_200: + cmd_pload[8] = 0x06; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x11; + else + cmd_pload[9] = 0x01; + break; + case INTERVAL_500: + cmd_pload[8] = 0x0f; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x12; + else + cmd_pload[9] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[8] = 0x01; + cmd_pload[9] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + ss_send_cmd(vdd, TX_SELF_TIME_SET); + + LCD_ERR("--\n"); + + return 0; +} + +static void self_move_on(struct samsung_display_driver_data *vdd, int enable) +{ + u8 *cmd_pload; + struct dsi_panel_cmd_set *pcmds; + static int reset_count = 1; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ Enable(%d), Interval(%d)\n", enable, vdd->self_disp.st_info.interval); + + mutex_lock(&vdd->self_disp.vdd_self_move_lock); + + if (enable) { + if (!vdd->self_disp.sa_info.en && !vdd->self_disp.sd_info.en) + self_time_set(vdd, true); + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_100); + break; + case INTERVAL_200: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_200); + break; + case INTERVAL_500: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_500); + break; + case INTERVAL_1000: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + break; + case INTERVAL_DEBUG: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_DEBUG); + break; + default: /* Default Interval is 1000 */ + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + } else { + pcmds = ss_get_cmds(vdd, TX_SELF_MOVE_RESET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_MOVE_RESET..\n"); + return; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + if (reset_count > 255) + reset_count = 1; + else + reset_count++; + cmd_pload[4] = reset_count & 0xFF; + ss_send_cmd(vdd, TX_SELF_MOVE_RESET); + } + + mutex_unlock(&vdd->self_disp.vdd_self_move_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_icon_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ICON_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_ICON_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static int self_icon_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_icon_info si_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + si_info = vdd->self_disp.si_info; + + LCD_INFO("Self Icon Enable(%d), x(%d), y(%d), w(%d), h(%d)\n", + si_info.en, si_info.pos_x, si_info.pos_y, + si_info.width, si_info.height); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_GRID); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_GRID..\n"); + return -ENODEV; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = si_info.pos_x & 0xFF; + + cmd_pload[5] = (si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = si_info.pos_y & 0xFF; + + cmd_pload[7] = (si_info.width & 0x700) >> 8; + cmd_pload[8] = si_info.width & 0xFF; + + cmd_pload[9] = (si_info.height & 0xF00) >> 8; + cmd_pload[10] = si_info.height & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + /* Self Grid setting from last stored information */ + if (vdd->self_disp.sg_info.en) { + cmd_pload[2] |= BIT(4); /* SG_GRID_EN */ + + cmd_pload[3] = (vdd->self_disp.sg_info.s_pos_x & 0x700) >> 8; + cmd_pload[4] = vdd->self_disp.sg_info.s_pos_x & 0xFF; + + cmd_pload[5] = (vdd->self_disp.sg_info.s_pos_y & 0xF00) >> 8; + cmd_pload[6] = vdd->self_disp.sg_info.s_pos_y & 0xFF; + + cmd_pload[7] = (vdd->self_disp.sg_info.e_pos_x & 0x700) >> 8; + cmd_pload[8] = vdd->self_disp.sg_info.e_pos_x & 0xFF; + + cmd_pload[9] = (vdd->self_disp.sg_info.e_pos_y & 0xF00) >> 8; + cmd_pload[10] = vdd->self_disp.sg_info.e_pos_y & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); + } + + ss_send_cmd(vdd, TX_SELF_ICON_GRID); + + LCD_ERR("--\n"); + + return 0; +} + +static int self_grid_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_grid_info sg_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sg_info = vdd->self_disp.sg_info; + + LCD_INFO("Self Grid Enable(%d), s_x(%d), s_y(%d), e_x(%d), e_y(%d)\n", + sg_info.en, sg_info.s_pos_x, sg_info.s_pos_y, + sg_info.e_pos_x, sg_info.e_pos_y); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_GRID); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_GRID..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (sg_info.en) { + cmd_pload[2] |= BIT(4); /* SG_GRID_EN */ + + cmd_pload[3] = (sg_info.s_pos_x & 0x700) >> 8; + cmd_pload[4] = sg_info.s_pos_x & 0xFF; + + cmd_pload[5] = (sg_info.s_pos_y & 0xF00) >> 8; + cmd_pload[6] = sg_info.s_pos_y & 0xFF; + + cmd_pload[7] = (sg_info.e_pos_x & 0x700) >> 8; + cmd_pload[8] = sg_info.e_pos_x & 0xFF; + + cmd_pload[9] = (sg_info.e_pos_y & 0xF00) >> 8; + cmd_pload[10] = sg_info.e_pos_y & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); /* SG_GRID_EN */ + } + + if (vdd->self_disp.si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (vdd->self_disp.si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = vdd->self_disp.si_info.pos_x & 0xFF; + + cmd_pload[5] = (vdd->self_disp.si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = vdd->self_disp.si_info.pos_y & 0xFF; + + cmd_pload[7] = (vdd->self_disp.si_info.width & 0x700) >> 8; + cmd_pload[8] = vdd->self_disp.si_info.width & 0xFF; + + cmd_pload[9] = (vdd->self_disp.si_info.height & 0xF00) >> 8; + cmd_pload[10] = vdd->self_disp.si_info.height & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + ss_send_cmd(vdd, TX_SELF_ICON_GRID); + + LCD_ERR("--\n"); + + return 0; +} +static void self_aclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_ACLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_ACLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_aclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_ACLOCK_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + LCD_ERR("--\n"); +} + +static int self_aclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_analog_clk_info sa_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sa_info = vdd->self_disp.sa_info; + + LCD_INFO("Self Analog Clock Enable(%d), x(%d), y(%d), rot(%d)\n", + sa_info.en, sa_info.pos_x, sa_info.pos_y, sa_info.rotate); + + if (!sa_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_ACLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ACLOCK_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* Time Update from Last Time Info */ + cmd_pload[4] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[5] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[6] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[8] = 0x03; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x10; + else + cmd_pload[9] = 0x00; + break; + case INTERVAL_200: + cmd_pload[8] = 0x06; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x11; + else + cmd_pload[9] = 0x01; + break; + case INTERVAL_500: + cmd_pload[8] = 0x0f; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x12; + else + cmd_pload[9] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[8] = 0x01; + cmd_pload[9] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + /* Self Analog Clock Position Update */ + cmd_pload[10] = (sa_info.pos_x & 0x700) >> 8; + cmd_pload[11] = sa_info.pos_x & 0xFF; + cmd_pload[12] = (sa_info.pos_y & 0xF00) >> 8; + cmd_pload[13] = sa_info.pos_y & 0xFF; + + /* Self Analog Clock Rotation Setting */ + switch (sa_info.rotate) { + case ROTATE_0: + cmd_pload[14] &= ~(BIT(0)); + cmd_pload[14] &= ~(BIT(1)); + break; + case ROTATE_90: + cmd_pload[14] |= BIT(0); + cmd_pload[14] &= ~(BIT(1)); + break; + case ROTATE_180: + cmd_pload[14] &= ~(BIT(0)); + cmd_pload[14] |= BIT(1); + break; + case ROTATE_270: + cmd_pload[14] |= BIT(0); + cmd_pload[14] |= BIT(1); + break; + default: + LCD_ERR("Invalid Rotation Setting, (%d)\n", sa_info.rotate); + } + + vdd->self_disp.time_set = true; + +skip_update: + self_aclock_on(vdd, sa_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_dclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_DCLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_DCLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_dclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_DCLOCK_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + LCD_ERR("--\n"); +} + +static int self_dclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_digital_clk_info sd_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sd_info = vdd->self_disp.sd_info; + + LCD_INFO("Self Digital Clock Enable(%d), 24H(%d), EN_HH(%d), EN_MM(%d), POS_1(%d,%d), POS_2(%d,%d), POS_3(%d,%d), POS_4(%d,%d), W(%d), H(%d)\n", + sd_info.en, vdd->self_disp.st_info.disp_24h, + sd_info.en_hh, sd_info.en_mm, + sd_info.pos1_x, sd_info.pos1_y, + sd_info.pos2_x, sd_info.pos2_y, + sd_info.pos3_x, sd_info.pos3_y, + sd_info.pos4_x, sd_info.pos4_y, + sd_info.img_width, sd_info.img_height); + + if (!sd_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_DCLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_DCLOCK_ON..\n"); + return -ENODEV; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (vdd->self_disp.st_info.disp_24h) + cmd_pload[2] |= BIT(5); + else + cmd_pload[2] &= ~(BIT(5)); + + /* Time Update from Last Time Info */ + cmd_pload[4] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[5] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[6] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[8] = 0x03; + cmd_pload[9] = 0x00; + break; + case INTERVAL_200: + cmd_pload[8] = 0x06; + cmd_pload[9] = 0x01; + break; + case INTERVAL_500: + cmd_pload[8] = 0x0f; + cmd_pload[9] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[8] = 0x1E; + cmd_pload[9] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[8] = 0x01; + cmd_pload[9] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[8] = 0x1E; + if (vdd->self_disp.time_set) + cmd_pload[9] = 0x13; + else + cmd_pload[9] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + if (sd_info.en_hh) { + /* Clear EN_HH First */ + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + cmd_pload[3] |= (sd_info.en_hh & 0x3) << 2; + } else { + cmd_pload[3] &= ~(BIT(2)); + cmd_pload[3] &= ~(BIT(3)); + } + + if (sd_info.en_mm) { + /* Clear EN_MM First */ + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + cmd_pload[3] |= (sd_info.en_mm & 0x3); + } else { + cmd_pload[3] &= ~(BIT(0)); + cmd_pload[3] &= ~(BIT(1)); + } + + cmd_pload[22] = (sd_info.pos1_x & 0x700) >> 8; + cmd_pload[23] = sd_info.pos1_x & 0xFF; + + cmd_pload[24] = (sd_info.pos1_y & 0xF00) >> 8; + cmd_pload[25] = sd_info.pos1_y & 0xFF; + + cmd_pload[26] = (sd_info.pos2_x & 0x700) >> 8; + cmd_pload[27] = sd_info.pos2_x & 0xFF; + + cmd_pload[28] = (sd_info.pos2_y & 0xF00) >> 8; + cmd_pload[29] = sd_info.pos2_y & 0xFF; + + cmd_pload[30] = (sd_info.pos3_x & 0x700) >> 8; + cmd_pload[31] = sd_info.pos3_x & 0xFF; + + cmd_pload[32] = (sd_info.pos3_y & 0xF00) >> 8; + cmd_pload[33] = sd_info.pos3_y & 0xFF; + + cmd_pload[34] = (sd_info.pos4_x & 0x700) >> 8; + cmd_pload[35] = sd_info.pos4_x & 0xFF; + + cmd_pload[36] = (sd_info.pos4_y & 0xF00) >> 8; + cmd_pload[37] = sd_info.pos4_y & 0xFF; + + cmd_pload[38] = (sd_info.img_width & 0x700) >> 8; + cmd_pload[39] = sd_info.img_width & 0xFF; + + cmd_pload[40] = (sd_info.img_height & 0xF00) >> 8; + cmd_pload[41] = sd_info.img_height & 0xFF; + + vdd->self_disp.time_set = true; + +skip_update: + self_dclock_on(vdd, sd_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_blinking_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_ON); + } else { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_OFF); + } + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_mask_img_write(struct samsung_display_driver_data *vdd) +{ + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_MASK_SIDE_MEM_SET); + ss_send_cmd(vdd, TX_SELF_MASK_IMAGE); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static void self_mask_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_mask_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_mask_lock); + + LCD_ERR("-- \n"); + + return; +} + +static int self_display_debug(struct samsung_display_driver_data *vdd) +{ + char buf[32]; + + if (ss_get_cmds(vdd, RX_SELF_DISP_DEBUG)->count) { + + ss_panel_data_read(vdd, RX_SELF_DISP_DEBUG, buf, LEVEL1_KEY); + + vdd->self_disp.debug.SI_X_O = ((buf[14] & 0x07) << 8); + vdd->self_disp.debug.SI_X_O |= (buf[15] & 0xFF); + + vdd->self_disp.debug.SI_Y_O = ((buf[16] & 0x0F) << 8); + vdd->self_disp.debug.SI_Y_O |= (buf[17] & 0xFF); + + vdd->self_disp.debug.SM_SUM_O = ((buf[6] & 0xFF) << 24); + vdd->self_disp.debug.SM_SUM_O |= ((buf[7] & 0xFF) << 16); + vdd->self_disp.debug.SM_SUM_O |= ((buf[8] & 0xFF) << 8); + vdd->self_disp.debug.SM_SUM_O |= (buf[9] & 0xFF); + + vdd->self_disp.debug.MEM_SUM_O = ((buf[10] & 0xFF) << 24); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[11] & 0xFF) << 16); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[12] & 0xFF) << 8); + vdd->self_disp.debug.MEM_SUM_O |= (buf[13] & 0xFF); + + LCD_INFO("SI_X_O(%u) SI_Y_O(%u) MEM_SUM_O(%X) SM_SUM_O(%X)\n", + vdd->self_disp.debug.SI_X_O, + vdd->self_disp.debug.SI_Y_O, + vdd->self_disp.debug.MEM_SUM_O, + vdd->self_disp.debug.SM_SUM_O); + + if (vdd->self_disp.operation[FLAG_SELF_MASK].img_checksum != + vdd->self_disp.debug.SM_SUM_O) { + LCD_ERR("self mask img checksum fail!!\n"); + return -1; + } + } + + 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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + if (!vdd->self_disp.on) { + /* Self Icon */ + self_icon_img_write(vdd); + + if (vdd->self_disp.operation[FLAG_SELF_ACLK].select) + self_aclock_img_write(vdd); + if (vdd->self_disp.operation[FLAG_SELF_DCLK].select) + self_dclock_img_write(vdd); + + /* Self display on */ + ss_send_cmd(vdd, TX_SELF_DISP_ON); + + self_mask_on(vdd, false); +#ifdef SELF_DISPLAY_TEST + //self_dclock_img_write(vdd); + self_aclock_img_write(vdd); +#endif + } + + vdd->self_disp.on = true; + + LCD_INFO("--\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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + /* self display off */ + ss_send_cmd(vdd, TX_SELF_DISP_OFF); + + self_mask_on(vdd, true); + + 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; + LCD_INFO("--\n"); + + return ret; +} + +/* + * 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 samsung_display_driver_data *vdd = file->private_data; + void __user *argp = (void __user *)arg; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return -ENODEV; + } + + if (!vdd->self_disp.on) { + LCD_ERR("self_display was turned off\n"); + return -EPERM; + } + + if ((_IOC_TYPE(cmd) != SELF_DISPLAY_IOCTL_MAGIC) || + (_IOC_NR(cmd) >= IOCTL_SELF_MAX)) { + LCD_ERR("TYPE(%u) NR(%u) is wrong..\n", + _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -EINVAL; + } + + LCD_INFO("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" : "IOCTL_ERR"); + + switch (cmd) { + case IOCTL_SELF_MOVE_EN: + self_move_on(vdd, true); + break; + case IOCTL_SELF_MOVE_OFF: + self_move_on(vdd, false); + break; + case IOCTL_SET_ICON: + ret = copy_from_user(&vdd->self_disp.si_info, argp, + sizeof(vdd->self_disp.si_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + self_icon_set(vdd); + break; + case IOCTL_SET_GRID: + ret = copy_from_user(&vdd->self_disp.sg_info, argp, + sizeof(vdd->self_disp.sg_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_grid_set(vdd); + break; + case IOCTL_SET_ANALOG_CLK: + ret = copy_from_user(&vdd->self_disp.sa_info, argp, + sizeof(vdd->self_disp.sa_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_aclock_set(vdd); + break; + case IOCTL_SET_DIGITAL_CLK: + ret = copy_from_user(&vdd->self_disp.sd_info, argp, + sizeof(vdd->self_disp.sd_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_dclock_set(vdd); + break; + case IOCTL_SET_TIME: + ret = copy_from_user(&vdd->self_disp.st_info, argp, + sizeof(vdd->self_disp.st_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_time_set(vdd, false); + break; + default: + LCD_ERR("invalid cmd : %u \n", cmd); + break; + } +error: + + 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) +{ + struct samsung_display_driver_data *vdd = file->private_data; + char op_buf[IMAGE_HEADER_SIZE]; + u32 op = 0; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if (unlikely(!buf)) { + LCD_ERR("invalid read buffer\n"); + return -EINVAL; + } + + /* + * get 2byte flas to distinguish what operation is passing + */ + ret = copy_from_user(op_buf, buf, IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (header)\n"); + return ret; + } + + LCD_INFO("Header Buffer = %c%c\n", op_buf[0], op_buf[1]); + + if (op_buf[0] == 'I' && op_buf[1] == 'C') + op = FLAG_SELF_ICON; + else if (op_buf[0] == 'A' && op_buf[1] == 'C') + op = FLAG_SELF_ACLK; + else if (op_buf[0] == 'D' && op_buf[1] == 'C') + op = FLAG_SELF_DCLK; + else { + LCD_ERR("Invalid Header, (%c%c)\n", op_buf[0], op_buf[1]); + return -EINVAL; + } + + LCD_INFO("flag (%d) \n", op); + + if (op >= FLAG_SELF_DISP_MAX) { + LCD_ERR("invalid data flag : %d \n", op); + return -EINVAL; + } + + if (count > vdd->self_disp.operation[op].img_size+IMAGE_HEADER_SIZE) { + LCD_ERR("Buffer OverFlow Detected!! Buffer_Size(%d) Write_Size(%d)\n", + vdd->self_disp.operation[op].img_size, (int)count); + return -EINVAL; + } + + vdd->self_disp.operation[op].wpos = *ppos; + vdd->self_disp.operation[op].wsize = count; + + ret = copy_from_user(vdd->self_disp.operation[op].img_buf, buf+IMAGE_HEADER_SIZE, count-IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (data)\n"); + return ret; + } + + switch (op) { + case FLAG_SELF_MOVE: + // MOVE has no image data.. + break; + case FLAG_SELF_MASK: + make_self_dispaly_img_cmds_HA8(vdd, TX_SELF_MASK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + break; + case FLAG_SELF_ICON: + make_self_dispaly_img_cmds_HA8(vdd, TX_SELF_ICON_IMAGE, op); + vdd->self_disp.operation[op].select = true; + break; + case FLAG_SELF_GRID: + // GRID has no image data.. + break; + case FLAG_SELF_ACLK: + make_self_dispaly_img_cmds_HA8(vdd, TX_SELF_ACLOCK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + vdd->self_disp.operation[FLAG_SELF_DCLK].select = false; + break; + case FLAG_SELF_DCLK: + make_self_dispaly_img_cmds_HA8(vdd, TX_SELF_DCLOCK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + vdd->self_disp.operation[FLAG_SELF_ACLK].select = false; + break; + default: + LCD_ERR("invalid data flag %d \n", op); + break; + } + + return ret; +} + +static int self_display_open(struct inode *inode, struct file *file) +{ + /* TODO: get appropriate vdd..primary or secondary... */ + struct samsung_display_driver_data *vdd = ss_get_vdd(PRIMARY_DISPLAY_NDX); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 1; + file->private_data = vdd; + + LCD_DEBUG("[open]\n"); + + return 0; +} + +static int self_display_release(struct inode *inode, struct file *file) +{ + struct samsung_display_driver_data *vdd = file->private_data; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 0; + + LCD_DEBUG("[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, +}; + +int self_display_init_HA8(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + char devname[15]; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("Self Display is not supported\n"); + return -EINVAL; + } + + mutex_init(&vdd->self_disp.vdd_self_move_lock); + mutex_init(&vdd->self_disp.vdd_self_mask_lock); + mutex_init(&vdd->self_disp.vdd_self_aclock_lock); + mutex_init(&vdd->self_disp.vdd_self_dclock_lock); + mutex_init(&vdd->self_disp.vdd_self_icon_grid_lock); + + if (vdd->ndx == PRIMARY_DISPLAY_NDX) + sprintf(devname, "self_display"); + else + sprintf(devname, "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 = NULL; + + 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_blinking_on = self_blinking_on; + vdd->self_disp.self_display_debug = self_display_debug; + + ret = misc_register(&vdd->self_disp.dev); + if (ret) { + LCD_ERR("failed to register driver : %d\n", ret); + vdd->self_disp.is_support = false; + return -ENODEV; + } + + LCD_INFO("Success to register self_disp device..(%d)\n", ret); + + return ret; +} + +MODULE_DESCRIPTION("Self Display driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA8.h b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA8.h new file mode 100755 index 000000000000..7ac5ba6838a5 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA8.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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 __SELF_DISPLAY_HA8_H__ +#define __SELF_DISPLAY_HA8_H__ + +#include +#include +#include +#include + +int self_display_init_HA8(struct samsung_display_driver_data *vdd); +void make_self_dispaly_img_cmds_HA8(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op); + +#endif // __SELF_DISPLAY_HA8_H__ \ No newline at end of file diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA9.c b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA9.c new file mode 100755 index 000000000000..6bb20bd3e5dc --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA9.c @@ -0,0 +1,1271 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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_HA9.h" + +/* #define SELF_DISPLAY_TEST */ + +/* + * make dsi_panel_cmds using image data + */ +void make_self_dispaly_img_cmds_HA9(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op) +{ + struct dsi_cmd_desc *tcmds; + struct dsi_panel_cmd_set *pcmds; + + u32 data_size = vdd->self_disp.operation[op].img_size; + char *data = vdd->self_disp.operation[op].img_buf; + int i, j; + int data_idx = 0; + + u32 p_size = CMD_ALIGN; + u32 paylod_size = 0; + u32 cmd_size = 0; + + if (!data) { + LCD_ERR("data is null..\n"); + return; + } + + if (!data_size) { + LCD_ERR("data size is zero..\n"); + return; + } + + /* msg.tx_buf size */ + while (p_size < MAX_PAYLOAD_SIZE) { + if (data_size % p_size == 0) { + paylod_size = p_size; + } + p_size += CMD_ALIGN; + } + /* cmd size */ + cmd_size = data_size / paylod_size; + + LCD_INFO("[%d] total data size [%d]\n", cmd, data_size); + LCD_INFO("cmd size [%d] msg.tx_buf size [%d]\n", cmd_size, paylod_size); + + pcmds = ss_get_cmds(vdd, cmd); + if (IS_ERR_OR_NULL(pcmds->cmds)) { + LCD_ERR("pcmds->cmds is null!!\n"); + pcmds->cmds = kzalloc(cmd_size * sizeof(struct dsi_cmd_desc), GFP_KERNEL); + if (IS_ERR_OR_NULL(pcmds->cmds)) { + LCD_ERR("fail to kzalloc for self_mask cmds \n"); + return; + } + } + + pcmds->state = DSI_CMD_SET_STATE_HS; + pcmds->count = cmd_size; + + tcmds = pcmds->cmds; + if (tcmds == NULL) { + LCD_ERR("tcmds is NULL \n"); + return; + } + + for (i = 0; i < pcmds->count; i++) { + tcmds[i].msg.type = MIPI_DSI_GENERIC_LONG_WRITE; + tcmds[i].last_command = 1; + + /* fill image data */ + if (tcmds[i].msg.tx_buf == NULL) { + /* +1 means HEADER TYPE 0x4C or 0x5C */ + tcmds[i].msg.tx_buf = kzalloc(paylod_size + 1, GFP_KERNEL); + if (tcmds[i].msg.tx_buf == NULL) { + LCD_ERR("fail to kzalloc for self_mask cmds msg.tx_buf \n"); + return; + } + } + + tcmds[i].msg.tx_buf[0] = (i == 0) ? 0x4C : 0x5C; + + for (j = 1; (j <= paylod_size) && (data_idx < data_size); j++) + tcmds[i].msg.tx_buf[j] = data[data_idx++]; + + tcmds[i].msg.tx_len = j; + + LCD_DEBUG("dlen (%d), data_idx (%d)\n", j, data_idx); + } + + return; +} + +static int self_time_set(struct samsung_display_driver_data *vdd, int from_self_move) +{ + u8 *cmd_pload; + struct self_time_info st_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + st_info = vdd->self_disp.st_info; + + LCD_INFO("Self Time Set h(%d):m(%d):s(%d).ms(%d) / 24h(%d) / Interval(%d) / Time Set(%d)\n", + st_info.cur_h, st_info.cur_m, st_info.cur_s, + st_info.cur_ms, st_info.disp_24h, st_info.interval, vdd->self_disp.time_set); + + pcmds = ss_get_cmds(vdd, TX_SELF_TIME_SET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_TIME_SET..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + cmd_pload[2] = 0x03; /* SC_TIME_EN | SC_T_DISP_ON */ + + cmd_pload[3] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[4] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[5] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[7] = 0x03; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x10; + else + cmd_pload[8] = 0x00; + break; + case INTERVAL_200: + cmd_pload[7] = 0x06; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x11; + else + cmd_pload[8] = 0x01; + break; + case INTERVAL_500: + cmd_pload[7] = 0x0f; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x12; + else + cmd_pload[8] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[7] = 0x1e; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x13; + else + cmd_pload[8] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[7] = 0x01; + cmd_pload[8] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[7] = 0x1e; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x13; + else + cmd_pload[8] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + if (from_self_move) { + LCD_INFO("Self Move Without Any Clock Enabled\n"); + cmd_pload[1] = cmd_pload[3] = cmd_pload[4] = cmd_pload[5] = 0x00; + cmd_pload[6] = cmd_pload[8] = cmd_pload[9] = cmd_pload[10] = 0x00; + cmd_pload[2] = 0x01; /* SC_TIME_EN */ + cmd_pload[7] = 0x03; /* SC_UPDATE_RATE(3) */ + } + + ss_send_cmd(vdd, TX_SELF_TIME_SET); + + vdd->self_disp.time_set = true; + + LCD_ERR("--\n"); + + return 0; +} + +static void self_move_set(struct samsung_display_driver_data *vdd, int ctrl) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ Control(%s), Interval(%d)\n", ctrl == SELF_MOVE_ON ? "SELF_MOVE_ON" : + ctrl == SELF_MOVE_RESET ? "SELF_MOVE_RESET" : + ctrl == SELF_MOVE_OFF ? "SELF_MOVE_OFF" : "Unknown Self Move" + , vdd->self_disp.st_info.interval); + + mutex_lock(&vdd->self_disp.vdd_self_move_lock); + + switch (ctrl) { + case SELF_MOVE_ON: + if (!vdd->self_disp.sa_info.en && !vdd->self_disp.sd_info.en) + self_time_set(vdd, true); + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_100); + break; + case INTERVAL_200: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_200); + break; + case INTERVAL_500: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_500); + break; + case INTERVAL_1000: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + break; + case INTERVAL_DEBUG: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_DEBUG); + break; + default: /* Default Interval is 1000 */ + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + break; + case SELF_MOVE_RESET: + ss_send_cmd(vdd, TX_SELF_MOVE_RESET); + break; + case SELF_MOVE_OFF: + ss_send_cmd(vdd, TX_SELF_MOVE_OFF); + break; + default: + LCD_ERR("Invalid Self Move Control (%d)\n", ctrl); + } + + mutex_unlock(&vdd->self_disp.vdd_self_move_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_icon_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ICON_SET_PRE); + ss_send_cmd(vdd, TX_SELF_ICON_IMAGE); + ss_send_cmd(vdd, TX_SELF_ICON_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static int self_icon_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_icon_info si_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + si_info = vdd->self_disp.si_info; + + LCD_INFO("Self Icon Enable(%d), x(%d), y(%d), w(%d), h(%d), Color(0x%x)\n", + si_info.en, si_info.pos_x, si_info.pos_y, + si_info.width, si_info.height, si_info.color); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = si_info.pos_x & 0xFF; + + cmd_pload[5] = (si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = si_info.pos_y & 0xFF; + + cmd_pload[7] = (si_info.width & 0x700) >> 8; + cmd_pload[8] = si_info.width & 0xFF; + + cmd_pload[9] = (si_info.height & 0xF00) >> 8; + cmd_pload[10] = si_info.height & 0xFF; + + cmd_pload[11] = (si_info.color & 0xFF000000) >> 24; /* A */ + cmd_pload[12] = (si_info.color & 0xFF0000) >> 16; /* R */ + cmd_pload[13] = (si_info.color & 0xFF00) >> 8; /* G */ + cmd_pload[14] = (si_info.color & 0xFF); /* B */ + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + ss_send_cmd(vdd, TX_SELF_ICON_ON); + + LCD_ERR("--\n"); + + return 0; +} + +static int self_grid_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_grid_info sg_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sg_info = vdd->self_disp.sg_info; + + LCD_INFO("Self Grid Enable(%d), s_x(%d), s_y(%d), e_x(%d), e_y(%d)\n", + sg_info.en, sg_info.s_pos_x, sg_info.s_pos_y, + sg_info.e_pos_x, sg_info.e_pos_y); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_GRID); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_GRID..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (sg_info.en) { + cmd_pload[2] |= BIT(4); /* SG_GRID_EN */ + + cmd_pload[3] = (sg_info.s_pos_x & 0x700) >> 8; + cmd_pload[4] = sg_info.s_pos_x & 0xFF; + + cmd_pload[5] = (sg_info.s_pos_y & 0xF00) >> 8; + cmd_pload[6] = sg_info.s_pos_y & 0xFF; + + cmd_pload[7] = (sg_info.e_pos_x & 0x700) >> 8; + cmd_pload[8] = sg_info.e_pos_x & 0xFF; + + cmd_pload[9] = (sg_info.e_pos_y & 0xF00) >> 8; + cmd_pload[10] = sg_info.e_pos_y & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); /* SG_GRID_EN */ + } + + if (vdd->self_disp.si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (vdd->self_disp.si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = vdd->self_disp.si_info.pos_x & 0xFF; + + cmd_pload[5] = (vdd->self_disp.si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = vdd->self_disp.si_info.pos_y & 0xFF; + + cmd_pload[7] = (vdd->self_disp.si_info.width & 0x700) >> 8; + cmd_pload[8] = vdd->self_disp.si_info.width & 0xFF; + + cmd_pload[9] = (vdd->self_disp.si_info.height & 0xF00) >> 8; + cmd_pload[10] = vdd->self_disp.si_info.height & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + ss_send_cmd(vdd, TX_SELF_ICON_GRID); + + LCD_ERR("--\n"); + + return 0; +} +static void self_aclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_ACLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_ACLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_aclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_SET_PRE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_IMAGE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + LCD_ERR("--\n"); +} + +static int self_aclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + u32 mem_reuse_x, mem_reuse_y; + struct self_analog_clk_info sa_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sa_info = vdd->self_disp.sa_info; + + LCD_INFO("Self Analog Clock Enable(%d), x(%d), y(%d), rot(%d), mem_mask_en(%d), mem_reuse_en(%d)\n", + sa_info.en, sa_info.pos_x, sa_info.pos_y, sa_info.rotate, sa_info.mem_mask_en, sa_info.mem_reuse_en); + + if (!sa_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_ACLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ACLOCK_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* Self Analog Clock Position Update */ + cmd_pload[3] = (sa_info.pos_x & 0x700) >> 8; + cmd_pload[4] = sa_info.pos_x & 0xFF; + cmd_pload[5] = (sa_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = sa_info.pos_y & 0xFF; + + /* Self Analog Clock Rotation Setting */ + cmd_pload[7] &= ~(BIT(0)); /* BIT_0 Clear */ + cmd_pload[7] &= ~(BIT(1)); /* BIT_1 Clear */ + + switch (sa_info.rotate) { + case ROTATE_0: + cmd_pload[7] &= ~(BIT(0)); + cmd_pload[7] &= ~(BIT(1)); + break; + case ROTATE_90: + cmd_pload[7] |= BIT(0); + cmd_pload[7] &= ~(BIT(1)); + break; + case ROTATE_180: + cmd_pload[7] &= ~(BIT(0)); + cmd_pload[7] |= BIT(1); + break; + case ROTATE_270: + cmd_pload[7] |= BIT(0); + cmd_pload[7] |= BIT(1); + break; + default: + LCD_ERR("Invalid Rotation Setting, (%d)\n", sa_info.rotate); + } + + /* Clock Memory Mask for Power Saving */ + if (sa_info.mem_mask_en) { + cmd_pload[15] = AC_HH_MASK_ST_X & 0xFF; + cmd_pload[16] = AC_HH_MASK_ST_Y & 0x3F; + cmd_pload[17] = AC_HH_MASK_ED_X & 0xFF; + cmd_pload[18] = AC_HH_MASK_ED_Y & 0x3F; + cmd_pload[19] = AC_MM_MASK_ST_X & 0xFF; + cmd_pload[20] = AC_MM_MASK_ST_Y & 0x3F; + cmd_pload[21] = AC_MM_MASK_ED_X & 0xFF; + cmd_pload[22] = AC_MM_MASK_ED_Y & 0x3F; + cmd_pload[23] = AC_SS_MASK_ST_X & 0xFF; + cmd_pload[24] = AC_SS_MASK_ST_Y & 0x3F; + cmd_pload[25] = AC_SS_MASK_ED_X & 0xFF; + cmd_pload[26] = AC_SS_MASK_ED_Y & 0x3F; + } else { + /* Memory Mask does not have enable register, All the value should be zero incase of disable */ + cmd_pload[15] = cmd_pload[16] = cmd_pload[17] = cmd_pload[18] = cmd_pload[19] = cmd_pload[20] = 0x00; + cmd_pload[21] = cmd_pload[22] = cmd_pload[23] = cmd_pload[24] = cmd_pload[25] = cmd_pload[26] = 0x00; + } + + /* Clock Memory Reuse for Power Saving */ + if ((sa_info.pos_x - (AC_HH_MEM_REUSE_W/2)) >= 0) + mem_reuse_x = sa_info.pos_x - (AC_HH_MEM_REUSE_W/2); + else + mem_reuse_x = 0; + + if ((sa_info.pos_y - (AC_HH_MEM_REUSE_H/2)) >= 0) + mem_reuse_y = sa_info.pos_x - (AC_HH_MEM_REUSE_H/2); + else + mem_reuse_y = 0; + + if (sa_info.mem_reuse_en) { + cmd_pload[2] |= BIT(4); /* SC_MEM_DISP_ON */ + cmd_pload[27] = (mem_reuse_x & 0x700) >> 8; + cmd_pload[28] = mem_reuse_x & 0xFF; + cmd_pload[29] = (mem_reuse_y & 0xF00) >> 8; + cmd_pload[30] = mem_reuse_y & 0xFF; + cmd_pload[31] = (AC_HH_MEM_REUSE_W & 0x700) >> 8; + cmd_pload[32] = AC_HH_MEM_REUSE_W & 0xFF; + cmd_pload[33] = (AC_HH_MEM_REUSE_H & 0xF00) >> 8; + cmd_pload[34] = AC_HH_MEM_REUSE_H & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); /* SC_MEM_DISP_ON */ + } + +skip_update: + self_aclock_on(vdd, sa_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_dclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_DCLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_DCLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_dclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_SET_PRE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_IMAGE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + LCD_ERR("--\n"); +} + +static int self_dclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_digital_clk_info sd_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sd_info = vdd->self_disp.sd_info; + + LCD_INFO("Self Digital Clock Enable(%d), 24H(%d), EN_HH(%d), EN_MM(%d), POS_1(%d,%d), POS_2(%d,%d), POS_3(%d,%d), POS_4(%d,%d), W(%d), H(%d), Color(0x%x), UNI_ATTR(%d), UNI_W(%d)\n", + sd_info.en, vdd->self_disp.st_info.disp_24h, + sd_info.en_hh, sd_info.en_mm, + sd_info.pos1_x, sd_info.pos1_y, + sd_info.pos2_x, sd_info.pos2_y, + sd_info.pos3_x, sd_info.pos3_y, + sd_info.pos4_x, sd_info.pos4_y, + sd_info.img_width, sd_info.img_height, + sd_info.color, sd_info.unicode_attr, sd_info.unicode_width); + + if (!sd_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_DCLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_DCLOCK_ON..\n"); + return -ENODEV; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (vdd->self_disp.st_info.disp_24h) { + cmd_pload[4] |= vdd->self_disp.st_info.disp_24h & 0x30; + } else { + cmd_pload[4] &= ~(BIT(4)); + cmd_pload[4] &= ~(BIT(5)); + } + + if (sd_info.en_hh) { + /* Clear EN_HH First */ + cmd_pload[4] &= ~(BIT(2)); + cmd_pload[4] &= ~(BIT(3)); + cmd_pload[4] |= (sd_info.en_hh & 0x3) << 2; + } else { + cmd_pload[4] &= ~(BIT(2)); + cmd_pload[4] &= ~(BIT(3)); + } + + if (sd_info.en_mm) { + /* Clear EN_MM First */ + cmd_pload[4] &= ~(BIT(0)); + cmd_pload[4] &= ~(BIT(1)); + cmd_pload[4] |= (sd_info.en_mm & 0x3); + } else { + cmd_pload[4] &= ~(BIT(0)); + cmd_pload[4] &= ~(BIT(1)); + } + + cmd_pload[5] = (sd_info.pos1_x & 0x700) >> 8; + cmd_pload[6] = sd_info.pos1_x & 0xFF; + cmd_pload[7] = (sd_info.pos1_y & 0xF00) >> 8; + cmd_pload[8] = sd_info.pos1_y & 0xFF; + + cmd_pload[9] = (sd_info.pos2_x & 0x700) >> 8; + cmd_pload[10] = sd_info.pos2_x & 0xFF; + cmd_pload[11] = (sd_info.pos2_y & 0xF00) >> 8; + cmd_pload[12] = sd_info.pos2_y & 0xFF; + + cmd_pload[13] = (sd_info.pos3_x & 0x700) >> 8; + cmd_pload[14] = sd_info.pos3_x & 0xFF; + cmd_pload[15] = (sd_info.pos3_y & 0xF00) >> 8; + cmd_pload[16] = sd_info.pos3_y & 0xFF; + + cmd_pload[17] = (sd_info.pos4_x & 0x700) >> 8; + cmd_pload[18] = sd_info.pos4_x & 0xFF; + cmd_pload[19] = (sd_info.pos4_y & 0xF00) >> 8; + cmd_pload[20] = sd_info.pos4_y & 0xFF; + + cmd_pload[21] = (sd_info.img_width & 0x700) >> 8; + cmd_pload[22] = sd_info.img_width & 0xFF; + cmd_pload[23] = (sd_info.img_height & 0xF00) >> 8; + cmd_pload[24] = sd_info.img_height & 0xFF; + + /* Color */ + cmd_pload[25] = (sd_info.color & 0xF000) >> 12; /* A */ + cmd_pload[26] = (sd_info.color & 0xF00) >> 8; /* R */ + cmd_pload[27] = (sd_info.color & 0xF0) >> 4; /* G */ + cmd_pload[28] = (sd_info.color & 0xF); /* B */ + + /* Unicode */ + cmd_pload[3] = sd_info.unicode_attr & 0xFF; + + switch (sd_info.unicode_attr) { + case 0x02: + default: + cmd_pload[31] = 0; + cmd_pload[32] = 0; + cmd_pload[33] = 0; + cmd_pload[34] = 0; + cmd_pload[35] = 0; + } + + cmd_pload[29] = ((sd_info.unicode_width) & 0x700) >> 8; + cmd_pload[30] = sd_info.unicode_width & 0xFF; + +skip_update: + self_dclock_on(vdd, sd_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_blinking_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_ON); + } else { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_OFF); + } + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_mask_img_write(struct samsung_display_driver_data *vdd) +{ + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_MASK_SET_PRE); + ss_send_cmd(vdd, TX_SELF_MASK_IMAGE); + ss_send_cmd(vdd, TX_SELF_MASK_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static void self_mask_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_mask_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_mask_lock); + + LCD_ERR("-- \n"); + + return; +} + +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 is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sphs_info = vdd->self_disp.sphs_info; + + LCD_INFO("Self Partial HLPM/Scan hlpm_En(%d), hlpm_mode_sel(0x%x), hlpm_a1(%d), hlpm_a2(%d),\ + hlpm_a3(%d), hlpm_a4(%d) / scan_en(%d), scan_line(%d_%d)\n", + sphs_info.hlpm_en, sphs_info.hlpm_mode_sel, + sphs_info.hlpm_area_1, sphs_info.hlpm_area_2, + sphs_info.hlpm_area_3, sphs_info.hlpm_area_4, + sphs_info.scan_en, sphs_info.scan_sl, sphs_info.scan_el); + + pcmds = ss_get_cmds(vdd, TX_SELF_PARTIAL_HLPM_SCAN_SET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_PARTIAL_HLPM_SCAN_SET..\n"); + return -ENODEV; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* Partial HLPM */ + if (sphs_info.hlpm_en) { + cmd_pload[1] |= BIT(0); /* SP_PTL_EN */ + cmd_pload[2] = sphs_info.hlpm_mode_sel & 0x1F; /* SP_PTL_MODE_SEL */ + + /* Area_1 */ + cmd_pload[9] = (sphs_info.hlpm_area_1 & 0xF00) >> 8; + cmd_pload[10] = sphs_info.hlpm_area_1 & 0xFF; + /* Area_2 */ + cmd_pload[11] = (sphs_info.hlpm_area_2 & 0xF00) >> 8; + cmd_pload[12] = sphs_info.hlpm_area_2 & 0xFF; + /* Area_3 */ + cmd_pload[13] = (sphs_info.hlpm_area_3 & 0xF00) >> 8; + cmd_pload[14] = sphs_info.hlpm_area_3 & 0xFF; + /* Area_4 */ + cmd_pload[15] = (sphs_info.hlpm_area_4 & 0xF00) >> 8; + cmd_pload[16] = sphs_info.hlpm_area_4 & 0xFF; + } else { + cmd_pload[1] &= ~(BIT(0)); /* SP_PTL_EN */ + } + + /* Partial Scan */ + if (sphs_info.scan_en) { + cmd_pload[1] |= BIT(4); /* SP_PTLSCAN_EN */ + + cmd_pload[5] = (sphs_info.scan_sl & 0xF00) >> 8; + cmd_pload[6] = sphs_info.scan_sl & 0xFF; + cmd_pload[7] = (sphs_info.scan_el & 0xF00) >> 8; + cmd_pload[8] = sphs_info.scan_el & 0xFF; + } else { + cmd_pload[1] &= ~(BIT(4)); /* SP_PTLSCAN_EN */ + } + + ss_send_cmd(vdd, TX_SELF_PARTIAL_HLPM_SCAN_SET); + + LCD_ERR("--\n"); + + return 0; +} + +static int self_display_debug(struct samsung_display_driver_data *vdd) +{ + char buf[32]; + + if (ss_get_cmds(vdd, RX_SELF_DISP_DEBUG)->count) { + + ss_panel_data_read(vdd, RX_SELF_DISP_DEBUG, buf, LEVEL1_KEY); + + vdd->self_disp.debug.SI_X_O = ((buf[14] & 0x07) << 8); + vdd->self_disp.debug.SI_X_O |= (buf[15] & 0xFF); + + vdd->self_disp.debug.SI_Y_O = ((buf[16] & 0x0F) << 8); + vdd->self_disp.debug.SI_Y_O |= (buf[17] & 0xFF); + + vdd->self_disp.debug.SM_SUM_O = ((buf[6] & 0xFF) << 24); + vdd->self_disp.debug.SM_SUM_O |= ((buf[7] & 0xFF) << 16); + vdd->self_disp.debug.SM_SUM_O |= ((buf[8] & 0xFF) << 8); + vdd->self_disp.debug.SM_SUM_O |= (buf[9] & 0xFF); + + vdd->self_disp.debug.MEM_SUM_O = ((buf[10] & 0xFF) << 24); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[11] & 0xFF) << 16); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[12] & 0xFF) << 8); + vdd->self_disp.debug.MEM_SUM_O |= (buf[13] & 0xFF); + + LCD_INFO("SI_X_O(%u) SI_Y_O(%u) MEM_SUM_O(%X) SM_SUM_O(%X)\n", + vdd->self_disp.debug.SI_X_O, + vdd->self_disp.debug.SI_Y_O, + vdd->self_disp.debug.MEM_SUM_O, + vdd->self_disp.debug.SM_SUM_O); + + if (vdd->self_disp.operation[FLAG_SELF_MASK].img_checksum != + vdd->self_disp.debug.SM_SUM_O) { + LCD_ERR("self mask img checksum fail!!\n"); + return -1; + } + } + + 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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + if (!vdd->self_disp.on) { + /* Self Icon */ + self_icon_img_write(vdd); + + if (vdd->self_disp.operation[FLAG_SELF_ACLK].select) + self_aclock_img_write(vdd); + if (vdd->self_disp.operation[FLAG_SELF_DCLK].select) + self_dclock_img_write(vdd); + + /* Self display on */ + ss_send_cmd(vdd, TX_SELF_DISP_ON); + + self_mask_on(vdd, false); +#ifdef SELF_DISPLAY_TEST + //self_dclock_img_write(vdd); + self_aclock_img_write(vdd); +#endif + } + + vdd->self_disp.on = true; + + LCD_INFO("--\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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + /* self display off */ + ss_send_cmd(vdd, TX_SELF_DISP_OFF); + + self_mask_on(vdd, true); + + 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; + LCD_INFO("--\n"); + + return ret; +} + +/* + * 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 samsung_display_driver_data *vdd = file->private_data; + void __user *argp = (void __user *)arg; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return -ENODEV; + } + + if (!vdd->self_disp.on) { + LCD_ERR("self_display was turned off\n"); + return -EPERM; + } + + if ((_IOC_TYPE(cmd) != SELF_DISPLAY_IOCTL_MAGIC) || + (_IOC_NR(cmd) >= IOCTL_SELF_MAX)) { + LCD_ERR("TYPE(%u) NR(%u) is wrong..\n", + _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -EINVAL; + } + + LCD_INFO("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" : "IOCTL_ERR"); + + switch (cmd) { + case IOCTL_SELF_MOVE_EN: + self_move_set(vdd, SELF_MOVE_ON); + break; + case IOCTL_SELF_MOVE_OFF: + self_move_set(vdd, SELF_MOVE_OFF); + break; + case IOCTL_SELF_MOVE_RESET: + self_move_set(vdd, SELF_MOVE_RESET); + break; + case IOCTL_SET_ICON: + ret = copy_from_user(&vdd->self_disp.si_info, argp, + sizeof(vdd->self_disp.si_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + self_icon_set(vdd); + break; + case IOCTL_SET_GRID: + ret = copy_from_user(&vdd->self_disp.sg_info, argp, + sizeof(vdd->self_disp.sg_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_grid_set(vdd); + break; + case IOCTL_SET_ANALOG_CLK: + ret = copy_from_user(&vdd->self_disp.sa_info, argp, + sizeof(vdd->self_disp.sa_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_aclock_set(vdd); + break; + case IOCTL_SET_DIGITAL_CLK: + ret = copy_from_user(&vdd->self_disp.sd_info, argp, + sizeof(vdd->self_disp.sd_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_dclock_set(vdd); + break; + case IOCTL_SET_TIME: + ret = copy_from_user(&vdd->self_disp.st_info, argp, + sizeof(vdd->self_disp.st_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_time_set(vdd, false); + break; + 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("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_partial_hlpm_scan_set(vdd); + break; + default: + LCD_ERR("invalid cmd : %u \n", cmd); + break; + } +error: + + 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) +{ + struct samsung_display_driver_data *vdd = file->private_data; + char op_buf[IMAGE_HEADER_SIZE]; + u32 op = 0; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if (unlikely(!buf)) { + LCD_ERR("invalid read buffer\n"); + return -EINVAL; + } + + /* + * get 2byte flas to distinguish what operation is passing + */ + ret = copy_from_user(op_buf, buf, IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (header)\n"); + return ret; + } + + LCD_INFO("Header Buffer = %c%c\n", op_buf[0], op_buf[1]); + + if (op_buf[0] == 'I' && op_buf[1] == 'C') + op = FLAG_SELF_ICON; + else if (op_buf[0] == 'A' && op_buf[1] == 'C') + op = FLAG_SELF_ACLK; + else if (op_buf[0] == 'D' && op_buf[1] == 'C') + op = FLAG_SELF_DCLK; + else { + LCD_ERR("Invalid Header, (%c%c)\n", op_buf[0], op_buf[1]); + return -EINVAL; + } + + LCD_INFO("flag (%d) \n", op); + + if (op >= FLAG_SELF_DISP_MAX) { + LCD_ERR("invalid data flag : %d \n", op); + return -EINVAL; + } + + if (count > vdd->self_disp.operation[op].img_size+IMAGE_HEADER_SIZE) { + LCD_ERR("Buffer OverFlow Detected!! Buffer_Size(%d) Write_Size(%d)\n", + vdd->self_disp.operation[op].img_size, (int)count); + return -EINVAL; + } + + vdd->self_disp.operation[op].wpos = *ppos; + vdd->self_disp.operation[op].wsize = count; + + ret = copy_from_user(vdd->self_disp.operation[op].img_buf, buf+IMAGE_HEADER_SIZE, count-IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (data)\n"); + return ret; + } + + switch (op) { + case FLAG_SELF_MOVE: + // MOVE has no image data.. + break; + case FLAG_SELF_MASK: + make_self_dispaly_img_cmds_HA9(vdd, TX_SELF_MASK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + break; + case FLAG_SELF_ICON: + make_self_dispaly_img_cmds_HA9(vdd, TX_SELF_ICON_IMAGE, op); + vdd->self_disp.operation[op].select = true; + break; + case FLAG_SELF_GRID: + // GRID has no image data.. + break; + case FLAG_SELF_ACLK: + make_self_dispaly_img_cmds_HA9(vdd, TX_SELF_ACLOCK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + vdd->self_disp.operation[FLAG_SELF_DCLK].select = false; + break; + case FLAG_SELF_DCLK: + make_self_dispaly_img_cmds_HA9(vdd, TX_SELF_DCLOCK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + vdd->self_disp.operation[FLAG_SELF_ACLK].select = false; + break; + default: + LCD_ERR("invalid data flag %d \n", op); + break; + } + + return ret; +} + +static int self_display_open(struct inode *inode, struct file *file) +{ + /* TODO: get appropriate vdd..primary or secondary... */ + struct samsung_display_driver_data *vdd = ss_get_vdd(PRIMARY_DISPLAY_NDX); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 1; + file->private_data = vdd; + + LCD_DEBUG("[open]\n"); + + return 0; +} + +static int self_display_release(struct inode *inode, struct file *file) +{ + struct samsung_display_driver_data *vdd = file->private_data; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 0; + + LCD_DEBUG("[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, +}; + +int self_display_init_HA9(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + char devname[15]; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("Self Display is not supported\n"); + return -EINVAL; + } + + mutex_init(&vdd->self_disp.vdd_self_move_lock); + mutex_init(&vdd->self_disp.vdd_self_mask_lock); + mutex_init(&vdd->self_disp.vdd_self_aclock_lock); + mutex_init(&vdd->self_disp.vdd_self_dclock_lock); + mutex_init(&vdd->self_disp.vdd_self_icon_grid_lock); + + if (vdd->ndx == PRIMARY_DISPLAY_NDX) + sprintf(devname, "self_display"); + else + sprintf(devname, "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 = NULL; + + 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_move_set = self_move_set; + vdd->self_disp.self_icon_set = self_icon_set; + vdd->self_disp.self_aclock_set = self_aclock_set; + vdd->self_disp.self_dclock_set = self_dclock_set; + vdd->self_disp.self_time_set = self_time_set; + vdd->self_disp.self_partial_hlpm_scan_set = self_partial_hlpm_scan_set; + vdd->self_disp.self_blinking_on = self_blinking_on; + vdd->self_disp.self_display_debug = self_display_debug; + + ret = misc_register(&vdd->self_disp.dev); + if (ret) { + LCD_ERR("failed to register driver : %d\n", ret); + vdd->self_disp.is_support = false; + return -ENODEV; + } + + LCD_INFO("Success to register self_disp device..(%d)\n", ret); + + return ret; +} + +MODULE_DESCRIPTION("Self Display driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA9.h b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA9.h new file mode 100755 index 000000000000..0eec4035d712 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_HA9.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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 __SELF_DISPLAY_HA9_H__ +#define __SELF_DISPLAY_HA9_H__ + +#include +#include +#include +#include + +int self_display_init_HA9(struct samsung_display_driver_data *vdd); +void make_self_dispaly_img_cmds_HA9(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op); + +#define AC_HH_MASK_ST_X 0x0F +#define AC_HH_MASK_ST_Y 0x07 +#define AC_HH_MASK_ED_X 0x85 +#define AC_HH_MASK_ED_Y 0x1F +#define AC_MM_MASK_ST_X 0x0F +#define AC_MM_MASK_ST_Y 0x07 +#define AC_MM_MASK_ED_X 0xA6 +#define AC_MM_MASK_ED_Y 0x20 +#define AC_SS_MASK_ST_X 0x01 +#define AC_SS_MASK_ST_Y 0x00 +#define AC_SS_MASK_ED_X 0xB2 +#define AC_SS_MASK_ED_Y 0x28 + +#define AC_HH_MEM_REUSE_X 0 +#define AC_HH_MEM_REUSE_Y 0 +#define AC_HH_MEM_REUSE_W 640 +#define AC_HH_MEM_REUSE_H 640 + +#endif // __SELF_DISPLAY_HA9_H__ diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_XA0.c b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_XA0.c new file mode 100755 index 000000000000..98d7ac89de7c --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_XA0.c @@ -0,0 +1,1291 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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_XA0.h" + +/* #define SELF_DISPLAY_TEST */ + +/* + * make dsi_panel_cmds using image data + */ +void make_self_dispaly_img_cmds_XA0(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op) +{ + struct dsi_cmd_desc *tcmds; + struct dsi_panel_cmd_set *pcmds; + + u32 data_size = vdd->self_disp.operation[op].img_size; + char *data = vdd->self_disp.operation[op].img_buf; + int i, j; + int data_idx = 0; + + u32 p_size = CMD_ALIGN; + u32 paylod_size = 0; + u32 cmd_size = 0; + + if (!data) { + LCD_ERR("data is null..\n"); + return; + } + + if (!data_size) { + LCD_ERR("data size is zero..\n"); + return; + } + + /* msg.tx_buf size */ + while (p_size < MAX_PAYLOAD_SIZE) { + if (data_size % p_size == 0) { + paylod_size = p_size; + } + p_size += CMD_ALIGN; + } + /* cmd size */ + cmd_size = data_size / paylod_size; + + LCD_INFO("[%d] total data size [%d]\n", cmd, data_size); + LCD_INFO("cmd size [%d] msg.tx_buf size [%d]\n", cmd_size, paylod_size); + + pcmds = ss_get_cmds(vdd, cmd); + if (IS_ERR_OR_NULL(pcmds->cmds)) { + LCD_ERR("pcmds->cmds is null!!\n"); + pcmds->cmds = kzalloc(cmd_size * sizeof(struct dsi_cmd_desc), GFP_KERNEL); + if (IS_ERR_OR_NULL(pcmds->cmds)) { + LCD_ERR("fail to kzalloc for self_mask cmds \n"); + return; + } + } + + pcmds->state = DSI_CMD_SET_STATE_HS; + pcmds->count = cmd_size; + + tcmds = pcmds->cmds; + if (tcmds == NULL) { + LCD_ERR("tcmds is NULL \n"); + return; + } + + for (i = 0; i < pcmds->count; i++) { + tcmds[i].msg.type = MIPI_DSI_GENERIC_LONG_WRITE; + tcmds[i].last_command = 1; + + /* fill image data */ + if (tcmds[i].msg.tx_buf == NULL) { + /* +1 means HEADER TYPE 0x4C or 0x5C */ + tcmds[i].msg.tx_buf = kzalloc(paylod_size + 1, GFP_KERNEL); + if (tcmds[i].msg.tx_buf == NULL) { + LCD_ERR("fail to kzalloc for self_mask cmds msg.tx_buf \n"); + return; + } + } + + tcmds[i].msg.tx_buf[0] = (i == 0) ? 0x4C : 0x5C; + + for (j = 1; (j <= paylod_size) && (data_idx < data_size); j++) + tcmds[i].msg.tx_buf[j] = data[data_idx++]; + + tcmds[i].msg.tx_len = j; + + LCD_DEBUG("dlen (%d), data_idx (%d)\n", j, data_idx); + } + + return; +} + +static int self_time_set(struct samsung_display_driver_data *vdd, int from_self_move) +{ + u8 *cmd_pload; + struct self_time_info st_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + st_info = vdd->self_disp.st_info; + + LCD_INFO("Self Time Set h(%d):m(%d):s(%d).ms(%d) / 24h(%d) / Interval(%d) / Time Set(%d)\n", + st_info.cur_h, st_info.cur_m, st_info.cur_s, + st_info.cur_ms, st_info.disp_24h, st_info.interval, vdd->self_disp.time_set); + + pcmds = ss_get_cmds(vdd, TX_SELF_TIME_SET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_TIME_SET..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + cmd_pload[2] = 0x03; /* SC_TIME_EN | SC_T_DISP_ON */ + + cmd_pload[3] = vdd->self_disp.st_info.cur_h & 0x1F; + cmd_pload[4] = vdd->self_disp.st_info.cur_m & 0x3F; + cmd_pload[5] = vdd->self_disp.st_info.cur_s & 0x3F; + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + cmd_pload[7] = 0x03; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x10; + else + cmd_pload[8] = 0x00; + break; + case INTERVAL_200: + cmd_pload[7] = 0x06; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x11; + else + cmd_pload[8] = 0x01; + break; + case INTERVAL_500: + cmd_pload[7] = 0x0f; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x12; + else + cmd_pload[8] = 0x02; + break; + case INTERVAL_1000: + cmd_pload[7] = 0x1e; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x13; + else + cmd_pload[8] = 0x03; + break; + case INTERVAL_DEBUG: + cmd_pload[7] = 0x01; + cmd_pload[8] = 0x03; + break; + default: /* Default Interval is 1000 */ + cmd_pload[7] = 0x1e; + if (vdd->self_disp.time_set) + cmd_pload[8] = 0x13; + else + cmd_pload[8] = 0x03; + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + + if (from_self_move) { + LCD_INFO("Self Move Without Any Clock Enabled\n"); + cmd_pload[1] = cmd_pload[3] = cmd_pload[4] = cmd_pload[5] = 0x00; + cmd_pload[6] = cmd_pload[8] = cmd_pload[9] = cmd_pload[10] = 0x00; + cmd_pload[2] = 0x01; /* SC_TIME_EN */ + cmd_pload[7] = 0x03; /* SC_UPDATE_RATE(3) */ + } + + ss_send_cmd(vdd, TX_SELF_TIME_SET); + + vdd->self_disp.time_set = true; + + LCD_ERR("--\n"); + + return 0; +} + +static void self_move_set(struct samsung_display_driver_data *vdd, int ctrl) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ Control(%s), Interval(%d)\n", ctrl == SELF_MOVE_ON ? "SELF_MOVE_ON" : + ctrl == SELF_MOVE_RESET ? "SELF_MOVE_RESET" : + ctrl == SELF_MOVE_OFF ? "SELF_MOVE_OFF" : "Unknown Self Move" + , vdd->self_disp.st_info.interval); + + mutex_lock(&vdd->self_disp.vdd_self_move_lock); + + switch (ctrl) { + case SELF_MOVE_ON: + if (!vdd->self_disp.sa_info.en && !vdd->self_disp.sd_info.en) + self_time_set(vdd, true); + + switch (vdd->self_disp.st_info.interval) { + case INTERVAL_100: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_100); + break; + case INTERVAL_200: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_200); + break; + case INTERVAL_500: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_500); + break; + case INTERVAL_1000: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + break; + case INTERVAL_DEBUG: + ss_send_cmd(vdd, TX_SELF_MOVE_ON_DEBUG); + break; + default: /* Default Interval is 1000 */ + ss_send_cmd(vdd, TX_SELF_MOVE_ON_1000); + LCD_ERR("Invalid Time Interval (%d)\n", vdd->self_disp.st_info.interval); + } + break; + case SELF_MOVE_RESET: + ss_send_cmd(vdd, TX_SELF_MOVE_RESET); + break; + case SELF_MOVE_OFF: + ss_send_cmd(vdd, TX_SELF_MOVE_OFF); + break; + default: + LCD_ERR("Invalid Self Move Control (%d)\n", ctrl); + } + + mutex_unlock(&vdd->self_disp.vdd_self_move_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_icon_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ICON_SET_PRE); + ss_send_cmd(vdd, TX_SELF_ICON_IMAGE); + ss_send_cmd(vdd, TX_SELF_ICON_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static int self_icon_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_icon_info si_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + si_info = vdd->self_disp.si_info; + + LCD_INFO("Self Icon Enable(%d), x(%d), y(%d), w(%d), h(%d), Color(0x%x)\n", + si_info.en, si_info.pos_x, si_info.pos_y, + si_info.width, si_info.height, si_info.color); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = si_info.pos_x & 0xFF; + + cmd_pload[5] = (si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = si_info.pos_y & 0xFF; + + cmd_pload[7] = (si_info.width & 0x700) >> 8; + cmd_pload[8] = si_info.width & 0xFF; + + cmd_pload[9] = (si_info.height & 0xF00) >> 8; + cmd_pload[10] = si_info.height & 0xFF; + + cmd_pload[11] = (si_info.color & 0xFF000000) >> 24; /* A */ + cmd_pload[12] = (si_info.color & 0xFF0000) >> 16; /* R */ + cmd_pload[13] = (si_info.color & 0xFF00) >> 8; /* G */ + cmd_pload[14] = (si_info.color & 0xFF); /* B */ + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + ss_send_cmd(vdd, TX_SELF_ICON_ON); + + LCD_ERR("--\n"); + + return 0; +} + +static int self_grid_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_grid_info sg_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sg_info = vdd->self_disp.sg_info; + + LCD_INFO("Self Grid Enable(%d), s_x(%d), s_y(%d), e_x(%d), e_y(%d)\n", + sg_info.en, sg_info.s_pos_x, sg_info.s_pos_y, + sg_info.e_pos_x, sg_info.e_pos_y); + + pcmds = ss_get_cmds(vdd, TX_SELF_ICON_GRID); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ICON_GRID..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (sg_info.en) { + cmd_pload[2] |= BIT(4); /* SG_GRID_EN */ + + cmd_pload[3] = (sg_info.s_pos_x & 0x700) >> 8; + cmd_pload[4] = sg_info.s_pos_x & 0xFF; + + cmd_pload[5] = (sg_info.s_pos_y & 0xF00) >> 8; + cmd_pload[6] = sg_info.s_pos_y & 0xFF; + + cmd_pload[7] = (sg_info.e_pos_x & 0x700) >> 8; + cmd_pload[8] = sg_info.e_pos_x & 0xFF; + + cmd_pload[9] = (sg_info.e_pos_y & 0xF00) >> 8; + cmd_pload[10] = sg_info.e_pos_y & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); /* SG_GRID_EN */ + } + + if (vdd->self_disp.si_info.en) { + cmd_pload[2] |= BIT(0); /* SI_ICON_EN */ + + cmd_pload[3] = (vdd->self_disp.si_info.pos_x & 0x700) >> 8; + cmd_pload[4] = vdd->self_disp.si_info.pos_x & 0xFF; + + cmd_pload[5] = (vdd->self_disp.si_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = vdd->self_disp.si_info.pos_y & 0xFF; + + cmd_pload[7] = (vdd->self_disp.si_info.width & 0x700) >> 8; + cmd_pload[8] = vdd->self_disp.si_info.width & 0xFF; + + cmd_pload[9] = (vdd->self_disp.si_info.height & 0xF00) >> 8; + cmd_pload[10] = vdd->self_disp.si_info.height & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(0)); /* SI_ICON_EN */ + } + + ss_send_cmd(vdd, TX_SELF_ICON_GRID); + + LCD_ERR("--\n"); + + return 0; +} +static void self_aclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_ACLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_ACLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_aclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_aclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_SET_PRE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_IMAGE); + ss_send_cmd(vdd, TX_SELF_ACLOCK_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_aclock_lock); + LCD_ERR("--\n"); +} + +static int self_aclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + u32 mem_reuse_x, mem_reuse_y; + struct self_analog_clk_info sa_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sa_info = vdd->self_disp.sa_info; + + LCD_INFO("Self Analog Clock Enable(%d), x(%d), y(%d), rot(%d), mem_mask_en(%d), mem_reuse_en(%d)\n", + sa_info.en, sa_info.pos_x, sa_info.pos_y, sa_info.rotate, sa_info.mem_mask_en, sa_info.mem_reuse_en); + + if (!sa_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_ACLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_ACLOCK_ON..\n"); + return -ENODEV; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* Self Analog Clock Position Update */ + cmd_pload[3] = (sa_info.pos_x & 0x700) >> 8; + cmd_pload[4] = sa_info.pos_x & 0xFF; + cmd_pload[5] = (sa_info.pos_y & 0xF00) >> 8; + cmd_pload[6] = sa_info.pos_y & 0xFF; + + /* Self Analog Clock Rotation Setting */ + cmd_pload[7] &= ~(BIT(0)); /* BIT_0 Clear */ + cmd_pload[7] &= ~(BIT(1)); /* BIT_1 Clear */ + + switch (sa_info.rotate) { + case ROTATE_0: + cmd_pload[7] &= ~(BIT(0)); + cmd_pload[7] &= ~(BIT(1)); + break; + case ROTATE_90: + cmd_pload[7] |= BIT(0); + cmd_pload[7] &= ~(BIT(1)); + break; + case ROTATE_180: + cmd_pload[7] &= ~(BIT(0)); + cmd_pload[7] |= BIT(1); + break; + case ROTATE_270: + cmd_pload[7] |= BIT(0); + cmd_pload[7] |= BIT(1); + break; + default: + LCD_ERR("Invalid Rotation Setting, (%d)\n", sa_info.rotate); + } + + /* Clock Memory Mask for Power Saving */ + if (sa_info.mem_mask_en) { + cmd_pload[15] = AC_HH_MASK_ST_X & 0xFF; + cmd_pload[16] = AC_HH_MASK_ST_Y & 0x3F; + cmd_pload[17] = AC_HH_MASK_ED_X & 0xFF; + cmd_pload[18] = AC_HH_MASK_ED_Y & 0x3F; + cmd_pload[19] = AC_MM_MASK_ST_X & 0xFF; + cmd_pload[20] = AC_MM_MASK_ST_Y & 0x3F; + cmd_pload[21] = AC_MM_MASK_ED_X & 0xFF; + cmd_pload[22] = AC_MM_MASK_ED_Y & 0x3F; + cmd_pload[23] = AC_SS_MASK_ST_X & 0xFF; + cmd_pload[24] = AC_SS_MASK_ST_Y & 0x3F; + cmd_pload[25] = AC_SS_MASK_ED_X & 0xFF; + cmd_pload[26] = AC_SS_MASK_ED_Y & 0x3F; + } else { + /* Memory Mask does not have enable register, All the value should be zero incase of disable */ + cmd_pload[15] = cmd_pload[16] = cmd_pload[17] = cmd_pload[18] = cmd_pload[19] = cmd_pload[20] = 0x00; + cmd_pload[21] = cmd_pload[22] = cmd_pload[23] = cmd_pload[24] = cmd_pload[25] = cmd_pload[26] = 0x00; + } + + /* Clock Memory Reuse for Power Saving */ + if ((sa_info.pos_x - (AC_HH_MEM_REUSE_W/2)) >= 0) + mem_reuse_x = sa_info.pos_x - (AC_HH_MEM_REUSE_W/2); + else + mem_reuse_x = 0; + + if ((sa_info.pos_y - (AC_HH_MEM_REUSE_H/2)) >= 0) + mem_reuse_y = sa_info.pos_x - (AC_HH_MEM_REUSE_H/2); + else + mem_reuse_y = 0; + + if (sa_info.mem_reuse_en) { + cmd_pload[2] |= BIT(4); /* SC_MEM_DISP_ON */ + cmd_pload[27] = (mem_reuse_x & 0x700) >> 8; + cmd_pload[28] = mem_reuse_x & 0xFF; + cmd_pload[29] = (mem_reuse_y & 0xF00) >> 8; + cmd_pload[30] = mem_reuse_y & 0xFF; + cmd_pload[31] = (AC_HH_MEM_REUSE_W & 0x700) >> 8; + cmd_pload[32] = AC_HH_MEM_REUSE_W & 0xFF; + cmd_pload[33] = (AC_HH_MEM_REUSE_H & 0xF00) >> 8; + cmd_pload[34] = AC_HH_MEM_REUSE_H & 0xFF; + } else { + cmd_pload[2] &= ~(BIT(4)); /* SC_MEM_DISP_ON */ + } + +skip_update: + self_aclock_on(vdd, sa_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_dclock_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) + ss_send_cmd(vdd, TX_SELF_DCLOCK_ON); + else + ss_send_cmd(vdd, TX_SELF_DCLOCK_HIDE); + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_dclock_img_write(struct samsung_display_driver_data *vdd) +{ + LCD_ERR("++\n"); + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_SET_PRE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_IMAGE); + ss_send_cmd(vdd, TX_SELF_DCLOCK_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + LCD_ERR("--\n"); +} + +static int self_dclock_set(struct samsung_display_driver_data *vdd) +{ + u8 *cmd_pload; + struct self_digital_clk_info sd_info; + struct dsi_panel_cmd_set *pcmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sd_info = vdd->self_disp.sd_info; + + LCD_INFO("Self Digital Clock Enable(%d), 24H(%d), EN_HH(%d), EN_MM(%d), POS_1(%d,%d), POS_2(%d,%d), POS_3(%d,%d), POS_4(%d,%d), W(%d), H(%d), Color(0x%x), UNI_ATTR(%d), UNI_W(%d)\n", + sd_info.en, vdd->self_disp.st_info.disp_24h, + sd_info.en_hh, sd_info.en_mm, + sd_info.pos1_x, sd_info.pos1_y, + sd_info.pos2_x, sd_info.pos2_y, + sd_info.pos3_x, sd_info.pos3_y, + sd_info.pos4_x, sd_info.pos4_y, + sd_info.img_width, sd_info.img_height, + sd_info.color, sd_info.unicode_attr, sd_info.unicode_width); + + if (!sd_info.en) + goto skip_update; + + pcmds = ss_get_cmds(vdd, TX_SELF_DCLOCK_ON); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_DCLOCK_ON..\n"); + return -ENODEV; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + if (vdd->self_disp.st_info.disp_24h) { + cmd_pload[4] |= vdd->self_disp.st_info.disp_24h & 0x30; + } else { + cmd_pload[4] &= ~(BIT(4)); + cmd_pload[4] &= ~(BIT(5)); + } + + if (sd_info.en_hh) { + /* Clear EN_HH First */ + cmd_pload[4] &= ~(BIT(2)); + cmd_pload[4] &= ~(BIT(3)); + cmd_pload[4] |= (sd_info.en_hh & 0x3) << 2; + } else { + cmd_pload[4] &= ~(BIT(2)); + cmd_pload[4] &= ~(BIT(3)); + } + + if (sd_info.en_mm) { + /* Clear EN_MM First */ + cmd_pload[4] &= ~(BIT(0)); + cmd_pload[4] &= ~(BIT(1)); + cmd_pload[4] |= (sd_info.en_mm & 0x3); + } else { + cmd_pload[4] &= ~(BIT(0)); + cmd_pload[4] &= ~(BIT(1)); + } + + cmd_pload[5] = (sd_info.pos1_x & 0x700) >> 8; + cmd_pload[6] = sd_info.pos1_x & 0xFF; + cmd_pload[7] = (sd_info.pos1_y & 0xF00) >> 8; + cmd_pload[8] = sd_info.pos1_y & 0xFF; + + cmd_pload[9] = (sd_info.pos2_x & 0x700) >> 8; + cmd_pload[10] = sd_info.pos2_x & 0xFF; + cmd_pload[11] = (sd_info.pos2_y & 0xF00) >> 8; + cmd_pload[12] = sd_info.pos2_y & 0xFF; + + cmd_pload[13] = (sd_info.pos3_x & 0x700) >> 8; + cmd_pload[14] = sd_info.pos3_x & 0xFF; + cmd_pload[15] = (sd_info.pos3_y & 0xF00) >> 8; + cmd_pload[16] = sd_info.pos3_y & 0xFF; + + cmd_pload[17] = (sd_info.pos4_x & 0x700) >> 8; + cmd_pload[18] = sd_info.pos4_x & 0xFF; + cmd_pload[19] = (sd_info.pos4_y & 0xF00) >> 8; + cmd_pload[20] = sd_info.pos4_y & 0xFF; + + cmd_pload[21] = (sd_info.img_width & 0x700) >> 8; + cmd_pload[22] = sd_info.img_width & 0xFF; + cmd_pload[23] = (sd_info.img_height & 0xF00) >> 8; + cmd_pload[24] = sd_info.img_height & 0xFF; + + /* Color */ + cmd_pload[25] = (sd_info.color & 0xF000) >> 12; /* A */ + cmd_pload[26] = (sd_info.color & 0xF00) >> 8; /* R */ + cmd_pload[27] = (sd_info.color & 0xF0) >> 4; /* G */ + cmd_pload[28] = (sd_info.color & 0xF); /* B */ + + /* Unicode */ + cmd_pload[3] = sd_info.unicode_attr & 0xFF; + + switch (sd_info.unicode_attr) { + case 0x02: + default: + cmd_pload[31] = 0; + cmd_pload[32] = 0; + cmd_pload[33] = 0; + cmd_pload[34] = 0; + cmd_pload[35] = 0; + } + + cmd_pload[29] = ((sd_info.unicode_width) & 0x700) >> 8; + cmd_pload[30] = sd_info.unicode_width & 0xFF; + +skip_update: + self_dclock_on(vdd, sd_info.en); + + LCD_ERR("-- \n"); + + return 0; +} + +static void self_blinking_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_dclock_lock); + + if (enable) { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_ON); + } else { + ss_send_cmd(vdd, TX_SELF_DCLOCK_BLINKING_OFF); + } + + mutex_unlock(&vdd->self_disp.vdd_self_dclock_lock); + + LCD_ERR("-- \n"); + + return; +} + +static void self_mask_img_write(struct samsung_display_driver_data *vdd) +{ + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++\n"); + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + ss_send_cmd(vdd, TX_SELF_MASK_SET_PRE); + ss_send_cmd(vdd, TX_SELF_MASK_IMAGE); + ss_send_cmd(vdd, TX_SELF_MASK_SET_POST); + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + LCD_ERR("--\n"); +} + +static void self_mask_on(struct samsung_display_driver_data *vdd, int enable) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return; + } + + LCD_ERR("++ (%d)\n", enable); + + mutex_lock(&vdd->self_disp.vdd_self_mask_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_mask_lock); + + LCD_ERR("-- \n"); + + return; +} + +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 is null or error\n"); + return -ENODEV; + } + + LCD_ERR("++\n"); + + sphs_info = vdd->self_disp.sphs_info; + + LCD_INFO("Self Partial HLPM/Scan hlpm_En(%d), hlpm_mode_sel(0x%x), hlpm_a1(%d), hlpm_a2(%d),\ + hlpm_a3(%d), hlpm_a4(%d) / scan_en(%d), scan_line(%d_%d)\n", + sphs_info.hlpm_en, sphs_info.hlpm_mode_sel, + sphs_info.hlpm_area_1, sphs_info.hlpm_area_2, + sphs_info.hlpm_area_3, sphs_info.hlpm_area_4, + sphs_info.scan_en, sphs_info.scan_sl, sphs_info.scan_el); + + pcmds = ss_get_cmds(vdd, TX_SELF_PARTIAL_HLPM_SCAN_SET); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_SELF_PARTIAL_HLPM_SCAN_SET..\n"); + return -ENODEV; + } + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* Partial HLPM */ + if (sphs_info.hlpm_en) { + cmd_pload[1] |= BIT(0); /* SP_PTL_EN */ + cmd_pload[2] = sphs_info.hlpm_mode_sel & 0x1F; /* SP_PTL_MODE_SEL */ + + /* Area_1 */ + cmd_pload[9] = (sphs_info.hlpm_area_1 & 0xF00) >> 8; + cmd_pload[10] = sphs_info.hlpm_area_1 & 0xFF; + /* Area_2 */ + cmd_pload[11] = (sphs_info.hlpm_area_2 & 0xF00) >> 8; + cmd_pload[12] = sphs_info.hlpm_area_2 & 0xFF; + /* Area_3 */ + cmd_pload[13] = (sphs_info.hlpm_area_3 & 0xF00) >> 8; + cmd_pload[14] = sphs_info.hlpm_area_3 & 0xFF; + /* Area_4 */ + cmd_pload[15] = (sphs_info.hlpm_area_4 & 0xF00) >> 8; + cmd_pload[16] = sphs_info.hlpm_area_4 & 0xFF; + } else { + cmd_pload[1] &= ~(BIT(0)); /* SP_PTL_EN */ + } + + /* Partial Scan */ + if (sphs_info.scan_en) { + cmd_pload[1] |= BIT(4); /* SP_PTLSCAN_EN */ + + cmd_pload[5] = (sphs_info.scan_sl & 0xF00) >> 8; + cmd_pload[6] = sphs_info.scan_sl & 0xFF; + cmd_pload[7] = (sphs_info.scan_el & 0xF00) >> 8; + cmd_pload[8] = sphs_info.scan_el & 0xFF; + } else { + cmd_pload[1] &= ~(BIT(4)); /* SP_PTLSCAN_EN */ + } + + ss_send_cmd(vdd, TX_SELF_PARTIAL_HLPM_SCAN_SET); + + LCD_ERR("--\n"); + + return 0; +} + +static int self_display_debug(struct samsung_display_driver_data *vdd) +{ + char buf[32]; + + if (ss_get_cmds(vdd, RX_SELF_DISP_DEBUG)->count) { + + ss_panel_data_read(vdd, RX_SELF_DISP_DEBUG, buf, LEVEL1_KEY); + + vdd->self_disp.debug.SI_X_O = ((buf[14] & 0x07) << 8); + vdd->self_disp.debug.SI_X_O |= (buf[15] & 0xFF); + + vdd->self_disp.debug.SI_Y_O = ((buf[16] & 0x0F) << 8); + vdd->self_disp.debug.SI_Y_O |= (buf[17] & 0xFF); + + vdd->self_disp.debug.SM_SUM_O = ((buf[6] & 0xFF) << 24); + vdd->self_disp.debug.SM_SUM_O |= ((buf[7] & 0xFF) << 16); + vdd->self_disp.debug.SM_SUM_O |= ((buf[8] & 0xFF) << 8); + vdd->self_disp.debug.SM_SUM_O |= (buf[9] & 0xFF); + + vdd->self_disp.debug.MEM_SUM_O = ((buf[10] & 0xFF) << 24); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[11] & 0xFF) << 16); + vdd->self_disp.debug.MEM_SUM_O |= ((buf[12] & 0xFF) << 8); + vdd->self_disp.debug.MEM_SUM_O |= (buf[13] & 0xFF); + + LCD_INFO("SI_X_O(%u) SI_Y_O(%u) MEM_SUM_O(%X) SM_SUM_O(%X)\n", + vdd->self_disp.debug.SI_X_O, + vdd->self_disp.debug.SI_Y_O, + vdd->self_disp.debug.MEM_SUM_O, + vdd->self_disp.debug.SM_SUM_O); + + if (vdd->self_disp.operation[FLAG_SELF_MASK].img_checksum != + vdd->self_disp.debug.SM_SUM_O) { + LCD_ERR("self mask img checksum fail!!\n"); + return -1; + } + } + + 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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + if (!vdd->self_disp.on) { + /* Self Icon */ + self_icon_img_write(vdd); + + if (vdd->self_disp.operation[FLAG_SELF_ACLK].select) + self_aclock_img_write(vdd); + if (vdd->self_disp.operation[FLAG_SELF_DCLK].select) + self_dclock_img_write(vdd); + + /* Self display on */ + ss_send_cmd(vdd, TX_SELF_DISP_ON); + + self_mask_on(vdd, false); +#ifdef SELF_DISPLAY_TEST + //self_dclock_img_write(vdd); + self_aclock_img_write(vdd); +#endif + } + + vdd->self_disp.on = true; + + LCD_INFO("--\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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_DEBUG("self display is not supported..(%d) \n", + vdd->self_disp.is_support); + return -ENODEV; + } + + LCD_INFO("++\n"); + + /* self display off */ + ss_send_cmd(vdd, TX_SELF_DISP_OFF); + + self_mask_on(vdd, true); + + 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; + LCD_INFO("--\n"); + + return ret; +} + +/* + * 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 is null or error\n"); + return -ENODEV; + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return -ENODEV; + } + + if (!vdd->self_disp.on) { + LCD_ERR("self_display was turned off\n"); + return -EPERM; + } + + if ((_IOC_TYPE(cmd) != SELF_DISPLAY_IOCTL_MAGIC) || + (_IOC_NR(cmd) >= IOCTL_SELF_MAX)) { + LCD_ERR("TYPE(%u) NR(%u) is wrong..\n", + _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -EINVAL; + } + + LCD_INFO("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" : "IOCTL_ERR"); + + switch (cmd) { + case IOCTL_SELF_MOVE_EN: + self_move_set(vdd, SELF_MOVE_ON); + break; + case IOCTL_SELF_MOVE_OFF: + self_move_set(vdd, SELF_MOVE_OFF); + break; + case IOCTL_SELF_MOVE_RESET: + self_move_set(vdd, SELF_MOVE_RESET); + break; + case IOCTL_SET_ICON: + ret = copy_from_user(&vdd->self_disp.si_info, argp, + sizeof(vdd->self_disp.si_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + self_icon_set(vdd); + break; + case IOCTL_SET_GRID: + ret = copy_from_user(&vdd->self_disp.sg_info, argp, + sizeof(vdd->self_disp.sg_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_grid_set(vdd); + break; + case IOCTL_SET_ANALOG_CLK: + ret = copy_from_user(&vdd->self_disp.sa_info, argp, + sizeof(vdd->self_disp.sa_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_aclock_set(vdd); + break; + case IOCTL_SET_DIGITAL_CLK: + ret = copy_from_user(&vdd->self_disp.sd_info, argp, + sizeof(vdd->self_disp.sd_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_dclock_set(vdd); + break; + case IOCTL_SET_TIME: + ret = copy_from_user(&vdd->self_disp.st_info, argp, + sizeof(vdd->self_disp.st_info)); + if (ret) { + LCD_ERR("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_time_set(vdd, false); + break; + 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("fail to copy_from_user.. (%d)\n", ret); + goto error; + } + + ret = self_partial_hlpm_scan_set(vdd); + break; + default: + LCD_ERR("invalid cmd : %u \n", cmd); + break; + } +error: + + 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) +{ + 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; + + char op_buf[IMAGE_HEADER_SIZE]; + u32 op = 0; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if (unlikely(!buf)) { + LCD_ERR("invalid read buffer\n"); + return -EINVAL; + } + + /* + * get 2byte flas to distinguish what operation is passing + */ + ret = copy_from_user(op_buf, buf, IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (header)\n"); + return ret; + } + + LCD_INFO("Header Buffer = %c%c\n", op_buf[0], op_buf[1]); + + if (op_buf[0] == 'I' && op_buf[1] == 'C') + op = FLAG_SELF_ICON; + else if (op_buf[0] == 'A' && op_buf[1] == 'C') + op = FLAG_SELF_ACLK; + else if (op_buf[0] == 'D' && op_buf[1] == 'C') + op = FLAG_SELF_DCLK; + else { + LCD_ERR("Invalid Header, (%c%c)\n", op_buf[0], op_buf[1]); + return -EINVAL; + } + + LCD_INFO("flag (%d) \n", op); + + if (op >= FLAG_SELF_DISP_MAX) { + LCD_ERR("invalid data flag : %d \n", op); + return -EINVAL; + } + + if (count > vdd->self_disp.operation[op].img_size+IMAGE_HEADER_SIZE) { + LCD_ERR("Buffer OverFlow Detected!! Buffer_Size(%d) Write_Size(%d)\n", + vdd->self_disp.operation[op].img_size, (int)count); + return -EINVAL; + } + + vdd->self_disp.operation[op].wpos = *ppos; + vdd->self_disp.operation[op].wsize = count; + + ret = copy_from_user(vdd->self_disp.operation[op].img_buf, buf+IMAGE_HEADER_SIZE, count-IMAGE_HEADER_SIZE); + if (unlikely(ret < 0)) { + LCD_ERR("failed to copy_from_user (data)\n"); + return ret; + } + + switch (op) { + case FLAG_SELF_MOVE: + // MOVE has no image data.. + break; + case FLAG_SELF_MASK: + make_self_dispaly_img_cmds_XA0(vdd, TX_SELF_MASK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + break; + case FLAG_SELF_ICON: + make_self_dispaly_img_cmds_XA0(vdd, TX_SELF_ICON_IMAGE, op); + vdd->self_disp.operation[op].select = true; + break; + case FLAG_SELF_GRID: + // GRID has no image data.. + break; + case FLAG_SELF_ACLK: + make_self_dispaly_img_cmds_XA0(vdd, TX_SELF_ACLOCK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + vdd->self_disp.operation[FLAG_SELF_DCLK].select = false; + break; + case FLAG_SELF_DCLK: + make_self_dispaly_img_cmds_XA0(vdd, TX_SELF_DCLOCK_IMAGE, op); + vdd->self_disp.operation[op].select = true; + vdd->self_disp.operation[FLAG_SELF_ACLK].select = false; + break; + default: + LCD_ERR("invalid data flag %d \n", op); + break; + } + + return ret; +} + +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 is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 1; + + LCD_DEBUG("[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 is null or error\n"); + return -ENODEV; + } + + vdd->self_disp.file_open = 0; + + LCD_DEBUG("[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, +}; + +int self_display_init_XA0(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + char devname[15]; + + 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 is null or error\n"); + return -ENODEV; + } + + if (!vdd->self_disp.is_support) { + LCD_ERR("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_move_lock); + mutex_init(&vdd->self_disp.vdd_self_mask_lock); + mutex_init(&vdd->self_disp.vdd_self_aclock_lock); + mutex_init(&vdd->self_disp.vdd_self_dclock_lock); + mutex_init(&vdd->self_disp.vdd_self_icon_grid_lock); + + if (vdd->ndx == PRIMARY_DISPLAY_NDX) + sprintf(devname, "self_display"); + else + sprintf(devname, "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.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_move_set = self_move_set; + vdd->self_disp.self_icon_set= self_icon_set; + vdd->self_disp.self_aclock_set= self_aclock_set; + vdd->self_disp.self_dclock_set= self_dclock_set; + vdd->self_disp.self_time_set= self_time_set; + vdd->self_disp.self_partial_hlpm_scan_set = self_partial_hlpm_scan_set; + vdd->self_disp.self_blinking_on = self_blinking_on; + vdd->self_disp.self_display_debug = self_display_debug; + + ret = misc_register(&vdd->self_disp.dev); + if (ret) { + LCD_ERR("failed to register driver : %d\n", ret); + vdd->self_disp.is_support = false; + return -ENODEV; + } + + LCD_INFO("Success to register self_disp device..(%d)\n", ret); + + return ret; +} + +MODULE_DESCRIPTION("Self Display driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_XA0.h b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_XA0.h new file mode 100755 index 000000000000..0373f1019d04 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_XA0.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * DDI operation : self clock, self mask, self icon.. etc. + * Author: QC LCD driver + * + * 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 __SELF_DISPLAY_XA0_H__ +#define __SELF_DISPLAY_XA0_H__ + +#include +#include +#include +#include + +int self_display_init_XA0(struct samsung_display_driver_data *vdd); +void make_self_dispaly_img_cmds_XA0(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, u32 op); + +#define AC_HH_MASK_ST_X 0x0F +#define AC_HH_MASK_ST_Y 0x07 +#define AC_HH_MASK_ED_X 0x85 +#define AC_HH_MASK_ED_Y 0x1F +#define AC_MM_MASK_ST_X 0x0F +#define AC_MM_MASK_ST_Y 0x07 +#define AC_MM_MASK_ED_X 0xA6 +#define AC_MM_MASK_ED_Y 0x20 +#define AC_SS_MASK_ST_X 0x01 +#define AC_SS_MASK_ST_Y 0x00 +#define AC_SS_MASK_ED_X 0xB2 +#define AC_SS_MASK_ED_Y 0x28 + +#define AC_HH_MEM_REUSE_X 0 +#define AC_HH_MEM_REUSE_Y 0 +#define AC_HH_MEM_REUSE_W 640 +#define AC_HH_MEM_REUSE_H 640 + +#endif // __SELF_DISPLAY_HA9_H__ diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7.dtsi b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7.dtsi new file mode 100755 index 000000000000..6714e726145f --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7.dtsi @@ -0,0 +1,459 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_FA7_dtsi: self_display_FA7_dtsi { + label = "self_display_FA7_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 B0 15 + 29 01 00 00 00 00 02 FE 00 /* Move Setting */ + 29 01 00 00 00 00 07 76 09 0F 85 A0 0B 90 + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 /* Self ICON & Grid 2C Sync Off */ + 29 01 00 00 00 00 02 77 01 /* Self Clock 2C Sync Off */ + 29 01 00 00 00 00 04 76 09 0F 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0A 77 + 00 04 00 00 00 + 00 00 03 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on_100 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 22 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 200ms Move Pattern */ + samsung,self_move_on_200 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 55 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 500ms Move Pattern */ + samsung,self_move_on_500 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 1000ms(1Sec) Move Pattern */ + samsung,self_move_on_1000 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 07 + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 07 + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Debug Move Pattern(Only for 1000ms Interval) */ + samsung,self_move_on_debug = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 00 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_reset = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 05 7D 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 00 00 03 7B 00 0C + 29 01 00 00 00 00 02 75 10 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 0B 8F 18 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_mem_setting = [ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 00 00 00 95 + 0A FA 0B 8F 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 0B 90 0C 25 + 0C 26 0C BB 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_mem_setting = [ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_grid = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 07 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 7C + 00 01 02 00 02 + 00 00 34 00 34 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_brightness_icon_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 + 00 00 34 00 34 + 70 1F 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_brightness_icon_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 1F 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_grid_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_sidemem_setting = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 77 + 00 07 00 0A 0A + 1D 00 03 00 02 + 00 02 00 00 00 + 78 13 78 13 78 + 13 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 00 00 + 00 00 03 01 02 + 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 05 + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_sidemem_setting = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 2A 77 + 00 36 00 0A 03 + 00 00 03 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 01 2C 01 90 + 01 F4 01 90 01 + 2C 02 F4 01 F4 + 02 F4 00 C8 01 + 64 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 1B 79 + 00 C0 00 00 00 + 00 57 01 0A 0A + 00 FF 00 00 FF + 00 02 08 06 72 + 02 08 06 D6 1D + 1D + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 36 05 03 1A + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 14 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_clock_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 77 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7_AMB458WJ01.dtsi b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7_AMB458WJ01.dtsi new file mode 100755 index 000000000000..2d19b9632557 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7_AMB458WJ01.dtsi @@ -0,0 +1,406 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_FA7_AMB458WJ01_dtsi: self_display_FA7_AMB458WJ01_dtsi { + label = "self_display_FA7_AMB458WJ01_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 B0 33 /* Global para */ + 29 01 00 00 00 00 02 FE 24 /* Move setting */ + 29 01 00 00 00 00 07 76 08 10 82 D0 06 90 /* SELF_HBP, SELF_VBP */ + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 /* Self ICON Off */ + 29 01 00 00 00 00 02 77 01 /* Self Clock 2C Sync Off */ + 29 01 00 00 00 00 03 79 00 00 /* Self Blink 2C Sync Off */ + 29 01 00 00 00 00 04 76 08 10 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on_100 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 22 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 200ms Move Pattern */ + samsung,self_move_on_200 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 55 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 500ms Move Pattern */ + samsung,self_move_on_500 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 1000ms(1Sec) Move Pattern */ + samsung,self_move_on_1000 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 00 10 00 10 00 10 00 10 00 01 + 00 50 00 50 00 50 00 50 00 05 + 00 10 00 10 00 10 00 10 00 01 + 00 50 00 50 00 50 00 50 00 05 + 00 10 00 10 00 10 00 10 00 01 + 00 50 00 50 00 50 00 50 00 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Debug Move Pattern(Only for 1000ms Interval) */ + samsung,self_move_on_debug = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 00 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 77 + 00 04 00 00 00 + 00 00 03 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 22 00 04 7B 00 00 00 /* Self Video Off 34ms delay */ + 29 01 00 00 00 00 02 75 04 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 0B 8F 18 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_mem_setting = [ + 29 01 00 00 22 00 02 7A 00 /* Self Mask Off 34ms delay */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 00 00 00 63 + 06 2C 06 8F 08 + 0C 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 06 90 06 F3 + 06 F4 07 57 08 + 0C 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_mem_setting = [ + 29 01 00 00 22 00 03 7C 00 00 /* Self ICON Disable 34ms delay */ + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_grid = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 01 00 01 + 00 01 00 01 00 + 70 11 00 00 00 + 00 02 D0 06 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 01 00 01 + 00 01 00 01 00 + 70 11 00 00 00 + 00 02 D0 06 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + samsung,self_icon_on_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 7C + 00 01 01 00 01 + 00 01 00 01 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + samsung,self_icon_off_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 11 00 00 00 + 00 02 D0 06 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_sidemem_setting = [ + 29 01 00 00 22 00 03 77 00 00 /*Self Clock & Self Timer Off 34ms delay */ + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 77 + 00 07 00 0A 0A 1D 00 03 + 00 02 00 02 00 00 00 78 + 13 78 13 78 13 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 0A 0A 1D 00 03 + 00 02 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_sidemem_setting = [ + 29 01 00 00 22 00 03 77 00 00 /* Self Clock & Self Timer Off 34ms delay */ + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 2A 77 + 00 36 05 0A 03 00 00 03 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 64 00 + 64 01 5E 00 64 00 64 02 + 58 01 5E 02 58 00 C8 01 + 64 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 1B 79 + 00 C0 00 00 00 00 57 01 + 0A 0A 00 FF 00 00 FF 00 + 02 08 06 72 02 08 06 D6 + 1D 1D + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 08 77 + 00 36 05 03 1A + 20 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 14 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_clock_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 77 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7_AMB575WD01.dtsi b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7_AMB575WD01.dtsi new file mode 100755 index 000000000000..3479c7d4970c --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_FA7_AMB575WD01.dtsi @@ -0,0 +1,410 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_FA7_AMB575WD01_dtsi: self_display_FA7_AMB575WD01_dtsi { + label = "self_display_FA7_AMB575WD01_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 B0 33 /* Global para */ + 29 01 00 00 00 00 02 FE 24 /* Move Setting */ + 29 01 00 00 00 00 07 76 08 10 84 38 08 E8 + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 /* Self ICON Off */ + 29 01 00 00 00 00 02 77 01 /* Self Clock 2C Sync Off */ + 29 01 00 00 00 00 03 79 00 00 /* Self Blink 2C Sync Off */ + 29 01 00 00 00 00 04 76 08 10 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on_100 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 22 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 200ms Move Pattern */ + samsung,self_move_on_200 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 55 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 500ms Move Pattern */ + samsung,self_move_on_500 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 1000ms(1Sec) Move Pattern */ + samsung,self_move_on_1000 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 00 10 00 10 00 10 00 10 00 01 + 00 50 00 50 00 50 00 50 00 05 + 00 10 00 10 00 10 00 10 00 01 + 00 50 00 50 00 50 00 50 00 05 + 00 10 00 10 00 10 00 10 00 01 + 00 50 00 50 00 50 00 50 00 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Debug Move Pattern(Only for 1000ms Interval) */ + samsung,self_move_on_debug = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 00 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 77 + 00 04 00 00 00 + 00 00 03 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 22 00 04 7B 00 00 00 /* Self Video Off 34ms delay */ + 29 01 00 00 00 00 02 75 04 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 0B 8F 18 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 00 + 05 00 05 00 05 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_mem_setting = [ + 29 01 00 00 22 00 02 7A 00 /* Self Mask Off 34ms delay */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 00 00 00 95 08 52 08 + E7 08 10 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 08 E8 09 7D 09 7E 0A + 13 08 10 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_mem_setting = [ + 29 01 00 00 22 00 03 7C 00 00 /* Self ICON Disable 34ms delay */ + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_grid = [ //need to check + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 07 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 00 01 00 + 01 00 70 11 00 00 00 00 + 05 A0 0B 90 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 7C + 00 01 02 00 02 00 01 00 + 01 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 00 00 00 + 00 00 70 11 00 00 00 00 + 05 A0 0B 90 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_grid_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_sidemem_setting = [ + 29 01 00 00 22 00 03 77 00 00 /*Self Clock & Self Timer Off 34ms delay */ + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 77 + 00 07 00 0A 0A 1D 00 03 + 00 02 00 02 00 00 00 78 + 13 78 13 78 13 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 0A 0A 1D 00 03 + 00 02 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_sidemem_setting = [ + 29 01 00 00 22 00 03 77 00 00 /* Self Clock & Self Timer Off 34ms delay */ + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 2A 77 + 00 36 05 0A 03 00 00 03 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 01 2C 01 + 90 01 F4 01 90 01 2C 02 + F4 02 F4 01 F4 00 C8 01 + 64 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 1B 79 + 00 C0 00 00 00 00 57 01 + 0A 0A 00 FF 00 00 FF 00 + 02 08 06 72 02 08 06 D6 + 1D 1D + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 36 05 03 1A + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 14 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_clock_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 77 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_HA8.dtsi b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_HA8.dtsi new file mode 100755 index 000000000000..bb738d1af8f1 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_HA8.dtsi @@ -0,0 +1,459 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_HA8_dtsi: self_display_HA8_dtsi { + label = "self_display_HA8_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 B0 15 + 29 01 00 00 00 00 02 FE 00 /* Move Setting */ + 29 01 00 00 00 00 07 76 09 0F 85 A0 0B 90 + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 /* Self ICON & Grid 2C Sync Off */ + 29 01 00 00 00 00 02 77 01 /* Self Clock 2C Sync Off */ + 29 01 00 00 00 00 04 76 09 0F 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0A 77 + 00 04 00 00 00 + 00 00 03 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on_100 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 22 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 200ms Move Pattern */ + samsung,self_move_on_200 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 55 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 500ms Move Pattern */ + samsung,self_move_on_500 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 1000ms(1Sec) Move Pattern */ + samsung,self_move_on_1000 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 07 + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 07 + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Debug Move Pattern(Only for 1000ms Interval) */ + samsung,self_move_on_debug = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 00 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_reset = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 05 7D 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 00 00 03 7B 00 0C + 29 01 00 00 00 00 02 75 10 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 0B 8F 18 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_mem_setting = [ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 00 00 00 95 + 0A FA 0B 8F 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 0B 90 0C 25 + 0C 26 0C BB 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_mem_setting = [ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_grid = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 07 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 7C + 00 01 02 00 02 + 00 00 34 00 34 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_brightness_icon_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 + 00 00 34 00 34 + 70 1F 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_brightness_icon_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 1F 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_grid_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_sidemem_setting = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 77 + 00 07 00 0A 0A + 1D 00 03 00 02 + 00 02 00 00 00 + 78 13 78 13 78 + 13 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 00 00 + 00 00 03 01 02 + 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 05 + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_sidemem_setting = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 2A 77 + 00 36 00 0A 03 + 00 00 03 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 01 2C 01 90 + 01 F4 01 90 01 + 2C 02 F4 01 F4 + 02 F4 00 C8 01 + 64 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 1B 79 + 00 C0 00 00 00 + 00 57 01 0A 0A + 00 FF 00 00 FF + 00 02 08 06 72 + 02 08 06 D6 1D + 1D + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 36 05 03 1A + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 14 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_clock_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 77 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_HA9.dtsi b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_HA9.dtsi new file mode 100755 index 000000000000..4030f8a1d904 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_HA9.dtsi @@ -0,0 +1,329 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_HA9_dtsi: self_display_HA9_dtsi { + label = "self_display_HA9_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 76 08 0F /* SELF_HBP, SELF_VBP */ + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 83 00 00 /* Self ICON Off */ + 29 01 00 00 00 00 03 77 00 00 /* Analog Clock 2C Sync Off */ + 29 01 00 00 00 00 03 80 00 00 /* Digital Clock Off */ + 29 01 00 00 00 00 03 81 00 00 /* Timer Off */ + 29 01 00 00 00 00 02 85 00 /* Partial HLPM & Scan Off */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 81 + 00 03 0A 0A 1E + 00 1E 03 00 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 4F 7D + 00 03 00 00 00 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_reset = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 10 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7D 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 00 00 03 7B 00 0C + 29 01 00 00 00 00 02 75 10 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 0B 8F 18 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_setting_pre = [ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_setting_post = [ + 29 01 00 00 00 00 02 75 00 /* MA_SEL(0), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + ]; + + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 21 00 00 00 95 + 0B 4A 0B DF 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 21 0B E0 0C 75 + 0C 26 0C BB 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_setting_pre = [ + 29 01 00 00 00 00 03 83 00 00 + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_setting_post = [ + 29 01 00 00 00 00 02 75 00 /* MA_SEL, IC_SEL(0), VI_SEL, DC_SEL, AC_SEL */ + ]; + + samsung,self_icon_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 83 + 00 11 02 00 02 + 00 01 00 01 00 + 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 83 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_setting_pre = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_setting_post = [ + 29 01 00 00 00 00 02 75 00 + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 25 77 + 00 03 02 00 02 + 00 00 00 50 13 + 50 13 50 13 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 08 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 00 00 + 00 00 03 01 02 + 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_setting_pre = [ + 29 01 00 00 00 00 03 80 00 00 + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_setting_post = [ + 29 01 00 00 00 00 02 75 00 + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 24 80 + 00 03 00 0F 01 + 2C 01 90 01 F4 + 01 90 01 2C 02 + F4 02 F4 01 F4 + 00 C8 01 64 FF + FF FF FF 00 00 + 00 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 80 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 80 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF PARTIAL HLPM/SCAN) + ************************************************************************************************************************* + */ + + samsung,self_partial_hlpm_scan_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 11 85 + 13 11 0F 0F 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_XA0.dtsi b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_XA0.dtsi new file mode 100755 index 000000000000..7db0bd1b11bb --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/SELF_DISPLAY/self_display_cmd_XA0.dtsi @@ -0,0 +1,327 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_XA0_dtsi: self_display_XA0_dtsi { + label = "self_display_XA0_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 76 08 0F /* SELF_HBP, SELF_VBP */ + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 83 00 00 /* Self ICON Off */ + 29 01 00 00 00 00 03 77 00 00 /* Analog Clock 2C Sync Off */ + 29 01 00 00 00 00 03 80 00 00 /* Digital Clock Off */ + 29 01 00 00 00 00 03 81 00 00 /* Timer Off */ + 29 01 00 00 00 00 02 85 00 /* Partial HLPM & Scan Off */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 81 + 00 03 0A 0A 1E + 00 1E 03 00 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 4F 7D + 00 03 00 00 00 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 05 + 10 10 10 10 01 50 50 50 50 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_reset = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 10 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7D 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 00 00 03 7B 00 0C + 29 01 00 00 00 00 02 75 10 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 0B 8F 18 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_setting_pre = [ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_setting_post = [ + 29 01 00 00 00 00 02 75 00 /* MA_SEL(0), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + ]; + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 21 00 00 00 63 + 05 9C 05 FF 09 + 10 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 21 06 00 06 63 + 06 64 06 C7 09 + 10 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_setting_pre = [ + 29 01 00 00 00 00 03 83 00 00 + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_setting_post = [ + 29 01 00 00 00 00 02 75 00 /* MA_SEL, IC_SEL(0), VI_SEL, DC_SEL, AC_SEL */ + ]; + + samsung,self_icon_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 83 + 00 11 02 00 02 + 00 00 40 00 42 + FF FF 80 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 83 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_setting_pre = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_setting_post = [ + 29 01 00 00 00 00 02 75 00 + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 25 77 + 00 03 02 00 02 + 00 03 00 78 13 + 78 13 78 13 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 07 + 07 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 00 00 + 00 00 03 01 02 + 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_setting_pre = [ + 29 01 00 00 00 00 03 80 00 00 + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_setting_post = [ + 29 01 00 00 00 00 02 75 00 + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 24 80 + 00 03 00 0F 01 + 2C 01 90 01 F4 + 01 90 01 2C 02 + F4 02 F4 01 F4 + 00 C8 01 64 FF + FF FF FF 00 00 + 00 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 80 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 80 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF PARTIAL HLPM/SCAN) + ************************************************************************************************************************* + */ + + samsung,self_partial_hlpm_scan_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 11 85 + 13 1B 0F 0F 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/self_display_cmd.dtsi b/drivers/gpu/drm/msm/samsung/self_display_cmd.dtsi new file mode 100755 index 000000000000..dc70d12a2b00 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/self_display_cmd.dtsi @@ -0,0 +1,459 @@ +/* Copyright (c) 2012, Samsung Electronics Corporation. 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. + */ + +&soc { + self_display_dtsi: self_display_dtsi { + label = "self_display_dtsi"; + + /* + ************************************************************************************************************************* + * Self Display Operation (Enable/Disable) + ************************************************************************************************************************* + */ + + samsung,self_dispaly_on = [ + 29 01 00 00 00 00 03 FC 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 B0 15 + 29 01 00 00 00 00 02 FE 00 /* Move Setting */ + 29 01 00 00 00 00 07 76 09 0F 85 A0 0B 90 + 29 01 00 00 00 00 03 FC A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dispaly_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 /* Self ICON & Grid 2C Sync Off */ + 29 01 00 00 00 00 02 77 01 /* Self Clock 2C Sync Off */ + 29 01 00 00 00 00 04 76 09 0F 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF TIME UPDATE) + ************************************************************************************************************************* + */ + + samsung,self_time_set = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0A 77 + 00 04 00 00 00 + 00 00 03 00 + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MOVE) + ************************************************************************************************************************* + */ + + /* Inverval 100ms Move Pattern */ + samsung,self_move_on_100 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 22 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 200ms Move Pattern */ + samsung,self_move_on_200 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 55 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 500ms Move Pattern */ + samsung,self_move_on_500 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Inverval 1000ms(1Sec) Move Pattern */ + samsung,self_move_on_1000 = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 dd + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 07 + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 07 + 00 30 00 30 00 30 00 30 00 03 + 00 70 00 70 00 70 00 70 00 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 04 04 04 04 01 0C 0C 0C 0C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* Debug Move Pattern(Only for 1000ms Interval) */ + samsung,self_move_on_debug = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 61 7D + 00 00 00 00 00 00 00 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 07 + 30 30 30 30 03 70 70 70 70 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 44 44 1C CC C3 44 44 1C CC C3 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_reset = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 05 7D 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_move_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7D 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF VIDEO) + ************************************************************************************************************************* + */ + + samsung,self_video_mem_setting = [ + 29 01 00 00 00 00 03 7B 00 0C + 29 01 00 00 00 00 02 75 10 + /* image data (4C,5C) ... */ + ]; + + samsung,self_video_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 58 7B + 00 C0 11 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 0B 8F 18 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 00 05 00 + 05 00 05 00 05 + 00 05 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_video_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 04 7B 00 C0 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF MASK) + ************************************************************************************************************************* + */ + + samsung,self_mask_mem_setting = [ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 02 75 10 /* MA_SEL(1), IC_SEL, VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_mask_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 00 00 00 95 + 0A FA 0B 8F 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_on_factory = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 10 7A + 01 0B 90 0C 25 + 0C 26 0C BB 09 + 0F 00 00 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_mask_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7A 00 /* SM_MASK_EN(0) */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ICON) + ************************************************************************************************************************* + */ + + samsung,self_icon_mem_setting = [ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 02 75 08 /* MA_SEL, IC_SEL(1), VI_SEL, DC_SEL, AC_SEL */ + /* image data (4C,5C) ... */ + ]; + + samsung,self_icon_grid = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 07 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 + 00 00 34 00 34 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_on_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0B 7C + 00 01 02 00 02 + 00 00 34 00 34 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 11 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_off_grid_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 7C 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_brightness_icon_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 11 02 00 02 + 00 00 34 00 34 + 70 1F 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_brightness_icon_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 7C + 00 10 00 00 00 + 00 00 00 00 00 + 70 1F 00 00 00 + 00 05 A0 0B 90 + 1E + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_icon_grid_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 7C 03 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF ALALOG CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_aclock_sidemem_setting = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 01 + /* image data (4C,5C) ... */ + ]; + + samsung,self_aclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 16 77 + 00 07 00 0A 0A + 1D 00 03 00 02 + 00 02 00 00 00 + 78 13 78 13 78 + 13 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 07 00 05 03 + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_rotation = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 0F 77 + 00 07 00 00 00 + 00 00 03 01 02 + 00 02 00 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_aclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 05 + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + + /* + ************************************************************************************************************************* + * Self Display Operation (SELF DIGITAL CLOCK) + ************************************************************************************************************************* + */ + + samsung,self_dclock_sidemem_setting = [ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 02 75 02 + /* image data (4C,5C) ... */ + ]; + + samsung,self_dclock_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 2A 77 + 00 36 00 0A 03 + 00 00 03 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 01 2C 01 90 + 01 F4 01 90 01 + 2C 02 F4 01 F4 + 02 F4 00 C8 01 + 64 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_on = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 1B 79 + 00 C0 00 00 00 + 00 57 01 0A 0A + 00 FF 00 00 FF + 00 02 08 06 72 + 02 08 06 D6 1D + 1D + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_time_update = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ +/* 29 01 00 00 00 00 08 77 + 00 36 05 03 1A + 20 00 +*/ + 29 01 00 00 00 00 02 78 01 /* SC_TIME_UPDATE */ + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_hide = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 77 00 14 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_dclock_blinking_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 03 79 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + samsung,self_clock_2c_sync_off = [ + 29 01 00 00 00 00 03 F0 5A 5A /* TEST KEY Enable */ + 29 01 00 00 00 00 02 77 01 + 29 01 00 00 00 00 03 F0 A5 A5 /* TEST KEY Disable */ + ]; + + /* + ************************************************************************************************************************* + * Self Display Operation (DEBUGGING FEATURE) + ************************************************************************************************************************* + */ + + samsung,self_disp_debug_rx_cmds = [ 06 01 00 00 00 00 01 7F 12 00]; /* read 7F 1st ~ 18th */ + }; +}; diff --git a/drivers/gpu/drm/msm/samsung/ss_copr_common.c b/drivers/gpu/drm/msm/samsung/ss_copr_common.c new file mode 100755 index 000000000000..39af40d9adbc --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_copr_common.c @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * COPR : + * Author: QC LCD driver + * + * 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_spi_common.h" +#include "ss_copr_common.h" + +struct COPR_REG_OSSET copr_offset_list[] = { + {.name = "copr_mask=", .offset = offsetof(struct COPR_CMD, COPR_MASK)}, + {.name = "copr_cnt_re=", .offset = offsetof(struct COPR_CMD, CNT_RE)}, + {.name = "copr_ilc=", .offset = offsetof(struct COPR_CMD, COPR_ILC)}, + {.name = "copr_gamma=", .offset = offsetof(struct COPR_CMD, COPR_GAMMA)}, + {.name = "copr_en=", .offset = offsetof(struct COPR_CMD, COPR_EN)}, + {.name = "copr_er=", .offset = offsetof(struct COPR_CMD, COPR_ER)}, + {.name = "copr_eg=", .offset = offsetof(struct COPR_CMD, COPR_EG)}, + {.name = "copr_eb=", .offset = offsetof(struct COPR_CMD, COPR_EB)}, + {.name = "copr_erc=", .offset = offsetof(struct COPR_CMD, COPR_ERC)}, + {.name = "copr_egc=", .offset = offsetof(struct COPR_CMD, COPR_EGC)}, + {.name = "copr_ebc=", .offset = offsetof(struct COPR_CMD, COPR_EBC)}, + {.name = "copr_max_cnt=", .offset = offsetof(struct COPR_CMD, MAX_CNT)}, + {.name = "copr_roi_on=", .offset = offsetof(struct COPR_CMD, ROI_ON)}, + + {.name = "copr_roi_x_s=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_X_S)}, + {.name = "copr_roi_y_s=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_Y_S)}, + {.name = "copr_roi_x_e=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_X_E)}, + {.name = "copr_roi_y_e=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_Y_E)}, + + {.name = "copr_roi1_x_s=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_X_S)}, + {.name = "copr_roi1_y_s=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_Y_S)}, + {.name = "copr_roi1_x_e=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_X_E)}, + {.name = "copr_roi1_y_e=", .offset = offsetof(struct COPR_CMD, roi[0].ROI_Y_E)}, + + {.name = "copr_roi2_x_s=", .offset = offsetof(struct COPR_CMD, roi[1].ROI_X_S)}, + {.name = "copr_roi2_y_s=", .offset = offsetof(struct COPR_CMD, roi[1].ROI_Y_S)}, + {.name = "copr_roi2_x_e=", .offset = offsetof(struct COPR_CMD, roi[1].ROI_X_E)}, + {.name = "copr_roi2_y_e=", .offset = offsetof(struct COPR_CMD, roi[1].ROI_Y_E)}, + + {.name = "copr_roi3_x_s=", .offset = offsetof(struct COPR_CMD, roi[2].ROI_X_S)}, + {.name = "copr_roi3_y_s=", .offset = offsetof(struct COPR_CMD, roi[2].ROI_Y_S)}, + {.name = "copr_roi3_x_e=", .offset = offsetof(struct COPR_CMD, roi[2].ROI_X_E)}, + {.name = "copr_roi3_y_e=", .offset = offsetof(struct COPR_CMD, roi[2].ROI_Y_E)}, + + {.name = "copr_roi4_x_s=", .offset = offsetof(struct COPR_CMD, roi[3].ROI_X_S)}, + {.name = "copr_roi4_y_s=", .offset = offsetof(struct COPR_CMD, roi[3].ROI_Y_S)}, + {.name = "copr_roi4_x_e=", .offset = offsetof(struct COPR_CMD, roi[3].ROI_X_E)}, + {.name = "copr_roi4_y_e=", .offset = offsetof(struct COPR_CMD, roi[3].ROI_Y_E)}, + + {.name = "copr_roi5_x_s=", .offset = offsetof(struct COPR_CMD, roi[4].ROI_X_S)}, + {.name = "copr_roi5_y_s=", .offset = offsetof(struct COPR_CMD, roi[4].ROI_Y_S)}, + {.name = "copr_roi5_x_e=", .offset = offsetof(struct COPR_CMD, roi[4].ROI_X_E)}, + {.name = "copr_roi5_y_e=", .offset = offsetof(struct COPR_CMD, roi[4].ROI_Y_E)}, + + {.name = "copr_roi6_x_s=", .offset = offsetof(struct COPR_CMD, roi[5].ROI_X_S)}, + {.name = "copr_roi6_y_s=", .offset = offsetof(struct COPR_CMD, roi[5].ROI_Y_S)}, + {.name = "copr_roi6_x_e=", .offset = offsetof(struct COPR_CMD, roi[5].ROI_X_E)}, + {.name = "copr_roi6_y_e=", .offset = offsetof(struct COPR_CMD, roi[5].ROI_Y_E)}, +}; + +void ss_copr_enable(struct samsung_display_driver_data *vdd, int enable) +{ + if (vdd->copr.copr_on == enable) { + LCD_ERR("copr already %d..\n", vdd->copr.copr_on); + return; + } + + mutex_lock(&vdd->copr.copr_lock); + + if (enable) { + /* enable COPR IP */ + ss_send_cmd(vdd, TX_COPR_ENABLE); + } else { + /* disable COPR IP */ + ss_send_cmd(vdd, TX_COPR_DISABLE); + + vdd->copr.copr_cd[COPR_CD_INDEX_0].cd_sum = 0; + vdd->copr.copr_cd[COPR_CD_INDEX_0].total_t = 0; + } + + vdd->copr.copr_on = enable; + + LCD_INFO("copr %s .. \n", vdd->copr.copr_on?"enabled..":"disabled.."); + + mutex_unlock(&vdd->copr.copr_lock); + + return; +} + +void print_copr_cmd(struct COPR_CMD cmd) +{ + int i; + + LCD_INFO("MASK(%d) RE(%d) ILC(%d) GAMMA(%d) EN(%d)\n", + cmd.COPR_MASK, cmd.CNT_RE, cmd.COPR_ILC, cmd.COPR_GAMMA, cmd.COPR_EN); + LCD_INFO("ER(%d) EG(%d) EB(%d) ERC(%d) EGC(%d) EBC(%d)\n", + cmd.COPR_ER, cmd.COPR_EG, cmd.COPR_EB, + cmd.COPR_ERC, cmd.COPR_EGC, cmd.COPR_EBC); + LCD_INFO("MAX_CNT(%d) ROI_EN(%d) ROI_CTRL(%d)\n", + cmd.MAX_CNT, cmd.ROI_ON, cmd.COPR_ROI_CTRL); + + for (i = 0; i < MAX_COPR_ROI_CNT; i++) + LCD_INFO("ROI[%d] X_S(%d) Y_S(%d) X_E(%d) Y_E(%d)\n", i + 1, + cmd.roi[i].ROI_X_S, cmd.roi[i].ROI_Y_S, cmd.roi[i].ROI_X_E, cmd.roi[i].ROI_Y_E); +} + +/** + * ss_copr_set_cnt_re - set Counter Reset Trigger Signal values to copr cmds + */ +void ss_copr_set_cnt_re(struct COPR_CMD *cmd, int reset) +{ + cmd->CNT_RE = !!reset; +} + +/** + * ss_copr_set_en - set copr power on/off control values to copr cmds + */ +void ss_copr_set_en(struct COPR_CMD *cmd, int enable) +{ + cmd->COPR_EN = !!enable; +} + +/** + * ss_copr_set_ilc - set Illuminance Compensated COPR values to copr cmds + */ +void ss_copr_set_ilc(struct COPR_CMD *cmd, int enable) +{ + cmd->COPR_ILC = !!enable; +} + +/** + * ss_copr_set_gamma - set Gamma Selection values to copr cmds + */ +void ss_copr_set_gamma(struct COPR_CMD *cmd, int val) +{ + cmd->COPR_GAMMA = !!val; +} + +/** + * ss_copr_set_max_cnt - set Max Count values to copr cmds + */ +void ss_copr_set_max_cnt(struct COPR_CMD *cmd, int cnt) +{ + if (cnt > MAX_COPR_CNT) + cnt = MAX_COPR_CNT; + + cmd->MAX_CNT = cnt; +} + +/** + * ss_copr_set_e - set Efficiency values to copr cmds + */ +void ss_copr_set_e(struct COPR_CMD *cmd, int r, int g, int b) +{ + cmd->COPR_ER = r; + cmd->COPR_EG = g; + cmd->COPR_EB = b; +} + +/** + * ss_copr_set_ec - set Efficiency for Illuminance Compensation values to copr cmds + */ +void ss_copr_set_ec(struct COPR_CMD *cmd, int r, int g, int b) +{ + cmd->COPR_ERC = r; + cmd->COPR_EGC = g; + cmd->COPR_EBC = b; +} + +/** + * ss_copr_set_roi - set roi values in cmds. + */ +void ss_copr_set_roi(struct samsung_display_driver_data *vdd, struct COPR_CMD *cmd, int idx) +{ + int roi_idx; + + if (vdd->copr.ver < COPR_VER_3P0) + roi_idx = 0; + else + roi_idx = idx; + + if (vdd->copr.afc_roi[idx].ROI_X_S == -1) { + LCD_ERR("Do not set for idx[%d] - %d %d %d %d\n", idx, + vdd->copr.afc_roi[idx].ROI_X_S, + vdd->copr.afc_roi[idx].ROI_Y_S, + vdd->copr.afc_roi[idx].ROI_X_E, + vdd->copr.afc_roi[idx].ROI_Y_E); + return; + } + + cmd->roi[roi_idx].ROI_X_S = vdd->copr.afc_roi[idx].ROI_X_S; + cmd->roi[roi_idx].ROI_Y_S = vdd->copr.afc_roi[idx].ROI_Y_S; + cmd->roi[roi_idx].ROI_X_E = vdd->copr.afc_roi[idx].ROI_X_E; + cmd->roi[roi_idx].ROI_Y_E = vdd->copr.afc_roi[idx].ROI_Y_E; + + if (vdd->copr.ver < COPR_VER_3P0) { + if (vdd->copr.afc_roi[idx].ROI_X_S || vdd->copr.afc_roi[idx].ROI_Y_S || + vdd->copr.afc_roi[idx].ROI_X_E || vdd->copr.afc_roi[idx].ROI_Y_E) + cmd->ROI_ON = 1; + else + cmd->ROI_ON = 0; + } +} + +int ss_copr_set_cmd_offset(struct COPR_CMD *cmd, char* p) +{ + int list_size = ARRAY_SIZE(copr_offset_list); + const char *name; + int i, val; + int *offset; + + for (i = 0; i < list_size; i++) { + name = copr_offset_list[i].name; + if (!strncmp(name, p, strlen(name))) { + sscanf(p + strlen(name), "%d", &val); + offset = (int *)((void*)cmd + copr_offset_list[i].offset); + *offset = val; + return 0; + } + } + + return -1; +} +/** + * ss_copr_set_cmd - set copr mipi cmd using COPR_CMD para. + * + * copr_cmd : new copr cmd to update. + */ +void ss_copr_set_cmd(struct samsung_display_driver_data *vdd, struct COPR_CMD *cmd) +{ + struct dsi_panel_cmd_set *pcmds = NULL; + int cmd_len; + u8 *cmd_pload; + char buf[256]; + int i, len = 0; + int roi_max, roi_start; + + pcmds = ss_get_cmds(vdd, TX_COPR_ENABLE); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_COPR_ENABLE..\n"); + return; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + cmd_len = pcmds->cmds[1].msg.tx_len; + + cmd_pload[1] = cmd->COPR_EN; + cmd_pload[1] |= (cmd->COPR_GAMMA << 1); + cmd_pload[1] |= (cmd->COPR_ILC << 2); + cmd_pload[1] |= (cmd->CNT_RE << 3); + + if (vdd->copr.ver >= COPR_VER_3P0) + cmd_pload[1] |= (cmd->COPR_MASK << 4); + + cmd_pload[2] = ((cmd->COPR_ER & 0x300) >> 4); + cmd_pload[2] |= ((cmd->COPR_EG & 0x300) >> 6); + cmd_pload[2] |= ((cmd->COPR_EB & 0x300) >> 8); + + cmd_pload[3] = ((cmd->COPR_ERC & 0x300) >> 4); + cmd_pload[3] |= ((cmd->COPR_EGC & 0x300) >> 6); + cmd_pload[3] |= ((cmd->COPR_EBC & 0x300) >> 8); + + cmd_pload[4] = (cmd->COPR_ER & 0xFF); + cmd_pload[5] = (cmd->COPR_EG & 0xFF); + cmd_pload[6] = (cmd->COPR_EB & 0xFF); + + cmd_pload[7] = (cmd->COPR_ERC & 0xFF); + cmd_pload[8] = (cmd->COPR_EGC & 0xFF); + cmd_pload[9] = (cmd->COPR_EBC & 0xFF); + + cmd_pload[10] = ((cmd->MAX_CNT & 0xFF00) >> 8); + cmd_pload[11] = (cmd->MAX_CNT & 0xFF); + + if (vdd->copr.ver <= COPR_VER_2P0) + cmd_pload[12] = (cmd->ROI_ON << 3); + else + cmd_pload[12] = (cmd->COPR_ROI_CTRL & 0x3F); + + if (vdd->copr.ver >= COPR_VER_3P0) { + roi_max = 6; + roi_start = 13; + } else { + roi_max = 1; + roi_start = 12; + } + + for (i = 0; i < roi_max; i++) { + if (vdd->copr.ver <= COPR_VER_2P0) { + cmd_pload[roi_start++] |= ((cmd->roi[i].ROI_X_S & 0x700) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_X_S & 0xFF); + + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_Y_S & 0xF00) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_Y_S & 0xFF); + + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_X_E & 0x700) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_X_E & 0xFF); + + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_Y_E & 0xF00) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_Y_E & 0xFF); + } else { + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_X_S & 0xF00) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_X_S & 0xFF); + + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_Y_S & 0xF00) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_Y_S & 0xFF); + + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_X_E & 0xF00) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_X_E & 0xFF); + + cmd_pload[roi_start++] = ((cmd->roi[i].ROI_Y_E & 0xF00) >> 8); + cmd_pload[roi_start++] = (cmd->roi[i].ROI_Y_E & 0xFF); + } + } + + for (i = 0; i < cmd_len; i++) + len += snprintf(buf + len, sizeof(buf) - len, + "%02x ", cmd_pload[i]); + LCD_ERR("cmd[%d] : %s\n", cmd_len, buf); + + /* reset current copr cmds */ + memcpy(&vdd->copr.cur_cmd, cmd, sizeof(struct COPR_CMD)); + + return; +} + +/** + * ss_get_copr_orig_cmd - get copr original cmd from panel dtsi (initial copr enable cmd). + */ +int ss_get_copr_orig_cmd(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *pcmds = NULL; + struct COPR_CMD *cmd = &vdd->copr.orig_cmd; + u8 *cmd_pload; + int cmd_len; + char buf[256]; + int i, len = 0, roi_max, roi_start; + + pcmds = ss_get_cmds(vdd, TX_COPR_ENABLE); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_COPR_ENABLE..\n"); + return -EINVAL;; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + cmd_len = pcmds->cmds[1].msg.tx_len; + + cmd->COPR_EN = (cmd_pload[1] & 0x01) ? 1 : 0; + cmd->COPR_GAMMA = (cmd_pload[1] & 0x02) ? 1 : 0; + cmd->COPR_ILC = (cmd_pload[1] & 0x04) ? 1 : 0; + cmd->CNT_RE = (cmd_pload[1] & 0x08) ? 1 : 0; + if (vdd->copr.ver >= COPR_VER_3P0) + cmd->COPR_MASK = (cmd_pload[1] & 0x10) ? 1 : 0; + + cmd->COPR_ER = ((cmd_pload[2] & 0x30) << 4) | (cmd_pload[4] & 0xFF); + cmd->COPR_EG = ((cmd_pload[2] & 0x0C) << 6) | (cmd_pload[5] & 0xFF); + cmd->COPR_EB = ((cmd_pload[2] & 0x03) << 8) | (cmd_pload[6] & 0xFF); + cmd->COPR_ERC = ((cmd_pload[3] & 0x30) << 4) | (cmd_pload[7] & 0xFF); + cmd->COPR_EGC = ((cmd_pload[3] & 0x0C) << 6) | (cmd_pload[8] & 0xFF); + cmd->COPR_EBC = ((cmd_pload[3] & 0x03) << 8) | (cmd_pload[9] & 0xFF); + + cmd->MAX_CNT = (cmd_pload[10] << 8) | cmd_pload[11]; + + if (vdd->copr.ver <= COPR_VER_2P0) + cmd->ROI_ON = (cmd_pload[12] & 0x08) ? 1 : 0; + + if (vdd->copr.ver >= COPR_VER_3P0) + cmd->COPR_ROI_CTRL = (cmd_pload[12] & 0x3F); + + if (vdd->copr.ver >= COPR_VER_3P0) { + roi_max = 6; + roi_start = 13; + } else { + roi_max = 1; + roi_start = 12; + } + + for (i = 0; i < roi_max; i++) { + if (vdd->copr.ver <= COPR_VER_2P0) { + cmd->roi[i].ROI_X_S = ((cmd_pload[roi_start++] & 0x07) << 8); + cmd->roi[i].ROI_X_S |= (cmd_pload[roi_start++] & 0xFF); + cmd->roi[i].ROI_Y_S = ((cmd_pload[roi_start++] & 0x0F) << 8); + cmd->roi[i].ROI_Y_S |= (cmd_pload[roi_start++] & 0xFF); + cmd->roi[i].ROI_X_E = ((cmd_pload[roi_start++] & 0x07) << 8); + cmd->roi[i].ROI_X_E |= (cmd_pload[roi_start++] & 0xFF); + cmd->roi[i].ROI_Y_E = ((cmd_pload[roi_start++] & 0x0F) << 8); + cmd->roi[i].ROI_Y_E |= (cmd_pload[roi_start++] & 0xFF); + } else { + cmd->roi[i].ROI_X_S = ((cmd_pload[roi_start++] & 0x0F) << 8); + cmd->roi[i].ROI_X_S |= (cmd_pload[roi_start++] & 0xFF); + cmd->roi[i].ROI_Y_S = ((cmd_pload[roi_start++] & 0x0F) << 8); + cmd->roi[i].ROI_Y_S |= (cmd_pload[roi_start++] & 0xFF); + cmd->roi[i].ROI_X_E = ((cmd_pload[roi_start++] & 0x0F) << 8); + cmd->roi[i].ROI_X_E |= (cmd_pload[roi_start++] & 0xFF); + cmd->roi[i].ROI_Y_E = ((cmd_pload[roi_start++] & 0x0F) << 8); + cmd->roi[i].ROI_Y_E |= (cmd_pload[roi_start++] & 0xFF); + } + } + + /* init current cmd with origianl cmd */ + memcpy(&vdd->copr.cur_cmd, cmd, sizeof(struct COPR_CMD)); + + print_copr_cmd(vdd->copr.cur_cmd); + + for (i = 0; i < cmd_len; i++) + len += snprintf(buf + len, sizeof(buf) - len, + "%02x ", cmd_pload[i]); + LCD_ERR("cmd[%d] : %s\n", cmd_len, buf); + + return 1; +} + +/** + * ss_copr_get_roi_opr_2P0 - get opr values of all roi's r/g/b for copr ver 2.0. + */ +int ss_copr_get_roi_opr_2P0(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + int i; + struct COPR_CMD cmd_backup; + struct COPR_CMD *cur_cmd; + + LCD_DEBUG("++ (%d)\n", vdd->copr.afc_roi_cnt); + + /* backup copr current cmd */ + memcpy(&cmd_backup, &vdd->copr.cur_cmd, sizeof(cmd_backup)); + cur_cmd = &vdd->copr.cur_cmd; + + ss_copr_set_en(cur_cmd, 1); + ss_copr_set_cnt_re(cur_cmd, 0); + ss_copr_set_max_cnt(cur_cmd, 1); + ss_copr_set_gamma(cur_cmd, 1); + + /** + * roi[0] - top + * roi[1] - mid + * roi[2] - bottom + * roi[3] - all + */ + for (i = 0; i < vdd->copr.afc_roi_cnt; i++) { + + ss_copr_set_roi(vdd, cur_cmd, i); + + /* R / G --------------------------------------- */ + ss_copr_set_ilc(cur_cmd, 1); + ss_copr_set_e(cur_cmd, 0x300, 0x0, 0x0); + ss_copr_set_ec(cur_cmd, 0x0, 0x300, 0x0); + + ss_copr_set_cmd(vdd, cur_cmd); + ss_send_cmd(vdd, TX_COPR_ENABLE); + + /* sleep 34ms (wait 2 frame : roi cmd write -> vsync -> image write -> opr read) */ + usleep_range(34000, 34000); + + ret = ss_copr_read(vdd); + if (ret) + goto err; + + vdd->copr.roi_opr[i].R_OPR = vdd->copr.current_copr; + vdd->copr.roi_opr[i].G_OPR = vdd->copr.comp_copr; + /* --------------------------------------------- */ + + /* B ------------------------------------------- */ + ss_copr_set_ilc(cur_cmd, 0); + ss_copr_set_e(cur_cmd, 0x0, 0x0, 0x300); + ss_copr_set_ec(cur_cmd, 0x0, 0x0, 0x0); + + ss_copr_set_cmd(vdd, cur_cmd); + ss_send_cmd(vdd, TX_COPR_ENABLE); + + /* sleep 34ms (wait 2 frame : roi cmd write -> vsync -> image write -> opr read) */ + usleep_range(34000, 34000); + + ret = ss_copr_read(vdd); + if (ret) + goto err; + + vdd->copr.roi_opr[i].B_OPR = vdd->copr.current_copr; + /* --------------------------------------------- */ + + LCD_INFO("R (%d) G (%d) B (%d)\n", + vdd->copr.roi_opr[i].R_OPR, + vdd->copr.roi_opr[i].G_OPR, + vdd->copr.roi_opr[i].B_OPR); + } + +err: + /* restore copr cmd */ + ss_copr_set_cmd(vdd, &cmd_backup); + + LCD_DEBUG("--\n"); + + return ret; +} + +/** + * ss_copr_get_roi_opr_3P0 - get opr values of all roi's r/g/b for copr ver 3.0. + */ +int ss_copr_get_roi_opr_3P0(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + int i; + struct COPR_CMD cmd_backup; + struct COPR_CMD *cur_cmd; + + LCD_DEBUG("++ (%d)\n", vdd->copr.afc_roi_cnt); + + /* backup copr current cmd */ + memcpy(&cmd_backup, &vdd->copr.cur_cmd, sizeof(cmd_backup)); + cur_cmd = &vdd->copr.cur_cmd; + + /* set roi values as roi values from mDNIe service */ + for (i = 0; i < vdd->copr.afc_roi_cnt; i++) + ss_copr_set_roi(vdd, cur_cmd, i); + + /* reset copr enable cmds with new roi values */ + ss_copr_set_cmd(vdd, cur_cmd); + + ret = ss_send_cmd(vdd, TX_COPR_ENABLE); + if (ret) + LCD_ERR("fail to TX_COPR_ENABLE .. \n"); + + ret = ss_copr_read(vdd); + if (ret) + LCD_ERR("fail to read copr .. \n"); + + /* restore copr cmd */ + ss_copr_set_cmd(vdd, &cmd_backup); + + LCD_DEBUG("--\n"); + + return ret; +} + +/** + * ss_copr_get_roi_opr - get opr values of all roi's r/g/b for AFC. + * + * Get copr snapshot for each roi's r/g/b. + * This function returns 0 if the average is valid. + * If not this function returns -ERR. + */ +int ss_copr_get_roi_opr(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + + if (vdd->copr.ver < COPR_VER_3P0) + ret = ss_copr_get_roi_opr_2P0(vdd); + else /* copr ver 3.0 */ + ret = ss_copr_get_roi_opr_3P0(vdd); + + return ret; +} + +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; + vdd->copr.copr_cd[idx].cd_sum += (vdd->br.interpolation_cd * delta); + mutex_unlock(&vdd->copr.copr_val_lock); + + LCD_DEBUG("[%d ]cd(%d) delta (%lld) cd_sum (%lld) total_t (%lld)\n", idx, + vdd->br.interpolation_cd, delta, vdd->copr.copr_cd[idx].cd_sum, vdd->copr.copr_cd[idx].total_t); +} + +/** + * ss_copr_reset_cnt - reset copr frame cnt using CNT_RE reg. only for rev 2.0 + */ +void ss_copr_reset_cnt(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *pcmds; + u8 *cmd_pload; + + pcmds = ss_get_cmds(vdd, TX_COPR_ENABLE); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_COPR_ENABLE..\n"); + return; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + + /* CNT_RE = 1 */ + cmd_pload[1] = 0x9; + ss_send_cmd(vdd, TX_COPR_ENABLE); + + /* sleep 20ms (wait te) */ + usleep_range(20000, 20000); + + /* CNT_RE = 0 */ + cmd_pload[1] = 0x3; + ss_send_cmd(vdd, TX_COPR_ENABLE); + + return; +} + +void ss_copr_parse_spi_data(struct samsung_display_driver_data *vdd, u8 *rxbuf) +{ + int i, roi_idx; + + if (vdd->copr.ver == COPR_VER_3P0) { + /* parse copr data (3.0) */ + vdd->copr.copr_ready = rxbuf[0] & 0x01; + vdd->copr.current_cnt = (rxbuf[1] << 8) | rxbuf[2]; + vdd->copr.current_copr = ((rxbuf[3] & 0x01) << 8) | rxbuf[4]; + vdd->copr.avg_copr = ((rxbuf[5] & 0x1) << 8) | rxbuf[6]; + vdd->copr.sliding_current_cnt = (rxbuf[7] << 8) | rxbuf[8]; + vdd->copr.sliding_avg_copr = ((rxbuf[9] & 0x1) << 8) | rxbuf[10]; + + roi_idx = 11; + for (i = 0; i < 4; i++) { + vdd->copr.roi_opr[i].R_OPR = (rxbuf[roi_idx++] & 0x1) << 8; + vdd->copr.roi_opr[i].R_OPR |= rxbuf[roi_idx++]; + vdd->copr.roi_opr[i].G_OPR = (rxbuf[roi_idx++] & 0x1) << 8; + vdd->copr.roi_opr[i].G_OPR |= rxbuf[roi_idx++]; + vdd->copr.roi_opr[i].B_OPR = (rxbuf[roi_idx++] & 0x1) << 8; + vdd->copr.roi_opr[i].B_OPR |= rxbuf[roi_idx++]; + } + } else { + /* parse copr data (2.0) */ + vdd->copr.copr_ready = (rxbuf[0] & 0x80) >> 7; + vdd->copr.current_cnt = ((rxbuf[0] & 0x7F) << 9) | (rxbuf[1] << 1) | (rxbuf[2] & 0x80 >> 7) ; + vdd->copr.current_copr = ((rxbuf[2] & 0x7F) << 2) | ((rxbuf[3] & 0xC0) >> 6); + vdd->copr.avg_copr = ((rxbuf[3] & 0x3F) << 3) | ((rxbuf[4] & 0xE0) >> 5); + vdd->copr.sliding_current_cnt = ((rxbuf[4] & 0x1F) << 11) | (rxbuf[5] << 3) | ((rxbuf[6] & 0xE0) >> 5); + vdd->copr.sliding_avg_copr = ((rxbuf[6] & 0x1F) << 4) | ((rxbuf[7] & 0xF0) >> 4); + vdd->copr.comp_copr = ((rxbuf[8] & 0xF8) >> 3) | ((rxbuf[7] & 0x0F) << 5); + } +} + +/** + * ss_copr_read() + * + * read copr registers via SPI interface + * This function returns zero on success, else a negative error code. + */ +int ss_copr_read(struct samsung_display_driver_data *vdd) +{ + u8 *rxbuf; + u8 rx_addr; + int tx_bpw, rx_bpw; + int tx_size, rx_size; + int ret = 0; + int i; + + LCD_DEBUG("%s ++ \n", __func__); + + if (!ss_is_panel_on(vdd)) { + LCD_ERR("panel stste (%d) \n", vdd->panel_state); + return -ENODEV; + } + + if (!vdd->copr.display_read) { + LCD_ERR("copr read is not from display driver (%d) \n", vdd->copr.display_read); + return -ENODEV; + } + + /* Get spi components */ + rx_addr = vdd->copr.rx_addr; + tx_size = vdd->copr.tx_size; + rx_size = vdd->copr.rx_size; + tx_bpw = vdd->copr.tx_bpw; + rx_bpw = vdd->copr.rx_bpw; + + rxbuf = kzalloc(rx_size, GFP_KERNEL); + if (rxbuf == NULL) { + LCD_ERR("fail to kzalloc for rxbuf \n"); + ret = -ENOMEM; + goto err; + } + + ret = ss_spi_read(vdd->spi_dev, rxbuf, tx_bpw, rx_bpw, tx_size, rx_size, rx_addr); + if (ret) { + LCD_ERR("[SDE SPI] %s : spi read fail..(%x)\n", __func__, rx_addr); + goto err; + } + + for (i = 0; i < rx_size; i++) + LCD_DEBUG("[%02d] %02x \n", i, rxbuf[i]); + + ss_copr_parse_spi_data(vdd, rxbuf); + + if (vdd->copr.ver == COPR_VER_3P0) + LCD_DEBUG("[%d] current_copr (%d), avg_copr (%d) sld_avg (%d) \n", + vdd->copr.current_cnt, vdd->copr.current_copr, vdd->copr.avg_copr, vdd->copr.sliding_avg_copr); + else + LCD_DEBUG("[%d] current_copr (%d), avg_copr (%d) , comp_copr(%d)\n", + vdd->copr.current_cnt, vdd->copr.current_copr, vdd->copr.avg_copr, vdd->copr.comp_copr); + + /* If current_copr is over 0, copr work thread will be stopped */ + if (vdd->display_status_dsi.wait_actual_disp_on) { + LCD_INFO("ACTUAL_DISPLAY_ON\n"); + vdd->display_status_dsi.wait_actual_disp_on = false; + } + + LCD_DEBUG("%s -- data (%d)\n", __func__, vdd->copr.current_copr); + +err: + kfree(rxbuf); + return ret; +} + +/** + * ss_read_copr_work() + * + * COPR 1.0 - Need to run work thread to get copr per frame. + * COPR 2.0 - Do not need to run work thread to get copr avg. + * but for the debugging purpose, this work is used. + */ +void ss_read_copr_work(struct work_struct *work) +{ + struct samsung_display_driver_data *vdd = NULL; + struct COPR *copr; + + copr = container_of(work, struct COPR, read_copr_work); + vdd = container_of(copr, struct samsung_display_driver_data, copr); + + LCD_DEBUG("copr_calc work!!\n"); + + mutex_lock(&vdd->copr.copr_lock); + + //ss_set_copr_sum(vdd); + ss_copr_read(vdd); + + LCD_DEBUG("COPR : %02x (%d) \n", vdd->copr.current_copr, vdd->copr.current_copr); + + mutex_unlock(&vdd->copr.copr_lock); + + return; +} + +void ss_copr_init(struct samsung_display_driver_data *vdd) +{ + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null or error\n"); + return; + } + + mutex_init(&vdd->copr.copr_val_lock); + mutex_init(&vdd->copr.copr_lock); + + if (vdd->copr.display_read) { + /* read_copr_wq is used for checking real frame is entered into ddi with opr value */ + vdd->copr.read_copr_wq = create_singlethread_workqueue("read_copr_wq"); + if (vdd->copr.read_copr_wq == NULL) { + LCD_ERR("failed to create read copr workqueue..\n"); + return; + } + + INIT_WORK(&vdd->copr.read_copr_work, (work_func_t)ss_read_copr_work); + } + + vdd->copr.copr_on = 1; + + LCD_INFO("ver[%d] display_read[%d] tx_bpw(%d) rx_bpw(%d) tx_size(%d) rx_size(%d) rx_addr(%x) on(%d)\n", + vdd->copr.ver, vdd->copr.display_read, + vdd->copr.tx_bpw, vdd->copr.rx_bpw, vdd->copr.tx_size, vdd->copr.rx_size, + vdd->copr.rx_addr, vdd->copr.copr_on); + + LCD_INFO("Success to initialized copr ..\n"); + + return; +} diff --git a/drivers/gpu/drm/msm/samsung/ss_copr_common.h b/drivers/gpu/drm/msm/samsung/ss_copr_common.h new file mode 100755 index 000000000000..0a3a29cf63c2 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_copr_common.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * COPR : + * Author: QC LCD driver + * + * 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__ + +#define MAX_COPR_CNT 36000 + +enum COPR_VER { + COPR_VER_1P0 = 0, + COPR_VER_2P0, + COPR_VER_3P0, /* COPR_1121 */ +}; + +/* MAX COPR ROI is from copr ver. 3.0 */ +#define MAX_COPR_ROI_CNT 6 + +struct COPR_ROI { + int ROI_X_S; + int ROI_Y_S; + int ROI_X_E; + int ROI_Y_E; +}; + +struct COPR_REG_ARGS { + const char *name; + u32 *store_ptr; +}; + +struct COPR_CMD { + int COPR_MASK; + int CNT_RE; + int COPR_ILC; + int COPR_GAMMA; + int COPR_EN; + + int COPR_ER; + int COPR_EG; + int COPR_EB; + int COPR_ERC; + int COPR_EGC; + int COPR_EBC; + + int MAX_CNT; + int ROI_ON; // only ver 2.0 + int COPR_ROI_CTRL; + struct COPR_ROI roi[MAX_COPR_ROI_CNT]; +}; + +struct COPR_REG_OSSET{ + const char *name; + int offset; +}; + +struct COPR_ROI_OPR { + int R_OPR; + int G_OPR; + int B_OPR; +}; + +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 { + int ver; + int copr_on; + + int tx_bpw; + int rx_bpw; + int tx_size; + int rx_size; + char rx_addr; + + /* read data */ + int copr_ready; + int current_cnt; + int current_copr; + int avg_copr; + int sliding_current_cnt; + int sliding_avg_copr; + int comp_copr; + struct COPR_ROI_OPR roi_opr[MAX_COPR_ROI_CNT]; + + struct mutex copr_lock; + struct mutex copr_val_lock; + struct workqueue_struct *read_copr_wq; + struct work_struct read_copr_work; + + struct COPR_CD copr_cd[MAX_COPR_CD_INDEX]; + struct COPR_CMD cmd; + struct COPR_CMD orig_cmd; + struct COPR_CMD cur_cmd; + + /* roi values from mDNIe service for AFC */ + struct COPR_ROI afc_roi[MAX_COPR_ROI_CNT]; + int afc_roi_cnt; + + int display_read; /* Does display driver use copr read operation? ? 1 : 0 */ + void (*panel_init)(struct samsung_display_driver_data *vdd); +}; + +void print_copr_cmd(struct COPR_CMD cmd); +int ss_copr_set_cmd_offset(struct COPR_CMD *cmd, char* p); +void ss_copr_set_cmd(struct samsung_display_driver_data *vdd, struct COPR_CMD *copr_cmd); +int ss_get_copr_orig_cmd(struct samsung_display_driver_data *vdd); +void ss_copr_enable(struct samsung_display_driver_data *vdd, int enable); +int ss_copr_read(struct samsung_display_driver_data *vdd); +void ss_set_copr_sum(struct samsung_display_driver_data *vdd, enum COPR_CD_INDEX idx); +void ss_copr_reset_cnt(struct samsung_display_driver_data *vdd); +int ss_copr_get_roi_opr(struct samsung_display_driver_data *vdd); + +void ss_read_copr_work(struct work_struct *work); +void ss_copr_init(struct samsung_display_driver_data *vdd); + +#endif // __SS_COPR_COMMON_H__ diff --git a/drivers/gpu/drm/msm/samsung/ss_ddi_poc_common.c b/drivers/gpu/drm/msm/samsung/ss_ddi_poc_common.c new file mode 100755 index 000000000000..00c9b2f5b9d4 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_ddi_poc_common.c @@ -0,0 +1,890 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Samsung's POC Driver + * Author: ChangJae Jang + * + * 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 +#include "ss_dsi_panel_common.h" +#include "ss_ddi_poc_common.h" + +#define DEBUG_POC_CNT 100000 + +static int ss_poc_erase(struct samsung_display_driver_data *vdd) +{ + int i; + int ret = 0; + int erase_delay_ms = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd\n"); + return -EINVAL; + } + + LCD_INFO("ss_poc_erase !! \n"); + ss_send_cmd(vdd, TX_POC_ERASE); + + erase_delay_ms = vdd->poc_driver.erase_delay_ms/100; /* Panel dtsi set */ + LCD_INFO("erase_delay_ms (%d)\n", erase_delay_ms); + + for (i = 0; i < erase_delay_ms; i++) { + msleep(100); + if (unlikely(atomic_read(&vdd->poc_driver.cancel))) { + LCD_ERR("cancel poc erase by user\n"); + ret = -EIO; + goto cancel_poc; + } + } + ss_send_cmd(vdd, TX_POC_ERASE1); + LCD_INFO("ss_poc_erase done!! \n"); + +cancel_poc: + atomic_set(&vdd->poc_driver.cancel, 0); + return ret; +} + +static int ss_poc_write(struct samsung_display_driver_data *vdd, char *data, u32 write_pos, u32 write_size) +{ + struct dsi_panel_cmd_set *poc_write_continue, *poc_write_1byte = NULL; + int pos = 0; + int ret = 0; + int type; + int i; + int erase_delay_ms = 0; + int write_delay_us = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd\n"); + return -EINVAL; + } + + //mutex_lock(&vdd->vdd_poc_operation_lock); + vdd->poc_driver.poc_operation = true; + + /* TODO: implement ss_dsi_samsung_poc_perf_mode_ctl() to boost up mdss performance... + ss_dsi_samsung_poc_perf_mode_ctl(ctrl_pdata, true); + */ + + /* enter exclusive mode*/ + mutex_lock(&vdd->exclusive_tx.ex_tx_lock); + vdd->exclusive_tx.enable = 1; + for (type = TX_POC_CMD_START + 1; type < TX_POC_CMD_END; type++) + ss_set_exclusive_tx_packet(vdd, type, 1); + + if (write_pos == 0) { + LCD_INFO("1st, ss_poc_erase +++++ \n"); + ss_send_cmd(vdd, TX_POC_ERASE); + + erase_delay_ms = vdd->poc_driver.erase_delay_ms/100; /* Panel dtsi set */ + LCD_INFO("erase_delay_ms (%d)\n", erase_delay_ms); + + for (i = 0; i < erase_delay_ms; i++) { + msleep(100); + if (unlikely(atomic_read(&vdd->poc_driver.cancel))) { + LCD_ERR("cancel poc erase by user\n"); + ret = -EIO; + goto cancel_poc; + } + } + ss_send_cmd(vdd, TX_POC_ERASE1); + LCD_INFO("ss_poc_erase ----- \n"); + + ss_send_cmd(vdd, TX_POC_PRE_WRITE); + } + + write_delay_us = vdd->poc_driver.write_delay_us; /* Panel dtsi set */ + LCD_INFO("write_delay_us (%d)\n", write_delay_us); + LCD_INFO("write_pos : %d size : %d +++++\n", write_pos, write_size); + + poc_write_continue = ss_get_cmds(vdd, TX_POC_WRITE_CONTINUE3); + if (IS_ERR_OR_NULL(poc_write_continue)) { + LCD_ERR("No cmds for TX_POC_WRITE_CONTINUE3..\n"); + ret = -EINVAL; + goto cancel_poc; + } + + poc_write_1byte = ss_get_cmds(vdd, TX_POC_WRITE_1BYTE); + if (IS_ERR_OR_NULL(poc_write_1byte)) { + LCD_ERR("No cmds for TX_POC_WRITE_1BYTE..\n"); + ret = -EINVAL; + goto cancel_poc; + } + + for (pos = write_pos; pos < (write_pos + write_size); pos++) { + if (unlikely(atomic_read(&vdd->poc_driver.cancel))) { + LCD_ERR("cancel poc write by user\n"); + ret = -EIO; + goto cancel_poc; + } + + if (pos > 1 && (pos % 256 == 0)) { + ss_send_cmd(vdd, TX_POC_WRITE_CONTINUE); + usleep_range(write_delay_us, write_delay_us+1); + ss_send_cmd(vdd, TX_POC_WRITE_CONTINUE2); + udelay(2); + poc_write_continue->cmds[0].msg.tx_buf[6] = (pos & 0xFF0000) >> 16; + poc_write_continue->cmds[0].msg.tx_buf[7] = (pos & 0x00FF00) >> 8; + poc_write_continue->cmds[0].msg.tx_buf[8] = 0; + ss_send_cmd(vdd, TX_POC_WRITE_CONTINUE3); + } + + poc_write_1byte->cmds[0].msg.tx_buf[1] = data[pos]; + ss_send_cmd(vdd, TX_POC_WRITE_1BYTE); + udelay(2); + + if (!(pos % DEBUG_POC_CNT)) + LCD_INFO("cur_write_pos : %d data : 0x%x\n", pos, data[pos]); + } + +cancel_poc: + if (unlikely(atomic_read(&vdd->poc_driver.cancel))) { + LCD_ERR("cancel poc write by user\n"); + atomic_set(&vdd->poc_driver.cancel, 0); + ret = -EIO; + } + + if (pos == POC_IMG_SIZE || ret == -EIO) { + LCD_INFO("POC_IMG_SIZE : %d cur_write_pos : %d ret : %d\n", POC_IMG_SIZE, pos, ret); + ss_send_cmd(vdd, TX_POC_POST_WRITE); + } + + /* TODO: implement ss_dsi_samsung_poc_perf_mode_ctl() to boost up mdss performance... + ss_dsi_samsung_poc_perf_mode_ctl(ctrl_pdata, false); + */ + + /* exit exclusive mode*/ + for (type = TX_POC_CMD_START + 1; type < TX_POC_CMD_END; type++) + ss_set_exclusive_tx_packet(vdd, type, 0); + vdd->exclusive_tx.enable = 0; + mutex_unlock(&vdd->exclusive_tx.ex_tx_lock); + wake_up_all(&vdd->exclusive_tx.ex_tx_waitq); + + vdd->poc_driver.poc_operation = false; + //mutex_unlock(&vdd->vdd_poc_operation_lock); + LCD_INFO("ss_poc_write ----- \n"); + return ret; +} + +static int ss_poc_read(struct samsung_display_driver_data *vdd, u8 *buf, u32 read_pos, u32 read_size) +{ + struct dsi_panel_cmd_set *poc_tx_read; + struct cpufreq_policy *policy; + int cpu; + int pos = 0; + int type; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd\n"); + return -EINVAL; + } + + LCD_INFO("ss_poc_read pos : %d size : %d+++++ \n", read_pos, read_size); + //mutex_lock(&vdd->vdd_poc_operation_lock); + vdd->poc_driver.poc_operation = true; + + get_online_cpus(); + for_each_online_cpu(cpu) { + if (cpu < 4) { + policy = cpufreq_cpu_get(cpu); + if (policy) { + policy->user_policy.min = 1900800; + cpufreq_update_policy(cpu); + cpufreq_cpu_put(policy); + } + } + } + put_online_cpus(); + + /* TODO: implement ss_dsi_samsung_poc_perf_mode_ctl() to boost up mdss performance... + ss_dsi_samsung_poc_perf_mode_ctl(ctrl_pdata, true); + */ + + /* enter exclusive mode*/ + mutex_lock(&vdd->exclusive_tx.ex_tx_lock); + vdd->exclusive_tx.enable = 1; + for (type = TX_POC_CMD_START + 1; type < TX_POC_CMD_END; type++) + ss_set_exclusive_tx_packet(vdd, type, 1); + ss_set_exclusive_tx_packet(vdd, TX_REG_READ_POS, 1); + ss_set_exclusive_tx_packet(vdd, RX_POC_READ, 1); + + poc_tx_read = ss_get_cmds(vdd, TX_POC_READ); + if (IS_ERR_OR_NULL(poc_tx_read)) { + LCD_ERR("No cmds for TX_POC_READ..\n"); + ret = -EINVAL; + goto cancel_poc; + } + //poc_rx_read = get_panel_rx_cmds(ctrl_pdata, RX_POC_READ); + + ss_send_cmd(vdd, TX_POC_PRE_READ); + for (pos = read_pos; pos < (read_pos + read_size); pos++) { + if (unlikely(atomic_read(&vdd->poc_driver.cancel))) { + LCD_ERR("cancel poc read by user\n"); + ret = -EIO; + goto cancel_poc; + } + + poc_tx_read->cmds[0].msg.tx_buf[6] = (pos & 0xFF0000) >> 16; + poc_tx_read->cmds[0].msg.tx_buf[7] = (pos & 0x00FF00) >> 8; + poc_tx_read->cmds[0].msg.tx_buf[8] = pos & 0x0000FF; + + ss_send_cmd(vdd, TX_POC_READ); + usleep_range(200, 200); + ss_panel_data_read(vdd, RX_POC_READ, buf + pos, LEVEL_KEY_NONE); + + LCD_DEBUG("[IOCTRL] pos = %d, 0x%x\n", pos, buf[pos]); + + if (!(pos % DEBUG_POC_CNT)) + LCD_INFO("cur_read_pos : %d data : 0x%x\n", pos, buf[pos]); + } + +cancel_poc: + if (unlikely(atomic_read(&vdd->poc_driver.cancel))) { + LCD_ERR("cancel poc read by user\n"); + atomic_set(&vdd->poc_driver.cancel, 0); + ret = -EIO; + } + + if (pos == POC_IMG_SIZE || ret == -EIO) { + LCD_INFO("POC_IMG_SIZE : %d cur_read_pos : %d ret : %d\n", POC_IMG_SIZE, pos, ret); + ss_send_cmd(vdd, TX_POC_POST_READ); + } + + /* exit exclusive mode*/ + for (type = TX_POC_CMD_START + 1; type < TX_POC_CMD_END; type++) + ss_set_exclusive_tx_packet(vdd, type, 0); + ss_set_exclusive_tx_packet(vdd, TX_REG_READ_POS, 0); + ss_set_exclusive_tx_packet(vdd, RX_POC_READ, 0); + vdd->exclusive_tx.enable = 0; + mutex_unlock(&vdd->exclusive_tx.ex_tx_lock); + wake_up_all(&vdd->exclusive_tx.ex_tx_waitq); + + /* TODO: implement ss_dsi_samsung_poc_perf_mode_ctl() to boost up mdss performance... + ss_dsi_samsung_poc_perf_mode_ctl(ctrl_pdata, false); + */ + + get_online_cpus(); + for_each_online_cpu(cpu) { + if (cpu < 4) { + policy = cpufreq_cpu_get(cpu); + if (policy) { + policy->user_policy.min = 300000; + cpufreq_update_policy(cpu); + cpufreq_cpu_put(policy); + } + } + } + put_online_cpus(); + + vdd->poc_driver.poc_operation = false; + //mutex_unlock(&vdd->vdd_poc_operation_lock); + LCD_INFO("ss_poc_read ----- \n"); + return ret; +} + +static int ss_poc_checksum(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("POC: checksum\n"); + return 0; +} + +static int ss_poc_check_flash(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("POC: check flash\n"); + return 0; +} + +static int ss_dsi_poc_ctrl(struct samsung_display_driver_data *vdd, u32 cmd) +{ + int ret = 0; + + if (cmd >= MAX_POC_OP) { + LCD_ERR("%s invalid poc_op %d\n", __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case POC_OP_ERASE: + ret = ss_poc_erase(vdd); + break; + case POC_OP_WRITE: + 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_ERR("%s, failed to write poc-write-seq\n", __func__); + return ret; + } + break; + case POC_OP_READ: + 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_ERR("%s, failed to write poc-read-seq\n", __func__); + 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_ERR("%s invalid poc_op %d\n", __func__, cmd); + break; + } + 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_ERR("no vdd\n"); + return -EINVAL; + } + + LCD_INFO("POC IOCTL CMD=%d\n", cmd); + + switch (cmd) { + case IOC_GET_POC_CHKSUM: + ret = ss_dsi_poc_ctrl(vdd, POC_OP_CHECKSUM); + if (ret) { + LCD_ERR("%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); + if (ret) { + LCD_ERR("%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 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; + + LCD_INFO("POC Open !!\n"); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd\n"); + return -ENOMEM; + } + + if (likely(!vdd->poc_driver.wbuf)) { + vdd->poc_driver.wbuf = vmalloc(POC_IMG_SIZE); + if (unlikely(!vdd->poc_driver.wbuf)) { + LCD_ERR("%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(POC_IMG_SIZE); + if (unlikely(!vdd->poc_driver.rbuf)) { + vfree(vdd->poc_driver.wbuf); + vdd->poc_driver.wbuf = NULL; + LCD_ERR("%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("POC Release\n"); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd\n"); + return -ENOMEM; + } + + 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); + + return ret; +} + +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 = 0; + + LCD_INFO("ss_dsi_poc_read \n"); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if (unlikely(!buf)) { + LCD_ERR("invalid read buffer\n"); + return -EINVAL; + } + + if (unlikely(*ppos + count > POC_IMG_SIZE)) { + LCD_ERR("POC:ERR:%s: invalid read size pos %d, size %d\n", + __func__, (int)*ppos, (int)count); + return -EINVAL; + } + + vdd->poc_driver.rpos = *ppos; + vdd->poc_driver.rsize = count; + ret = ss_dsi_poc_ctrl(vdd, POC_OP_READ); + if (ret) { + LCD_ERR("fail to read poc (%d)\n", ret); + return ret; + } + + return simple_read_from_buffer(buf, count, ppos, vdd->poc_driver.rbuf, POC_IMG_SIZE); +} + +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; + + LCD_INFO("ss_dsi_poc_write : count (%d), ppos(%d) \n", (int)count, (int)*ppos); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV;; + } + + if (unlikely(!buf)) { + LCD_ERR("invalid read buffer\n"); + return -EINVAL; + } + + if (unlikely(*ppos + count > POC_IMG_SIZE)) { + LCD_ERR("POC:ERR:%s: invalid write size pos %d, size %d\n", + __func__, (int)*ppos, (int)count); + return -EINVAL; + } + + vdd->poc_driver.wpos = *ppos; + vdd->poc_driver.wsize = count; + + ret = simple_write_to_buffer(vdd->poc_driver.wbuf, POC_IMG_SIZE, ppos, buf, count); + if (unlikely(ret < 0)) { + LCD_ERR("%s, failed to simple_write_to_buffer \n", __func__); + return ret; + } + + ret = ss_dsi_poc_ctrl(vdd, POC_OP_WRITE); + if (ret) { + LCD_ERR("fail to write poc (%d)\n", ret); + return ret; + } + + return count; +} + +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, +}; + +#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, +}; + +static int poc_get_efs_s32(char *filename, int *value) +{ + mm_segment_t old_fs; + struct file *filp = NULL; + int fsize = 0, nread, ret = 0; + + if (!filename || !value) { + pr_err("%s invalid parameter\n", __func__); + return -EINVAL; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + filp = filp_open(filename, O_RDONLY, 0440); + if (IS_ERR(filp)) { + ret = PTR_ERR(filp); + if (ret == -ENOENT) + pr_err("%s file(%s) not exist\n", __func__, filename); + else + pr_info("%s file(%s) open error(ret %d)\n", + __func__, filename, ret); + set_fs(old_fs); + return -EPOCEFS_NOENT; + } + + if (filp->f_path.dentry && filp->f_path.dentry->d_inode) + fsize = filp->f_path.dentry->d_inode->i_size; + + if (fsize == 0) { + pr_err("%s invalid file(%s) size %d\n", + __func__, filename, fsize); + ret = -EPOCEFS_EMPTY; + goto exit; + } + + nread = vfs_read(filp, (char __user *)value, 4, &filp->f_pos); + if (nread != 4) { + pr_err("%s failed to read (ret %d)\n", __func__, nread); + ret = -EPOCEFS_READ; + goto exit; + } + + pr_info("%s %s(size %d) : %d\n", __func__, filename, fsize, *value); + +exit: + filp_close(filp, current->files); + set_fs(old_fs); + + return ret; +} + +static int poc_get_efs_image_index_org(char *filename, int *value) +{ + mm_segment_t old_fs; + struct file *filp = NULL; + int fsize = 0, nread, rc, ret = 0; + char binary; + int image_index, chksum; + u8 buf[128]; + + if (!filename || !value) { + pr_err("%s invalid parameter\n", __func__); + return -EINVAL; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + filp = filp_open(filename, O_RDONLY, 0440); + if (IS_ERR(filp)) { + ret = PTR_ERR(filp); + if (ret == -ENOENT) + pr_err("%s file(%s) not exist\n", __func__, filename); + else + pr_info("%s file(%s) open error(ret %d)\n", + __func__, filename, ret); + set_fs(old_fs); + return -EPOCEFS_NOENT; + } + + if (filp->f_path.dentry && filp->f_path.dentry->d_inode) + fsize = filp->f_path.dentry->d_inode->i_size; + + if (fsize == 0 || fsize > ARRAY_SIZE(buf)) { + pr_err("%s invalid file(%s) size %d\n", + __func__, filename, fsize); + ret = -EPOCEFS_EMPTY; + goto exit; + } + + memset(buf, 0, sizeof(buf)); + nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos); + if (nread != fsize) { + pr_err("%s failed to read (ret %d)\n", __func__, nread); + ret = -EPOCEFS_READ; + goto exit; + } + + rc = sscanf(buf, "%c %d %d", &binary, &image_index, &chksum); + if (rc != 3) { + pr_err("%s failed to sscanf %d\n", __func__, rc); + ret = -EINVAL; + goto exit; + } + + pr_info("%s %s(size %d) : %c %d %d\n", + __func__, filename, fsize, binary, image_index, chksum); + + *value = image_index; + +exit: + filp_close(filp, current->files); + set_fs(old_fs); + + return ret; +} + +static int poc_get_efs_image_index(char *filename, int *value) +{ + mm_segment_t old_fs; + struct file *filp = NULL; + int fsize = 0, nread, rc, ret = 0; + int image_index, seek; + u8 buf[128]; + + if (!filename || !value) { + pr_err("%s invalid parameter\n", __func__); + return -EINVAL; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + filp = filp_open(filename, O_RDONLY, 0440); + if (IS_ERR(filp)) { + ret = PTR_ERR(filp); + if (ret == -ENOENT) + pr_err("%s file(%s) not exist\n", __func__, filename); + else + pr_info("%s file(%s) open error(ret %d)\n", + __func__, filename, ret); + set_fs(old_fs); + return -EPOCEFS_NOENT; + } + + if (filp->f_path.dentry && filp->f_path.dentry->d_inode) + fsize = filp->f_path.dentry->d_inode->i_size; + + if (fsize == 0 || fsize > ARRAY_SIZE(buf)) { + pr_err("%s invalid file(%s) size %d\n", + __func__, filename, fsize); + ret = -EPOCEFS_EMPTY; + goto exit; + } + + memset(buf, 0, sizeof(buf)); + nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos); + if (nread != fsize) { + pr_err("%s failed to read (ret %d)\n", __func__, nread); + ret = -EPOCEFS_READ; + goto exit; + } + + rc = sscanf(buf, "%d,%d", &image_index, &seek); + if (rc != 2) { + pr_err("%s failed to sscanf %d\n", __func__, rc); + ret = -EINVAL; + goto exit; + } + + pr_info("%s %s(size %d) : %d %d\n", + __func__, filename, fsize, image_index, seek); + + *value = image_index; + +exit: + filp_close(filp, current->files); + set_fs(old_fs); + + return ret; +} + +#ifdef CONFIG_SEC_FACTORY +#define POC_TOTAL_TRY_COUNT_FILE_PATH ("/efs/FactoryApp/poc_totaltrycount") +#define POC_TOTAL_FAIL_COUNT_FILE_PATH ("/efs/FactoryApp/poc_totalfailcount") +#else +#define POC_TOTAL_TRY_COUNT_FILE_PATH ("/efs/etc/poc/totaltrycount") +#define POC_TOTAL_FAIL_COUNT_FILE_PATH ("/efs/etc/poc/totalfailcount") +#endif + +#define POC_INFO_FILE_PATH ("/efs/FactoryApp/poc_info") +#define POC_USER_FILE_PATH ("/efs/FactoryApp/poc_user") + +static 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 dpui_info *dpui = data; + char tbuf[MAX_DPUI_VAL_LEN]; + int total_fail_cnt; + int total_try_cnt; + int size, ret, poci, poci_org; + + if (dpui == NULL) { + LCD_ERR("err: dpui is null\n"); + return 0; + } + + if (poc == NULL) { + LCD_ERR("err: poc is null\n"); + return 0; + } + + ret = poc_get_efs_s32(POC_TOTAL_TRY_COUNT_FILE_PATH, &total_try_cnt); + if (ret < 0) + total_try_cnt = (ret > -MAX_EPOCEFS) ? ret : -1; + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", total_try_cnt); + set_dpui_field(DPUI_KEY_PNPOCT, tbuf, size); + + ret = poc_get_efs_s32(POC_TOTAL_FAIL_COUNT_FILE_PATH, &total_fail_cnt); + if (ret < 0) + total_fail_cnt = (ret > -MAX_EPOCEFS) ? ret : -1; + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", total_fail_cnt); + set_dpui_field(DPUI_KEY_PNPOCF, tbuf, size); + + ret = poc_get_efs_image_index_org(POC_INFO_FILE_PATH, &poci_org); + if (ret < 0) + poci_org = -EPOCEFS_IMGIDX + ret; + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poci_org); + set_dpui_field(DPUI_KEY_PNPOCI_ORG, tbuf, size); + + ret = poc_get_efs_image_index(POC_USER_FILE_PATH, &poci); + if (ret < 0) + poci = -EPOCEFS_IMGIDX + ret; + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poci); + set_dpui_field(DPUI_KEY_PNPOCI, tbuf, size); + + LCD_INFO("poc dpui: try=%d, fail=%d, id=%d, %d\n", + total_try_cnt, total_fail_cnt, poci, poci_org); + + 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); +} + +int ss_dsi_poc_init(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + char devname[5]; + + struct dsi_panel *panel = NULL; + struct mipi_dsi_host *host = NULL; + struct dsi_display *display = NULL; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if (!vdd->poc_driver.is_support) { + LCD_ERR("Not Support POC Driver! \n"); + return -ENODEV; + } + + 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) + sprintf(devname, "poc"); + else + sprintf(devname, "poc%d", vdd->ndx); + + LCD_ERR("POC Driver Init ++\n"); + 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); + + vdd->panel_func.samsung_poc_ctrl = ss_dsi_poc_ctrl; + + ret = misc_register(&vdd->poc_driver.dev); + if (ret) { + LCD_ERR("failed to register POC driver : %d\n", ret); + return ret; + } + + ss_dsi_poc_register_dpui(&vdd->poc_driver); + + LCD_ERR("POC Driver Init --\n"); + return ret; +} diff --git a/drivers/gpu/drm/msm/samsung/ss_ddi_poc_common.h b/drivers/gpu/drm/msm/samsung/ss_ddi_poc_common.h new file mode 100755 index 000000000000..0de6f29ee56b --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_ddi_poc_common.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Samsung's POC Driver + * Author: ChangJae Jang + * + * 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 +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SUPPORT_POC_2_0 +#define POC_IMG_SIZE (546008) +#else +#define POC_IMG_SIZE (532816) +#endif + +#define POC_IMG_ADDR (0x000000) +#define POC_PAGE (4096) +#define POC_TEST_PATTERN_SIZE (1024) + +/* Register to cnotrol POC */ +#define POC_CTRL_REG 0xEB + +#ifdef CONFIG_SUPPORT_POC_FLASH +int ss_dsi_poc_init(struct samsung_display_driver_data *vdd); +#else +static inline int ss_dsi_poc_init(struct samsung_display_driver_data *vdd) { return 0; } +#endif + +#endif diff --git a/drivers/gpu/drm/msm/samsung/ss_ddi_spi_common.c b/drivers/gpu/drm/msm/samsung/ss_ddi_spi_common.c new file mode 100755 index 000000000000..fc74a9835a35 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_ddi_spi_common.c @@ -0,0 +1,346 @@ +/* + * ================================================================= + * + * + * Description: samsung display common file + * + * Author: samsung display driver team + * Company: Samsung Electronics + * + * ================================================================ + */ + +#include "ss_ddi_spi_common.h" + +#define SPI_CTRL_RX 0x00 + +int ss_spi_read(struct spi_device *spi, u8 *buf, + int tx_bpw, int rx_bpw, int tx_size, int rx_size, u8 rx_addr) +{ + int i; + u8 *rx_buf = NULL; + u8 *tx_buf = NULL; +#if defined(CONFIG_SEC_FACTORY) + u8 *dummy_buf = NULL; +#endif + struct samsung_display_driver_data *vdd = NULL; + struct spi_message msg; + int ret = 0; + + struct spi_transfer xfer[] = { + { .bits_per_word = tx_bpw, .len = tx_size, }, + { .bits_per_word = rx_bpw, .len = rx_size, }, +#if defined(CONFIG_SEC_FACTORY) + { .bits_per_word = rx_bpw, .len = 1, }, +#endif + }; + + if (!spi) { + LCD_ERR("no spi device..\n"); + return -EINVAL; + } + + vdd = container_of(spi->dev.driver, + struct samsung_display_driver_data, + spi_driver.driver); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + mutex_lock(&vdd->ss_spi_lock); + + tx_buf = kmalloc(tx_size, GFP_KERNEL | GFP_DMA); + if (tx_buf == NULL) { + LCD_ERR("fail to alloc tx_buf..\n"); + goto err; + } + tx_buf[0] = rx_addr; // TX + xfer[0].tx_buf = tx_buf; + + rx_buf = kmalloc(rx_size, GFP_KERNEL | GFP_DMA); + if (rx_buf == NULL) { + LCD_ERR("fail to alloc rx_buf..\n"); + goto err; + } + xfer[1].rx_buf = rx_buf; + +#if defined(CONFIG_SEC_FACTORY) + dummy_buf = kmalloc(xfer[2].len, GFP_KERNEL | GFP_DMA); + if (dummy_buf == NULL) { + LCD_ERR("fail to alloc dummy_buf..\n"); + goto err; + } + xfer[2].rx_buf = dummy_buf; +#endif + + if (vdd->ddi_spi_status == DDI_SPI_SUSPEND) { + LCD_DEBUG("ddi spi is suspend..\n"); + ret = -EINVAL; + goto err; + } + + LCD_DEBUG("++\n"); + + spi_message_init(&msg); + + for (i = 0; i < ARRAY_SIZE(xfer); i++) + spi_message_add_tail(&xfer[i], &msg); + + ret = spi_sync(spi, &msg); + if (ret) { + pr_err("[mdss spi] %s : spi_sync fail..\n", __func__); + goto err; + } + + LCD_DEBUG("rx(0x%x) : ", tx_buf[1]); + for (i = 0; i < rx_size; i++) { + LCD_DEBUG("[%d] %02x ", i+1, rx_buf[i]); + } + LCD_DEBUG("\n"); + + memcpy(buf, rx_buf, rx_size); +#if KERNEL_VER == 414 + if (vdd->ddi_spi_cs_high_gpio_for_gpara > 0) { + LCD_INFO("%s wait \n", dev_name(spi->controller->dev.parent)); + + /* max wait for 4 second */ + for (i = 0; i < 200; i++) { + if (pm_runtime_status_suspended(spi->controller->dev.parent)) + break; + + msleep(20); + } + + if (!pm_runtime_status_suspended(spi->controller->dev.parent)) { + LCD_INFO("%s is not suspend for 4second\n", dev_name(spi->controller->dev.parent)); + + pm_runtime_barrier(spi->controller->dev.parent); + + LCD_INFO("%s suspend status : %d \n", dev_name(spi->controller->dev.parent), + pm_runtime_status_suspended(spi->controller->dev.parent)); + } + + LCD_INFO("%s end \n", dev_name(spi->controller->dev.parent)); + } +#endif +err: + mutex_unlock(&vdd->ss_spi_lock); + + if (rx_buf) + kfree(rx_buf); + if (tx_buf) + kfree(tx_buf); +#if defined(CONFIG_SEC_FACTORY) + if (dummy_buf) + kfree(dummy_buf); +#endif + + LCD_DEBUG("--\n"); + + return ret; +} + +static int ss_spi_parse_dt(struct spi_device *spi_dev) +{ + struct device_node *np; + int val, ret; + + np = spi_dev->dev.of_node; + if (!np) { + LCD_ERR("of_node is null..\n"); + return -ENODEV; + } + + LCD_ERR("np name : %s\n", np->full_name); + + ret = of_property_read_u32(np, "spi-max-frequency", &val); + if (!ret) + spi_dev->max_speed_hz = val; + else + LCD_ERR("No spi-max-frequency..\n"); + + ret = of_property_read_u32(np, "spi-bpw", &val); + if (!ret) + spi_dev->bits_per_word = val; + else + LCD_ERR("No spi-bpw..\n"); + + ret = of_property_read_u32(np, "spi-mode", &val); + if (!ret) + spi_dev->mode = (val > 3) ? 0 : val; + else + LCD_ERR("No spi-mode..\n"); + + LCD_INFO("max speed (%d), bpw (%d), mode (%d) \n", + spi_dev->max_speed_hz, spi_dev->bits_per_word, spi_dev->mode); + + return ret; +} + +static int ss_spi_probe(struct spi_device *client) +{ + struct samsung_display_driver_data *vdd; + int ret = 0; + + if (client == NULL) { + LCD_ERR("%s : ss_spi_probe spi is null\n", __func__); + return -EINVAL; + } + + vdd = container_of(client->dev.driver, + struct samsung_display_driver_data, + spi_driver.driver); + + LCD_INFO("chip select(%d), bus number(%d)\n", + client->chip_select, client->master->bus_num); + + ret = ss_spi_parse_dt(client); + if (ret) { + LCD_ERR("can not parse from ddi spi dt..\n"); + return ret; + } + + ret = spi_setup(client); + if (ret < 0) { + LCD_ERR("%s : spi_setup error (%d)\n", __func__, ret); + return ret; + } + + vdd->spi_dev = client; + dev_set_drvdata(&client->dev, vdd); + + LCD_ERR("%s : --\n", __func__); + return ret; +} + +static int ss_spi_remove(struct spi_device *spi) +{ + pr_err("[mdss] %s : remove \n", __func__); + return 0; +} + +static int ddi_spi_suspend(struct device *dev) +{ + struct samsung_display_driver_data *vdd = + (struct samsung_display_driver_data *)dev_get_drvdata(dev); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + mutex_lock(&vdd->ss_spi_lock); + vdd->ddi_spi_status = DDI_SPI_SUSPEND; + LCD_DEBUG(" %d\n", vdd->ddi_spi_status); + mutex_unlock(&vdd->ss_spi_lock); + + return 0; +} + +static int ddi_spi_resume(struct device *dev) +{ + struct samsung_display_driver_data *vdd = + (struct samsung_display_driver_data *)dev_get_drvdata(dev); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + mutex_lock(&vdd->ss_spi_lock); + vdd->ddi_spi_status = DDI_SPI_RESUME; + LCD_DEBUG(" %d\n", vdd->ddi_spi_status); + mutex_unlock(&vdd->ss_spi_lock); + + return 0; +} + +static int ddi_spi_reboot_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct samsung_display_driver_data *vdd = container_of(nb, + struct samsung_display_driver_data, spi_notif); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + mutex_lock(&vdd->ss_spi_lock); + vdd->ddi_spi_status = DDI_SPI_SUSPEND; + LCD_ERR(" %d\n", vdd->ddi_spi_status); + mutex_unlock(&vdd->ss_spi_lock); + + return NOTIFY_OK; +} + +static const struct dev_pm_ops ddi_spi_pm_ops = { + .suspend = ddi_spi_suspend, + .resume = ddi_spi_resume, +}; + +static const struct of_device_id ddi_spi_match_table[] = { + { .compatible = "ss,ddi-spi", + }, + {} +}; + +#if 0 +static struct spi_driver ddi_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = ddi_spi_match_table, + .pm = &ddi_spi_pm_ops, + }, + .probe = ss_spi_probe, + .remove = ss_spi_remove, +}; + +static struct notifier_block ss_spi_reboot_notifier = { + .notifier_call = ddi_spi_reboot_cb, +}; +#endif + +int ss_spi_init(struct samsung_display_driver_data *vdd) +{ + int ret; + char drivername[10]; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return -ENODEV; + } + + if(!vdd->samsung_support_ddi_spi) { + LCD_ERR("%s : No support for ddi spi\n", __func__); + return 0; + } + + if (vdd->ndx == PRIMARY_DISPLAY_NDX) + sprintf(drivername, "ddi_spi"); + else + sprintf(drivername, "ddi_spi%d", vdd->ndx); + + LCD_ERR("%s : ++\n", __func__); + + vdd->spi_driver.driver.name = drivername; + vdd->spi_driver.driver.owner = THIS_MODULE; + vdd->spi_driver.driver.of_match_table = ddi_spi_match_table; + vdd->spi_driver.driver.pm = &ddi_spi_pm_ops; + vdd->spi_driver.probe = ss_spi_probe; + vdd->spi_driver.remove = ss_spi_remove; + vdd->spi_notif.notifier_call = ddi_spi_reboot_cb; + vdd->ddi_spi_status = DDI_SPI_RESUME; + + ret = spi_register_driver(&vdd->spi_driver); + if (ret) + LCD_ERR("%s : ddi spi register fail : %d\n", __func__, ret); + + register_reboot_notifier(&vdd->spi_notif); + + LCD_ERR("%s : --\n", __func__); + return ret; +} diff --git a/drivers/gpu/drm/msm/samsung/ss_ddi_spi_common.h b/drivers/gpu/drm/msm/samsung/ss_ddi_spi_common.h new file mode 100755 index 000000000000..6115660371b3 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_ddi_spi_common.h @@ -0,0 +1,23 @@ +#ifndef SAMSUNG_SPI_COMMON_H +#define SAMSUNG_SPI_COMMON_H + +#include +#include +#include +#include +#include +#include +#include "ss_dsi_panel_common.h" + +enum DDI_SPI_STATUS { + DDI_SPI_SUSPEND = 0, + DDI_SPI_RESUME, +}; + +struct samsung_display_driver_data; + +int ss_spi_read(struct spi_device *spi, u8 *buf, + int tx_bpw, int rx_bpw, int tx_size, int rx_size, u8 rx_addr); +int ss_spi_init(struct samsung_display_driver_data *vdd); + +#endif diff --git a/drivers/gpu/drm/msm/samsung/ss_dpui_common.c b/drivers/gpu/drm/msm/samsung/ss_dpui_common.c new file mode 100755 index 000000000000..ee78dae4ec6a --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dpui_common.c @@ -0,0 +1,480 @@ +/* + * linux/drivers/gpu/drm/msm/samsung/ss_dpui_common.c + * + * 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 +#include +#include +#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); + +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", +#ifdef CONFIG_SUPPORT_POC_FLASH + [DPUI_KEY_PNPOCT] = "PNPOCT", + [DPUI_KEY_PNPOCF] = "PNPOCF", + [DPUI_KEY_PNPOCI] = "PNPOCI", + [DPUI_KEY_PNPOCI_ORG] = "PNPOCI_ORG", +#endif + [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", + + /* GPU */ + [DPUI_KEY_QCT_GPU_PF] = "QCT_GPU_PF", +}; + +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), +#ifdef CONFIG_SUPPORT_POC_FLASH + 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), +#endif + 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), + + /* GPU */ + DPUI_FIELD_INIT(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL, + DPUI_VAR_U32, DPUI_AUTO_CLEAR_ON, "0", DPUI_KEY_QCT_GPU_PF), + }, +}; + +/** + * 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); +} + +void clear_dpui_log(enum dpui_log_level level, enum dpui_type type) +{ + int i; + + if (level < 0 || level >= MAX_DPUI_LOG_LEVEL) { + pr_err("%s invalid log level %d\n", __func__, level); + return; + } + + 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); +} + +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; +} + +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); diff --git a/drivers/gpu/drm/msm/samsung/ss_dpui_common.h b/drivers/gpu/drm/msm/samsung/ss_dpui_common.h new file mode 100755 index 000000000000..6d02303248ca --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dpui_common.h @@ -0,0 +1,138 @@ +/* + * linux/drivers/gpu/drm/msm/samsung/ss_dpui_common.h + * + * Header file for Samsung Common LCD Driver. + * + * Copyright (c) 2017 Samsung Electronics + * Gwanghui Lee + * + * 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 */ +#ifdef CONFIG_SUPPORT_POC_FLASH + 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 */ +#endif + 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 recovery count */ + DPUI_KEY_QCT_SSLOG, /* display controller ss debugging log */ + + /* GPU */ + DPUI_KEY_QCT_GPU_PF, /* GPU Page Fault 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]; +}; + +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); +void 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 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__ */ diff --git a/drivers/gpu/drm/msm/samsung/ss_dsi_mdnie_lite_common.c b/drivers/gpu/drm/msm/samsung/ss_dsi_mdnie_lite_common.c new file mode 100755 index 000000000000..602fe7c38509 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dsi_mdnie_lite_common.c @@ -0,0 +1,1742 @@ +/* + * ================================================================= + * + * + * Description: samsung display common file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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_mdnie_lite_common.h" + +#define MDNIE_LITE_TUN_DEBUG + +#ifdef MDNIE_LITE_TUN_DEBUG +#define DPRINT(x...) printk(KERN_ERR "[SDE_mdnie] " x) +#else +#define DPRINT(x...) +#endif + +static struct class *mdnie_class; + +char mdnie_app_name[][NAME_STRING_MAX] = { + "UI_APP", + "VIDEO_APP", + "VIDEO_WARM_APP", + "VIDEO_COLD_APP", + "CAMERA_APP", + "NAVI_APP", + "GALLERY_APP", + "VT_APP", + "BROWSER_APP", + "eBOOK_APP", + "EMAIL_APP", + "GAME_LOW_APP", + "GAME_MID_APP", + "GAME_HIGH_APP", + "VIDEO_ENHANCER", + "VIDEO_ENHANCER_THIRD", + "TDMB_APP", +}; + +char mdnie_mode_name[][NAME_STRING_MAX] = { + "DYNAMIC_MODE", + "STANDARD_MODE", +#if defined(NATURAL_MODE_ENABLE) + "NATURAL_MODE", +#endif + "MOVIE_MODE", + "AUTO_MODE", + "READING_MODE", +}; + +char outdoor_name[][NAME_STRING_MAX] = { + "OUTDOOR_OFF_MODE", + "OUTDOOR_ON_MODE", +}; + +char mdnie_hdr_name[][NAME_STRING_MAX] = { + "HDR_OFF", + "HDR_1", + "HDR_2", + "HDR_3" +}; + +char mdnie_light_notification_name[][NAME_STRING_MAX] = { + "LIGHT_NOTIFICATION_OFF", + "LIGHT_NOTIFICATION_ON" +}; + +/* send_dsi_tcon_mdnie_register(): + * tx mdnie packet to panel wich vdd is included. + */ +static void send_dsi_tcon_mdnie_register(struct samsung_display_driver_data *vdd, + struct dsi_cmd_desc *tune_data_dsi, + struct mdnie_lite_tun_type *tune) +{ + struct mdnie_lite_tune_data *mdnie_data = vdd->mdnie.mdnie_data; + struct dsi_panel_cmd_set *pcmds; + int i; + + if (!vdd->mdnie.support_mdnie) + return; + + if (!tune_data_dsi || !mdnie_data->dsi_bypass_mdnie_size) { + DPRINT("Command Tx Fail, tune_data_dsi=%p(%d), vdd=%p, tune=%p\n", + tune_data_dsi, mdnie_data->dsi_bypass_mdnie_size, vdd, tune); + return; + } + + DPRINT("hbm: %d bypass: %d accessibility: %d app: %d mode: %d hdr: %d " \ + "night_mode: %d whiteRGB: (%d %d %d) color_lens: (%d %d %d)\n", + tune->hbm_enable, tune->mdnie_bypass, tune->mdnie_accessibility, + tune->mdnie_app, tune->mdnie_mode, tune->hdr, tune->night_mode_enable, + mdnie_data->dsi_white_balanced_r, mdnie_data->dsi_white_balanced_g, + mdnie_data->dsi_white_balanced_b, + tune->color_lens_enable, tune->color_lens_color, tune->color_lens_level); + + pcmds = ss_get_cmds(vdd, TX_MDNIE_TUNE); + pcmds->state = DSI_CMD_SET_STATE_HS; + pcmds->cmds = tune_data_dsi; + pcmds->count = mdnie_data->dsi_bypass_mdnie_size; + + /* temp to avoid tx fail with single TX enabled */ + for (i = 0; i < pcmds->count; i++) + pcmds->cmds[i].last_command = true; + + ss_send_cmd(vdd, TX_MDNIE_TUNE); +} + +/* uupdate_dsi_tcon_mdnie_register(): + * pdate and tx mdnie packet to panel wich vdd is included. + */ +int update_dsi_tcon_mdnie_register(struct samsung_display_driver_data *vdd) +{ + struct mdnie_lite_tun_type *tune = NULL; + struct dsi_cmd_desc *tune_data_dsi = NULL; + struct mdnie_lite_tune_data *mdnie_data; + enum BYPASS temp_bypass = BYPASS_ENABLE; + + if (vdd == NULL || !vdd->mdnie.support_mdnie) + return 0; + + if (ss_is_seamless_mode(vdd) || + ss_is_panel_off(vdd)) { + LCD_ERR("do not send mdnie data (%d) (%d)\n", + ss_is_seamless_mode(vdd), ss_is_panel_off(vdd)); + return 0; + } + + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + /* + * Checking HBM mode first. + */ + if (vdd->lux >= vdd->mdnie.enter_hbm_ce_lux) + tune->hbm_enable = true; + else + tune->hbm_enable = false; + + /* + * Safe Code for When LCD ON is should be LIGHT_NOTIFICATION_OFF + */ + if (vdd->mdnie.lcd_on_notifiy) { + tune->light_notification = LIGHT_NOTIFICATION_OFF; + vdd->mdnie.lcd_on_notifiy = false; + } + + if(tune->mdnie_bypass == BYPASS_DISABLE) { + if (ss_is_panel_lpm(vdd)) { + if (tune->mdnie_accessibility == CURTAIN) { + temp_bypass = BYPASS_DISABLE; + } + else if((tune->mdnie_accessibility == NEGATIVE) || (tune->mdnie_accessibility == GRAYSCALE_NEGATIVE) || (tune->color_lens_enable == true)){ + temp_bypass = BYPASS_ENABLE; + } + else if ((tune->light_notification) || (tune->mdnie_accessibility == COLOR_BLIND || tune->mdnie_accessibility == COLOR_BLIND_HBM) || + (tune->mdnie_accessibility == GRAYSCALE) || (tune->night_mode_enable == true) || (tune->ldu_mode_index != 0)) { + temp_bypass = BYPASS_DISABLE; + } + else { + temp_bypass = BYPASS_ENABLE; + } + } + else { + temp_bypass = BYPASS_DISABLE; + } + } + + /* + * mDnie priority + * Accessibility > HBM > Screen Mode + */ + if (temp_bypass == BYPASS_ENABLE) { + tune_data_dsi = mdnie_data->DSI_BYPASS_MDNIE; + } else if (tune->light_notification) { + tune_data_dsi = mdnie_data->light_notification_tune_value_dsi[tune->light_notification]; + } else if (tune->mdnie_accessibility == COLOR_BLIND || + tune->mdnie_accessibility == COLOR_BLIND_HBM) { + tune_data_dsi = mdnie_data->DSI_COLOR_BLIND_MDNIE; + } else if (tune->mdnie_accessibility == NEGATIVE) { + tune_data_dsi = mdnie_data->DSI_NEGATIVE_MDNIE; + } else if (tune->mdnie_accessibility == CURTAIN) { + tune_data_dsi = mdnie_data->DSI_CURTAIN; + } else if (tune->mdnie_accessibility == GRAYSCALE) { + tune_data_dsi = mdnie_data->DSI_GRAYSCALE_MDNIE; + } else if (tune->mdnie_accessibility == GRAYSCALE_NEGATIVE) { + tune_data_dsi = mdnie_data->DSI_GRAYSCALE_NEGATIVE_MDNIE; + } else if (tune->color_lens_enable == true) { + tune_data_dsi = mdnie_data->DSI_COLOR_LENS_MDNIE; + } else if (tune->hdr) { + tune_data_dsi = mdnie_data->hdr_tune_value_dsi[tune->hdr]; + } else if (tune->hmt_color_temperature) { + tune_data_dsi = + mdnie_data->hmt_color_temperature_tune_value_dsi[tune->hmt_color_temperature]; + } else if (tune->night_mode_enable == true) { + tune_data_dsi = mdnie_data->DSI_NIGHT_MODE_MDNIE; + } else if (tune->hbm_enable == true) { + if(tune->mdnie_mode == AUTO_MODE) { + if (vdd->dtsi_data.hbm_ce_text_mode_support && + ((tune->mdnie_app == BROWSER_APP) || + (tune->mdnie_app == eBOOK_APP))) + tune_data_dsi = mdnie_data->DSI_HBM_CE_TEXT_MDNIE; + else + tune_data_dsi = mdnie_data->DSI_HBM_CE_MDNIE; + } else { + tune_data_dsi = mdnie_data->DSI_HBM_CE_D65_MDNIE; + } + } else if (tune->mdnie_app == EMAIL_APP) { + /* + Some kind of panel doesn't suooprt EMAIL_APP mode, but SSRM module use same control logic. + It means SSRM doesn't consider panel unique character. + To support this issue eBOOK_APP used insted of EMAIL_APP under EMAIL_APP doesn't exist status.. + */ + tune_data_dsi = mdnie_data->mdnie_tune_value_dsi[tune->mdnie_app][tune->mdnie_mode][tune->outdoor]; + if (!tune_data_dsi) + tune_data_dsi = mdnie_data->mdnie_tune_value_dsi[eBOOK_APP][tune->mdnie_mode][tune->outdoor]; + } else { + tune_data_dsi = mdnie_data->mdnie_tune_value_dsi[tune->mdnie_app][tune->mdnie_mode][tune->outdoor]; + } + + if (vdd->mdnie.support_trans_dimming && vdd->mdnie.disable_trans_dimming && (tune->hbm_enable == false)) { + if (tune_data_dsi) { + memcpy(mdnie_data->DSI_RGB_SENSOR_MDNIE_1, + tune_data_dsi[mdnie_data->mdnie_step_index[MDNIE_STEP1]].msg.tx_buf, + mdnie_data->dsi_rgb_sensor_mdnie_1_size); + memcpy(mdnie_data->DSI_RGB_SENSOR_MDNIE_2, + tune_data_dsi[mdnie_data->mdnie_step_index[MDNIE_STEP2]].msg.tx_buf, + mdnie_data->dsi_rgb_sensor_mdnie_2_size); + memcpy(mdnie_data->DSI_RGB_SENSOR_MDNIE_3, + tune_data_dsi[mdnie_data->mdnie_step_index[MDNIE_STEP3]].msg.tx_buf, + mdnie_data->dsi_rgb_sensor_mdnie_3_size); + + mdnie_data->DSI_TRANS_DIMMING_MDNIE[mdnie_data->dsi_trans_dimming_data_index] = 0x0; + tune_data_dsi = mdnie_data->DSI_RGB_SENSOR_MDNIE; + } + } + + if (!tune_data_dsi) { + DPRINT("%s tune_data is NULL hbm : %d mdnie_bypass : %d mdnie_accessibility : %d color_lens : %d" + " mdnie_app: %d mdnie_mode : %d hdr : %d night_mode_enable : %d\n", + __func__, tune->hbm_enable, tune->mdnie_bypass, tune->mdnie_accessibility, tune->color_lens_enable, + tune->mdnie_app, tune->mdnie_mode, tune->hdr, tune->night_mode_enable); + return -EFAULT; + } + + tune->scr_white_red = tune_data_dsi[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]]; + tune->scr_white_green = tune_data_dsi[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]]; + tune->scr_white_blue = tune_data_dsi[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]]; + + send_dsi_tcon_mdnie_register(vdd, tune_data_dsi, tune); + + return 0; +} + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current Mode: %s\n", + mdnie_mode_name[tune->mdnie_mode]); + + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int value; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value < DYNAMIC_MODE || value >= MAX_MODE) { + DPRINT("[ERROR] wrong mode value : %d\n", + value); + return size; + } + + if (vdd->dtsi_data.tft_common_support && value >= NATURAL_MODE) + value++; + + tune->mdnie_mode = value; + + DPRINT("%s mode : %d\n", __func__, tune->mdnie_mode); + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t scenario_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf, 256, "Current APP : "); + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current APP: %s\n", + mdnie_app_name[tune->mdnie_app]); + + DPRINT("%s \n", buf); + + return buffer_pos; +} + +/* app_id : App give self_app_id to mdnie driver. +* ret_id : app_id for mdnie data structure. +* example. TDMB app tell mdnie-driver that my app_id is 20. but mdnie driver will change it to TDMB_APP value. +*/ +static int fake_id(int app_id) +{ + int ret_id; + + switch (app_id) { +#ifdef CONFIG_TDMB + case APP_ID_TDMB: + ret_id = TDMB_APP; + DPRINT("%s : change app_id(%d) to mdnie_app(%d)\n", __func__, app_id, ret_id); + break; +#endif + default: + ret_id = app_id; + break; + } + + return ret_id; +} + + +static ssize_t scenario_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int value; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + value = fake_id(value); + + if (value < UI_APP || value >= MAX_APP_MODE) { + DPRINT("[ERROR] wrong Scenario mode value : %d\n", + value); + return size; + } + + tune->mdnie_app = value; + DPRINT("%s APP : %d\n", __func__, tune->mdnie_app); + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t outdoor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current outdoor Mode: %s\n", + outdoor_name[tune->outdoor]); + + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t outdoor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int value; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value < OUTDOOR_OFF_MODE || value >= MAX_OUTDOOR_MODE) { + DPRINT("[ERROR] : wrong outdoor mode value : %d\n", value); + return size; + } + + tune->outdoor = value; + DPRINT("outdoor value = %d, APP = %d\n", value, tune->mdnie_app); + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t bypass_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current MDNIE bypass: %s\n", + tune->mdnie_bypass ? "ENABLE" : "DISABLE"); + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t bypass_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tun_type *tune = NULL; + int value; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value) + tune->mdnie_bypass = BYPASS_ENABLE; + else + tune->mdnie_bypass = BYPASS_DISABLE; + + DPRINT("%s bypass : %s value : %d\n", __func__, + tune->mdnie_bypass ? "ENABLE" : "DISABLE", + value); + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t accessibility_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current accessibility: %s\n", + tune->mdnie_accessibility ? + tune->mdnie_accessibility == 1 ? "NEGATIVE" : + tune->mdnie_accessibility == 2 ? "COLOR_BLIND" : + tune->mdnie_accessibility == 3 ? "CURTAIN" : + tune->mdnie_accessibility == 4 ? "GRAYSCALE" : + tune->mdnie_accessibility == 5 ? "GRAYSCALE_NEGATIVE" : + "COLOR_BLIND_HBM" : "ACCESSIBILITY_OFF"); + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t accessibility_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + struct mdnie_lite_tun_type *tune = NULL; + int cmd_value; + char buffer[MDNIE_COLOR_BLINDE_HBM_CMD_SIZE] = {0,}; + int buffer2[MDNIE_COLOR_BLINDE_HBM_CMD_SIZE/2] = {0,}; + int loop; + char temp; + + if (!vdd) + return 0; + + mdnie_data = vdd->mdnie.mdnie_data; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d %x %x %x %x %x %x %x %x %x %x %x %x", &cmd_value, + &buffer2[0], &buffer2[1], &buffer2[2], &buffer2[3], &buffer2[4], + &buffer2[5], &buffer2[6], &buffer2[7], &buffer2[8], &buffer2[9], + &buffer2[10], &buffer2[11]); + + for (loop = 0; loop < MDNIE_COLOR_BLINDE_HBM_CMD_SIZE/2; loop++) { + buffer2[loop] = buffer2[loop] & 0xFFFF; + buffer[loop * 2] = (buffer2[loop] & 0xFF00) >> 8; + buffer[loop * 2 + 1] = buffer2[loop] & 0xFF; + } + + for (loop = 0; loop < MDNIE_COLOR_BLINDE_HBM_CMD_SIZE; loop += 2) { + temp = buffer[loop]; + buffer[loop] = buffer[loop + 1]; + buffer[loop + 1] = temp; + } + + /* + * mDnie priority + * Accessibility > HBM > Screen Mode + */ + if (cmd_value == NEGATIVE) { + tune->mdnie_accessibility = NEGATIVE; + } else if (cmd_value == COLOR_BLIND) { + tune->mdnie_accessibility = COLOR_BLIND; + + if (!IS_ERR_OR_NULL(mdnie_data->DSI_COLOR_BLIND_MDNIE_SCR)) + memcpy(&mdnie_data->DSI_COLOR_BLIND_MDNIE_SCR[mdnie_data->mdnie_color_blinde_cmd_offset], + buffer, MDNIE_COLOR_BLINDE_CMD_SIZE); + } else if (cmd_value == COLOR_BLIND_HBM) { + tune->mdnie_accessibility = COLOR_BLIND_HBM; + + if (!IS_ERR_OR_NULL(mdnie_data->DSI_COLOR_BLIND_MDNIE_SCR)) + memcpy(&mdnie_data->DSI_COLOR_BLIND_MDNIE_SCR[mdnie_data->mdnie_color_blinde_cmd_offset], + buffer, MDNIE_COLOR_BLINDE_HBM_CMD_SIZE); + } else if (cmd_value == CURTAIN) { + tune->mdnie_accessibility = CURTAIN; + } else if (cmd_value == GRAYSCALE) { + tune->mdnie_accessibility = GRAYSCALE; + } else if (cmd_value == GRAYSCALE_NEGATIVE) { + tune->mdnie_accessibility = GRAYSCALE_NEGATIVE; + } else if (cmd_value == ACCESSIBILITY_OFF) { + tune->mdnie_accessibility = ACCESSIBILITY_OFF; + } else + DPRINT("%s ACCESSIBILITY_MAX", __func__); + +#if defined(CONFIG_64BIT) + DPRINT("%s cmd_value : %d size : %lu", __func__, cmd_value, size); +#else + DPRINT("%s cmd_value : %d size : %u", __func__, cmd_value, size); +#endif + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + return size; +} + +static ssize_t sensorRGB_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf, 256, "%d %d %d", + tune->scr_white_red, + tune->scr_white_green, + tune->scr_white_blue); + + return buffer_pos; +} + +static ssize_t sensorRGB_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + int white_red, white_green, white_blue; + struct mdnie_lite_tun_type *tune = NULL; + struct dsi_cmd_desc *data_dsi = NULL; + struct dsi_cmd_desc *tune_data_dsi = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + sscanf(buf, "%d %d %d", &white_red, &white_green, &white_blue); + + if (tune->ldu_mode_index == 0) { + if ((tune->mdnie_accessibility == ACCESSIBILITY_OFF) && (tune->mdnie_mode == AUTO_MODE) && + ((tune->mdnie_app == BROWSER_APP) || (tune->mdnie_app == eBOOK_APP))) { + + tune->scr_white_red = (char)white_red; + tune->scr_white_green = (char)white_green; + tune->scr_white_blue = (char)white_blue; + + DPRINT("%s: white_red = %d, white_green = %d, white_blue = %d, %d %d\n", + __func__, + white_red, white_green, white_blue, + mdnie_data->dsi_rgb_sensor_mdnie_1_size, + mdnie_data->dsi_rgb_sensor_mdnie_2_size); + + tune_data_dsi = mdnie_data->DSI_RGB_SENSOR_MDNIE; + + data_dsi = mdnie_data->mdnie_tune_value_dsi[tune->mdnie_app][tune->mdnie_mode][tune->outdoor]; + + if (data_dsi) { + memcpy(mdnie_data->DSI_RGB_SENSOR_MDNIE_1, data_dsi[mdnie_data->mdnie_step_index[MDNIE_STEP1]].msg.tx_buf, mdnie_data->dsi_rgb_sensor_mdnie_1_size); + memcpy(mdnie_data->DSI_RGB_SENSOR_MDNIE_2, data_dsi[mdnie_data->mdnie_step_index[MDNIE_STEP2]].msg.tx_buf, mdnie_data->dsi_rgb_sensor_mdnie_2_size); + memcpy(mdnie_data->DSI_RGB_SENSOR_MDNIE_3, data_dsi[mdnie_data->mdnie_step_index[MDNIE_STEP3]].msg.tx_buf, mdnie_data->dsi_rgb_sensor_mdnie_3_size); + + mdnie_data->DSI_RGB_SENSOR_MDNIE_SCR[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = white_red; + mdnie_data->DSI_RGB_SENSOR_MDNIE_SCR[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = white_green; + mdnie_data->DSI_RGB_SENSOR_MDNIE_SCR[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = white_blue; + } + } + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + send_dsi_tcon_mdnie_register(vdd, tune_data_dsi, tune); + + return size; +} + +static ssize_t whiteRGB_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + int r, g, b; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + r = mdnie_data->dsi_white_balanced_r; + g = mdnie_data->dsi_white_balanced_g; + b = mdnie_data->dsi_white_balanced_b; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current whiteRGB SETTING: %d %d %d\n", r, g, b); + + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t whiteRGB_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + int i; + int white_red, white_green, white_blue; + struct mdnie_lite_tun_type *tune = NULL; + struct dsi_cmd_desc *white_tunning_data = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + sscanf(buf, "%d %d %d", &white_red, &white_green, &white_blue); + + DPRINT("%s: white_red = %d, white_green = %d, white_blue = %d\n", __func__, white_red, white_green, white_blue); + + if (tune->mdnie_mode == AUTO_MODE) { + if ((white_red <= 0 && white_red >= -40) && (white_green <= 0 && white_green >= -40) && (white_blue <= 0 && white_blue >= -40)) { + if (tune->ldu_mode_index == 0) { + mdnie_data->dsi_white_ldu_r = mdnie_data->dsi_white_default_r; + mdnie_data->dsi_white_ldu_g = mdnie_data->dsi_white_default_g; + mdnie_data->dsi_white_ldu_b = mdnie_data->dsi_white_default_b; + } + for (i = 0; i < MAX_APP_MODE; i++) { + if ((mdnie_data->mdnie_tune_value_dsi[i][AUTO_MODE][0] != NULL) && (i != eBOOK_APP)) { + white_tunning_data = mdnie_data->mdnie_tune_value_dsi[i][AUTO_MODE][0]; + white_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = (char)(mdnie_data->dsi_white_ldu_r + white_red); + white_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = (char)(mdnie_data->dsi_white_ldu_g + white_green); + white_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = (char)(mdnie_data->dsi_white_ldu_b + white_blue); + mdnie_data->dsi_white_balanced_r = white_red; + mdnie_data->dsi_white_balanced_g = white_green; + mdnie_data->dsi_white_balanced_b = white_blue; + } + } + } + } + + white_tunning_data = mdnie_data->DSI_HBM_CE_MDNIE; + if(white_tunning_data != NULL) { + white_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = (char)(mdnie_data->dsi_white_ldu_r + white_red); + white_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = (char)(mdnie_data->dsi_white_ldu_g + white_green); + white_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = (char)(mdnie_data->dsi_white_ldu_b + white_blue); + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + return size; +} + +static ssize_t mdnie_ldu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf, 256, "%d %d %d", + tune->scr_white_red, + tune->scr_white_green, + tune->scr_white_blue); + + return buffer_pos; +} + +static ssize_t mdnie_ldu_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + int i, j, idx; + struct mdnie_lite_tun_type *tune = NULL; + struct dsi_cmd_desc *ldu_tunning_data = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + sscanf(buf, "%d", &idx); + + DPRINT("%s: idx = %d\n", __func__, idx); + + if ((idx >= 0) && (idx < mdnie_data->dsi_max_adjust_ldu)) { + tune->ldu_mode_index = idx; + for (i = 0; i < MAX_APP_MODE; i++) { + for (j = 0; j < MAX_MODE; j++) { + if ((mdnie_data->mdnie_tune_value_dsi[i][j][0] != NULL) && (i != eBOOK_APP) && (j != READING_MODE)) { + ldu_tunning_data = mdnie_data->mdnie_tune_value_dsi[i][j][0]; + if (j == AUTO_MODE) { + ldu_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = + mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 0] + mdnie_data->dsi_white_balanced_r; + ldu_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = + mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 1] + mdnie_data->dsi_white_balanced_g; + ldu_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = + mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 2] + mdnie_data->dsi_white_balanced_b; + mdnie_data->dsi_white_ldu_r = mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 0]; + mdnie_data->dsi_white_ldu_g = mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 1]; + mdnie_data->dsi_white_ldu_b = mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 2]; + } else { + ldu_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = + mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 0]; + ldu_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = + mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 1]; + ldu_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = + mdnie_data->dsi_adjust_ldu_table[j][idx * 3 + 2]; + } + } + } + } + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + return size; +} + +static ssize_t night_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf, 256, "%d %d", + tune->night_mode_enable, + tune->night_mode_index); + + return buffer_pos; +} + +static ssize_t night_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + int enable, idx; + char *buffer; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + sscanf(buf, "%d %d", &enable, &idx); + + tune->night_mode_enable = enable; + + DPRINT("%s: enable = %d, idx = %d\n", __func__, enable, idx); + + if (((idx >= 0) && (idx < mdnie_data->dsi_max_night_mode_index)) && (enable == true)) { + if (!IS_ERR_OR_NULL(mdnie_data->dsi_night_mode_table)) { + buffer = &mdnie_data->dsi_night_mode_table[(MDNIE_SCR_CMD_SIZE * idx)]; + if (!IS_ERR_OR_NULL(mdnie_data->DSI_NIGHT_MODE_MDNIE_SCR)) { + memcpy(&mdnie_data->DSI_NIGHT_MODE_MDNIE_SCR[mdnie_data->mdnie_color_blinde_cmd_offset], + buffer, MDNIE_SCR_CMD_SIZE); + tune->night_mode_index = idx; + } + } + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + return size; +} + +static ssize_t color_lens_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf, 256, "%d %d %d", + tune->color_lens_enable, + tune->color_lens_color, + tune->color_lens_level); + + return buffer_pos; +} + +static ssize_t color_lens_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + int enable, color, level; + char *buffer; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + sscanf(buf, "%d %d %d", &enable, &color, &level); + + tune->color_lens_enable = enable; + + DPRINT("%s: enable = %d, color = %d, level = %d\n", __func__, enable, color, level); + + if ((enable == true) && ((color >= 0) && (color < COLOR_LENS_COLOR_MAX)) && ((level >= 0) && (level < COLOR_LENS_LEVEL_MAX))) { + if (!IS_ERR_OR_NULL(mdnie_data->dsi_color_lens_table)) { + buffer = &mdnie_data->dsi_color_lens_table[(color * MDNIE_SCR_CMD_SIZE * COLOR_LENS_LEVEL_MAX) + (MDNIE_SCR_CMD_SIZE * level)]; + if (!IS_ERR_OR_NULL(mdnie_data->DSI_COLOR_LENS_MDNIE_SCR)) { + memcpy(&mdnie_data->DSI_COLOR_LENS_MDNIE_SCR[mdnie_data->mdnie_color_blinde_cmd_offset], + buffer, MDNIE_SCR_CMD_SIZE); + tune->color_lens_color = color; + tune->color_lens_level = level; + } + } + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + return size; +} + +static ssize_t hdr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current HDR SETTING: %s\n", + mdnie_hdr_name[tune->hdr]); + + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t hdr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int value; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value < HDR_OFF || value >= HDR_MAX) { + DPRINT("[ERROR] wrong hdr value : %d\n", value); + return size; + } + + DPRINT("%s : (%d) -> (%d)\n", __func__, tune->hdr, value); + tune->hdr = value; + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t light_notification_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + pos = snprintf(buf, 256, "Current LIGHT NOTIFICATION SETTING: %s\n", + mdnie_light_notification_name[tune->light_notification]); + + DPRINT("%s\n", buf); + + return pos; +} + +static ssize_t light_notification_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int value; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value < LIGHT_NOTIFICATION_OFF || value >= LIGHT_NOTIFICATION_MAX) { + DPRINT("[ERROR] wrong light notification value : %d\n", value); + return size; + } + + DPRINT("%s : (%d) -> (%d)\n", __func__, + tune->light_notification, value); + tune->light_notification = value; + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t afc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf, 256, "%d %d %d %d %d %d %d %d %d %d %d %d %d", + tune->afc_enable, tune->afc_roi[0], tune->afc_roi[1], tune->afc_roi[2], tune->afc_roi[3], + tune->afc_roi[4], tune->afc_roi[5], tune->afc_roi[6], tune->afc_roi[7], tune->afc_roi[8], + tune->afc_roi[9], tune->afc_roi[10], tune->afc_roi[11]); + + return buffer_pos; +} + +static ssize_t afc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tune_data *mdnie_data; + struct mdnie_lite_tun_type *tune = NULL; + int i, enable = 0; + int roi[12] = {0}; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + mdnie_data = vdd->mdnie.mdnie_data; + + if ((mdnie_data->DSI_AFC == NULL) || (mdnie_data->DSI_AFC_ON == NULL) || (mdnie_data->DSI_AFC_OFF == NULL)) + return 0; + + sscanf(buf, "%d %d %d %d %d %d %d %d %d %d %d %d %d", &enable, &roi[0], &roi[1], &roi[2], &roi[3], &roi[4], &roi[5], &roi[6], &roi[7], &roi[8], &roi[9], &roi[10], &roi[11]); + + if (enable) { + tune->afc_enable = enable; + DPRINT("%s: enable, roi = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", + __func__, roi[0], roi[1], roi[2], roi[3], roi[4], roi[5], roi[6], roi[7], roi[8], roi[9], roi[10], roi[11]); + + memcpy(mdnie_data->DSI_AFC, mdnie_data->DSI_AFC_ON, mdnie_data->dsi_afc_size); + + for (i = 0; i < AFC_ROI_CMD_SIZE; i++) { + if ((roi[i] > 0) && (roi[i] <= 255)) + tune->afc_roi[i] = (char)roi[i]; + else + tune->afc_roi[i] = 0xff; + mdnie_data->DSI_AFC[mdnie_data->dsi_afc_index+i] = tune->afc_roi[i]; + } + } else { + tune->afc_enable = enable; + for (i = 0; i < AFC_ROI_CMD_SIZE; i++) { + tune->afc_roi[i] = 0xff; + } + + DPRINT("%s: disable\n", __func__); + + memcpy(mdnie_data->DSI_AFC, mdnie_data->DSI_AFC_OFF, mdnie_data->dsi_afc_size); + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static ssize_t cabc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int buffer_pos = 0; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + buffer_pos += snprintf(buf + buffer_pos, 256, "Current CABC bypass: %s\n", + tune->cabc_bypass ? "ENABLE" : "DISABLE"); + + DPRINT("%s\n", buf); + + return buffer_pos; +} + +static ssize_t cabc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tun_type *tune = NULL; + int value; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value) + tune->cabc_bypass = BYPASS_DISABLE; + else + tune->cabc_bypass = BYPASS_ENABLE; + + DPRINT("%s bypass : %s value : %d\n", __func__, tune->cabc_bypass ? "ENABLE" : "DISABLE", value); + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + config_cabc(vdd, value); + + return size; +} + +static ssize_t hmt_color_temperature_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + DPRINT("Current color temperature : %d\n", tune->hmt_color_temperature); + + return snprintf(buf, 256, "Current color temperature : %d\n", tune->hmt_color_temperature); +} + +static ssize_t hmt_color_temperature_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct samsung_display_driver_data *vdd = dev_get_drvdata(dev); + int value; + struct mdnie_lite_tun_type *tune = NULL; + + if (!vdd) + return 0; + + vdd = ss_check_hall_ic_get_vdd(vdd); + tune = vdd->mdnie.mdnie_tune_state_dsi; + + sscanf(buf, "%d", &value); + + if (value < HMT_COLOR_TEMP_OFF || value >= HMT_COLOR_TEMP_MAX) { + DPRINT("[ERROR] wrong color temperature value : %d\n", value); + return size; + } + + if (tune->mdnie_accessibility == NEGATIVE) { + DPRINT("already negative mode(%d), do not update color temperature(%d)\n", + tune->mdnie_accessibility, value); + return size; + } + + DPRINT("%s : (%d) -> (%d)\n", __func__, tune->hmt_color_temperature, value); + tune->hmt_color_temperature = value; + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return size; + } + + update_dsi_tcon_mdnie_register(vdd); + + return size; +} + +static DEVICE_ATTR(mode, 0664, mode_show, mode_store); +static DEVICE_ATTR(scenario, 0664, scenario_show, scenario_store); +static DEVICE_ATTR(outdoor, 0664, outdoor_show, outdoor_store); +static DEVICE_ATTR(bypass, 0664, bypass_show, bypass_store); +static DEVICE_ATTR(accessibility, 0664, accessibility_show, accessibility_store); +static DEVICE_ATTR(sensorRGB, 0664, sensorRGB_show, sensorRGB_store); +static DEVICE_ATTR(whiteRGB, 0664, whiteRGB_show, whiteRGB_store); +static DEVICE_ATTR(mdnie_ldu, 0664, mdnie_ldu_show, mdnie_ldu_store); +static DEVICE_ATTR(night_mode, 0664, night_mode_show, night_mode_store); +static DEVICE_ATTR(color_lens, 0664, color_lens_show, color_lens_store); +static DEVICE_ATTR(hdr, 0664, hdr_show, hdr_store); +static DEVICE_ATTR(light_notification, 0664, light_notification_show, light_notification_store); +static DEVICE_ATTR(afc, 0664, afc_show, afc_store); +static DEVICE_ATTR(cabc, 0664, cabc_show, cabc_store); +static DEVICE_ATTR(hmt_color_temperature, 0664, hmt_color_temperature_show, hmt_color_temperature_store); + +#define MDNIE_WOFS_ORG_PATH ("/efs/FactoryApp/mdnie") +static int mdnie_get_efs(char *filename, int *value) +{ + mm_segment_t old_fs; + struct file *filp = NULL; + int fsize = 0, nread, rc, ret = 0; + u8 buf[128] = { 0, }; + + if (!filename || !value) { + pr_err("%s invalid parameter\n", __func__); + return -EINVAL; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + filp = filp_open(filename, O_RDONLY, 0440); + if (IS_ERR(filp)) { + ret = PTR_ERR(filp); + if (ret == -ENOENT) + pr_err("%s file(%s) not exist\n", __func__, filename); + else + pr_info("%s file(%s) open error(ret %d)\n", + __func__, filename, ret); + set_fs(old_fs); + return -ENOENT; + } + + if (filp->f_path.dentry && filp->f_path.dentry->d_inode) + fsize = filp->f_path.dentry->d_inode->i_size; + + if (fsize == 0 || fsize >= ARRAY_SIZE(buf)) { + pr_err("%s invalid file(%s) size %d\n", + __func__, filename, fsize); + ret = -EEXIST; + goto exit; + } + + memset(buf, 0, sizeof(buf)); + nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos); + buf[nread] = '\0'; + + if (nread != fsize) { + pr_err("%s failed to read (ret %d)\n", __func__, nread); + ret = -EIO; + goto exit; + } + + rc = sscanf(buf, "%d %d %d", &value[0], &value[1], &value[2]); + if (rc != 3) { + pr_err("%s failed to kstrtoint %d\n", __func__, rc); + ret = -EINVAL; + goto exit; + } + + pr_info("%s %s(size %d) : %d %d %d\n", + __func__, filename, fsize, value[0], value[1], value[2]); + +exit: + filp_close(filp, current->files); + set_fs(old_fs); + + return ret; +} + +static int dpui_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct mdnie_lite_tun_type *tune = container_of(self, + struct mdnie_lite_tun_type, dpui_notif); + + struct samsung_display_driver_data *vdd = tune->vdd; + struct mdnie_lite_tune_data *mdnie_data = vdd->mdnie.mdnie_data; + struct dpui_info *dpui = data; + char tbuf[MAX_DPUI_VAL_LEN]; + int def_wrgb_ofs_org[3] = { 0, }; + int size; + + if (dpui == NULL) { + DPRINT("err: dpui is null\n"); + return 0; + } + + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", + mdnie_data->dsi_white_balanced_r); + set_dpui_field(DPUI_KEY_WOFS_R, tbuf, size); + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", + mdnie_data->dsi_white_balanced_g); + set_dpui_field(DPUI_KEY_WOFS_G, tbuf, size); + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", + mdnie_data->dsi_white_balanced_b); + set_dpui_field(DPUI_KEY_WOFS_B, tbuf, size); + + mdnie_get_efs(MDNIE_WOFS_ORG_PATH, def_wrgb_ofs_org); + + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", def_wrgb_ofs_org[0]); + set_dpui_field(DPUI_KEY_WOFS_R_ORG, tbuf, size); + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", def_wrgb_ofs_org[1]); + set_dpui_field(DPUI_KEY_WOFS_G_ORG, tbuf, size); + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", def_wrgb_ofs_org[2]); + set_dpui_field(DPUI_KEY_WOFS_B_ORG, tbuf, size); + + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", vdd->mdnie.mdnie_x); + set_dpui_field(DPUI_KEY_WCRD_X, tbuf, size); + size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", vdd->mdnie.mdnie_y); + set_dpui_field(DPUI_KEY_WCRD_Y, tbuf, size); + + return 0; +} + +static int mdnie_register_dpui(struct mdnie_lite_tun_type *tune) +{ + memset(&tune->dpui_notif, 0, + sizeof(tune->dpui_notif)); + tune->dpui_notif.notifier_call = dpui_notifier_callback; + + return dpui_logging_register(&tune->dpui_notif, + DPUI_TYPE_PANEL); +} + +void create_tcon_mdnie_node(struct samsung_display_driver_data *vdd) + +{ + struct device *tune_mdnie_dev; + char dirname[10]; + + if (vdd->ndx == PRIMARY_DISPLAY_NDX) + sprintf(dirname, "mdnie"); + else + sprintf(dirname, "mdnie%d", vdd->ndx); + + tune_mdnie_dev = device_create(mdnie_class, NULL, 0, vdd, dirname); + + if (IS_ERR(tune_mdnie_dev)) + DPRINT("Failed to create device(mdnie)!\n"); + + /* APP */ + if (device_create_file(tune_mdnie_dev, &dev_attr_scenario) < 0) + DPRINT("Failed to create device file(%s)!\n", dev_attr_scenario.attr.name); + + /* MODE */ + if (device_create_file(tune_mdnie_dev, &dev_attr_mode) < 0) + DPRINT("Failed to create device file(%s)!\n", dev_attr_mode.attr.name); + + /* OUTDOOR */ + if (device_create_file(tune_mdnie_dev, &dev_attr_outdoor) < 0) + DPRINT("Failed to create device file(%s)!\n", dev_attr_outdoor.attr.name); + + /* MDNIE ON/OFF */ + if (device_create_file(tune_mdnie_dev, &dev_attr_bypass) < 0) + DPRINT("Failed to create device file(%s)!\n", dev_attr_bypass.attr.name); + + /* COLOR BLIND */ + if (device_create_file(tune_mdnie_dev, &dev_attr_accessibility) < 0) + DPRINT("Failed to create device file(%s)!=n", dev_attr_accessibility.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_sensorRGB) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_sensorRGB.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_whiteRGB) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_whiteRGB.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_mdnie_ldu) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_mdnie_ldu.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_night_mode) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_night_mode.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_color_lens) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_color_lens.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_hdr) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_hdr.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_light_notification) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_light_notification.attr.name); + + if (device_create_file + (tune_mdnie_dev, &dev_attr_afc) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_afc.attr.name); + + /* hmt_color_temperature */ + if (device_create_file + (tune_mdnie_dev, &dev_attr_hmt_color_temperature) < 0) + DPRINT("Failed to create device file(%s)!=n", + dev_attr_hmt_color_temperature.attr.name); + + /* CABC ON/OFF */ + if (vdd->support_cabc) + if (device_create_file(tune_mdnie_dev, &dev_attr_cabc) < 0) + DPRINT("Failed to create device file(%s)!\n", dev_attr_cabc.attr.name); +} + +struct mdnie_lite_tun_type *init_dsi_tcon_mdnie_class(struct samsung_display_driver_data *vdd) +{ + struct mdnie_lite_tun_type *tune; + + if (!mdnie_class) { + mdnie_class = class_create(THIS_MODULE, "mdnie"); + if (IS_ERR(mdnie_class)) { + DPRINT("Failed to create class(mdnie)!\n"); + return NULL; + } + + } + + create_tcon_mdnie_node(vdd); + + tune = + kzalloc(sizeof(struct mdnie_lite_tun_type), GFP_KERNEL); + + if (!tune) { + DPRINT("%s allocation fail", __func__); + return NULL; + } + + tune->vdd = vdd; + vdd->mdnie.mdnie_tune_state_dsi = tune; + + tune->mdnie_bypass = BYPASS_DISABLE; + if (tune->vdd->support_cabc) + tune->cabc_bypass = BYPASS_DISABLE; + tune->hbm_enable = false; + + tune->mdnie_app = UI_APP; + tune->mdnie_mode = AUTO_MODE; + tune->outdoor = OUTDOOR_OFF_MODE; + tune->hdr = HDR_OFF; + tune->light_notification = LIGHT_NOTIFICATION_OFF; + + tune->mdnie_accessibility = ACCESSIBILITY_OFF; + + tune->scr_white_red = 0xff; + tune->scr_white_green = 0xff; + tune->scr_white_blue = 0xff; + + tune->night_mode_enable = false; + tune->night_mode_index = 0; + tune->ldu_mode_index = 0; + + tune->color_lens_enable = false; + tune->color_lens_color = 0; + tune->color_lens_level = 0; + + tune->afc_enable = 0; + mdnie_register_dpui(tune); + + /* Set default link_stats as DSI_HS_MODE for mdnie tune data */ +// vdd_data->mdnie_tune_data[index].mdnie_tune_packet_tx_cmds_dsi.link_state = DSI_HS_MODE; + + return tune; +} + + +void coordinate_tunning_multi(struct samsung_display_driver_data *vdd, + char (*coordinate_data_multi[MAX_MODE])[COORDINATE_DATA_SIZE], int mdnie_tune_index, int scr_wr_addr, int data_size) +{ + int i, j; + struct dsi_cmd_desc *coordinate_tunning_data = NULL; + struct mdnie_lite_tune_data *mdnie_data = vdd->mdnie.mdnie_data; + + for (i = 0; i < MAX_APP_MODE; i++) { + for (j = 0; j < MAX_MODE; j++) { + if ((mdnie_data->mdnie_tune_value_dsi[i][j][0] != NULL) && (i != eBOOK_APP) && (j != READING_MODE)) { + coordinate_tunning_data = mdnie_data->mdnie_tune_value_dsi[i][j][0]; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = coordinate_data_multi[j][mdnie_tune_index][0]; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = coordinate_data_multi[j][mdnie_tune_index][2]; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = coordinate_data_multi[j][mdnie_tune_index][4]; + } + if((i == UI_APP) && (j == AUTO_MODE)) { + mdnie_data->dsi_white_default_r = coordinate_data_multi[j][mdnie_tune_index][0]; + mdnie_data->dsi_white_default_g = coordinate_data_multi[j][mdnie_tune_index][2]; + mdnie_data->dsi_white_default_b = coordinate_data_multi[j][mdnie_tune_index][4]; +#if defined(CONFIG_SEC_FACTORY) + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] += mdnie_data->dsi_white_balanced_r; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] += mdnie_data->dsi_white_balanced_g; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] += mdnie_data->dsi_white_balanced_b; +#endif + } + } + } + +} + + +void coordinate_tunning_calculate(struct samsung_display_driver_data *vdd, + int x, int y, + char (*coordinate_data_multi[MAX_MODE])[COORDINATE_DATA_SIZE], + int *rgb_index, int scr_wr_addr, int data_size) +{ + struct mdnie_lite_tune_data *mdnie_data = vdd->mdnie.mdnie_data; + int i, j; + int r, g, b; + int r_00, r_01, r_10, r_11; + int g_00, g_01, g_10, g_11; + int b_00, b_01, b_10, b_11; + struct dsi_cmd_desc *coordinate_tunning_data = NULL; + struct dsi_cmd_desc *coordinate_outdoor_data = NULL; + + DPRINT("coordinate_tunning_calculate index_0 : %d, index_1 : %d, index_2 : %d, index_3 : %d, x : %d, y : %d\n", + rgb_index[0], rgb_index[1], rgb_index[2], rgb_index[3], x, y); + + for (i = 0; i < MAX_APP_MODE; i++) { + for (j = 0; j < MAX_MODE; j++) { + if ((mdnie_data->mdnie_tune_value_dsi[i][j][0] != NULL) && (i != eBOOK_APP) && (j != READING_MODE)) { + coordinate_tunning_data = mdnie_data->mdnie_tune_value_dsi[i][j][0]; + + r_00 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[0]][0]; + r_01 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[1]][0]; + r_10 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[2]][0]; + r_11 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[3]][0]; + + g_00 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[0]][2]; + g_01 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[1]][2]; + g_10 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[2]][2]; + g_11 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[3]][2]; + + b_00 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[0]][4]; + b_01 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[1]][4]; + b_10 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[2]][4]; + b_11 = (int)(unsigned char)coordinate_data_multi[j][rgb_index[3]][4]; + + r = ((r_00 * (1024 - x) + r_10 * x) * (1024 - y) + (r_01 * (1024 - x) + r_11 * x) * y) + 524288; + r = r >> 20; + g = ((g_00 * (1024 - x) + g_10 * x) * (1024 - y) + (g_01 * (1024 - x) + g_11 * x) * y) + 524288; + g = g >> 20; + b = ((b_00 * (1024 - x) + b_10 * x) * (1024 - y) + (b_01 * (1024 - x) + b_11 * x) * y) + 524288; + b = b >> 20; + + if (i == 0 && j == 4) + DPRINT("coordinate_tunning_calculate_Adaptive r : %d, g : %d, b : %d\n", r, g, b); + if (i == 0 && j == 2) + DPRINT("coordinate_tunning_calculate_D65 r : %d, g : %d, b : %d\n", r, g, b); + + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = (char)r; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = (char)g; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = (char)b; + + if ((i == UI_APP) && (j == AUTO_MODE)) { + mdnie_data->dsi_white_default_r = (char)r; + mdnie_data->dsi_white_default_g = (char)g; + mdnie_data->dsi_white_default_b = (char)b; + + coordinate_outdoor_data = mdnie_data->DSI_HBM_CE_MDNIE; + if(coordinate_outdoor_data != NULL) { + coordinate_outdoor_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = (char)r; + coordinate_outdoor_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = (char)g; + coordinate_outdoor_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = (char)b; + } +#if defined(CONFIG_SEC_FACTORY) + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] += mdnie_data->dsi_white_balanced_r; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] += mdnie_data->dsi_white_balanced_g; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] += mdnie_data->dsi_white_balanced_b; +#endif + } + if ((i == UI_APP) && (j == DYNAMIC_MODE)) { + coordinate_outdoor_data = mdnie_data->DSI_HBM_CE_D65_MDNIE; + if(coordinate_outdoor_data != NULL) { + coordinate_outdoor_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = (char)r; + coordinate_outdoor_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = (char)g; + coordinate_outdoor_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = (char)b; + } + } + } + } + } +} + +void coordinate_tunning(struct samsung_display_driver_data *vdd, + char *coordinate_data, int scr_wr_addr, int data_size) +{ + struct mdnie_lite_tune_data *mdnie_data = vdd->mdnie.mdnie_data; + int i, j; + char white_r, white_g, white_b; + struct dsi_cmd_desc *coordinate_tunning_data = NULL; + + for (i = 0; i < MAX_APP_MODE; i++) { + for (j = 0; j < MAX_MODE; j++) { + if (mdnie_data->mdnie_tune_value_dsi[i][j][0] != NULL) { + coordinate_tunning_data = mdnie_data->mdnie_tune_value_dsi[i][j][0]; + white_r = coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]]; + white_g = coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]]; + white_b = coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]]; + if ((white_r == 0xff) && (white_g == 0xff) && (white_b == 0xff)) { + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] = coordinate_data[0]; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] = coordinate_data[2]; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] = coordinate_data[4]; + } + if ((i == UI_APP) && (j == AUTO_MODE)) { + mdnie_data->dsi_white_default_r = coordinate_data[0]; + mdnie_data->dsi_white_default_g = coordinate_data[2]; + mdnie_data->dsi_white_default_b = coordinate_data[4]; +#if defined(CONFIG_SEC_FACTORY) + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_RED_OFFSET]] += mdnie_data->dsi_white_balanced_r; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_GREEN_OFFSET]] += mdnie_data->dsi_white_balanced_g; + coordinate_tunning_data[mdnie_data->dsi_scr_step_index].msg.tx_buf[mdnie_data->address_scr_white[ADDRESS_SCR_WHITE_BLUE_OFFSET]] += mdnie_data->dsi_white_balanced_b; +#endif + } + } + } + } +} diff --git a/drivers/gpu/drm/msm/samsung/ss_dsi_mdnie_lite_common.h b/drivers/gpu/drm/msm/samsung/ss_dsi_mdnie_lite_common.h new file mode 100755 index 000000000000..537f6a187ded --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dsi_mdnie_lite_common.h @@ -0,0 +1,374 @@ +/* + * ================================================================= + * + * + * Description: samsung display common file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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_TCON_MDNIE_LIGHT_H_ +#define _SAMSUNG_DSI_TCON_MDNIE_LIGHT_H_ + +#include "ss_dsi_panel_common.h" + +#define NATURAL_MODE_ENABLE + +#define NAME_STRING_MAX 30 +#define MDNIE_COLOR_BLINDE_CMD_SIZE 18 +#define MDNIE_COLOR_BLINDE_HBM_CMD_SIZE 24 +#define COORDINATE_DATA_SIZE 6 +#define MDNIE_SCR_CMD_SIZE 24 +#define AFC_ROI_CMD_SIZE 12 + +extern char mdnie_app_name[][NAME_STRING_MAX]; +extern char mdnie_mode_name[][NAME_STRING_MAX]; +extern char outdoor_name[][NAME_STRING_MAX]; +extern struct mdnie_lite_tune_data mdnie_data; + +#define APP_ID_TDMB (20) /* for fake_id() */ + +enum BYPASS { + BYPASS_DISABLE = 0, + BYPASS_ENABLE, +}; + +enum APP { + UI_APP = 0, + VIDEO_APP, + VIDEO_WARM_APP, + VIDEO_COLD_APP, + CAMERA_APP, + NAVI_APP, + GALLERY_APP, + VT_APP, + BROWSER_APP, + eBOOK_APP, + EMAIL_APP, + GAME_LOW_APP, + GAME_MID_APP, + GAME_HIGH_APP, + VIDEO_ENHANCER, + VIDEO_ENHANCER_THIRD, + TDMB_APP, /* is linked to APP_ID_TDMB */ + MAX_APP_MODE, +}; + +enum MODE { + DYNAMIC_MODE = 0, + STANDARD_MODE, +#if defined(NATURAL_MODE_ENABLE) + NATURAL_MODE, +#endif + MOVIE_MODE, + AUTO_MODE, + READING_MODE, + MAX_MODE, +}; + +enum OUTDOOR { + OUTDOOR_OFF_MODE = 0, + OUTDOOR_ON_MODE, + MAX_OUTDOOR_MODE, +}; + +enum ACCESSIBILITY { + ACCESSIBILITY_OFF = 0, + NEGATIVE, + COLOR_BLIND, + CURTAIN, + GRAYSCALE, + GRAYSCALE_NEGATIVE, + COLOR_BLIND_HBM, + ACCESSIBILITY_MAX, +}; + +enum HMT_GRAY { + HMT_GRAY_OFF = 0, + HMT_GRAY_1, + HMT_GRAY_2, + HMT_GRAY_3, + HMT_GRAY_4, + HMT_GRAY_5, + HMT_GRAY_MAX, +}; + +enum HMT_COLOR_TEMPERATURE { + HMT_COLOR_TEMP_OFF = 0, + HMT_COLOR_TEMP_3000K, /* 3000K */ + HMT_COLOR_TEMP_4000K, /* 4000K */ + HMT_COLOR_TEMP_5000K, /* 5000K */ + HMT_COLOR_TEMP_6500K, /* 6500K */ + HMT_COLOR_TEMP_7500K, /* 7500K */ + HMT_COLOR_TEMP_MAX +}; + +enum HDR { + HDR_OFF = 0, + HDR_1, + HDR_2, + HDR_3, + HDR_4, + HDR_5, + HDR_MAX +}; + +enum LIGHT_NOTIFICATION { + LIGHT_NOTIFICATION_OFF = 0, + LIGHT_NOTIFICATION_ON, + LIGHT_NOTIFICATION_MAX, +}; + +enum COLOR_LENS { + COLOR_LENS_OFF = 0, + COLOR_LENS_ON, + COLOR_LENS_MAX +}; + +enum COLOR_LENS_COLOR { + COLOR_LENS_COLOR_BLUE = 0, + COLOR_LENS_COLOR_AZURE, + COLOR_LENS_COLOR_CYAN, + COLOR_LENS_COLOR_SPRING_GREEN, + COLOR_LENS_COLOR_GREEN, + COLOR_LENS_COLOR_CHARTREUSE_GREEN, + COLOR_LENS_COLOR_YELLOW, + COLOR_LENS_COLOR_ORANGE, + COLOR_LENS_COLOR_RED, + COLOR_LENS_COLOR_ROSE, + COLOR_LENS_COLOR_MAGENTA, + COLOR_LENS_COLOR_VIOLET, + COLOR_LENS_COLOR_MAX +}; + +enum COLOR_LENS_LEVEL { + COLOR_LENS_LEVEL_20P = 0, + COLOR_LENS_LEVEL_25P, + COLOR_LENS_LEVEL_30P, + COLOR_LENS_LEVEL_35P, + COLOR_LENS_LEVEL_40P, + COLOR_LENS_LEVEL_45P, + COLOR_LENS_LEVEL_50P, + COLOR_LENS_LEVEL_55P, + COLOR_LENS_LEVEL_60P, + COLOR_LENS_LEVEL_MAX, +}; + +struct mdnie_lite_tun_type { + enum BYPASS mdnie_bypass; + enum BYPASS cabc_bypass; + int hbm_enable; + + enum APP mdnie_app; + enum MODE mdnie_mode; + enum OUTDOOR outdoor; + enum ACCESSIBILITY mdnie_accessibility; + enum HMT_COLOR_TEMPERATURE hmt_color_temperature; + enum HDR hdr; + enum LIGHT_NOTIFICATION light_notification; + + char scr_white_red; + char scr_white_green; + char scr_white_blue; + + int night_mode_enable; + int night_mode_index; + int ldu_mode_index; + + int color_lens_enable; + int color_lens_color; + int color_lens_level; + + int afc_enable; + char afc_roi[AFC_ROI_CMD_SIZE]; + + struct samsung_display_driver_data *vdd; + struct notifier_block dpui_notif; +}; + +int config_cabc(struct samsung_display_driver_data *vdd, int value); + +enum { + MDNIE_STEP1 = 0, + MDNIE_STEP2, + MDNIE_STEP3, + MDNIE_STEP_MAX, +}; + +enum { + ADDRESS_SCR_WHITE_RED_OFFSET = 0, + ADDRESS_SCR_WHITE_GREEN_OFFSET, + ADDRESS_SCR_WHITE_BLUE_OFFSET, + ADDRESS_SCR_WHITE_MAX, +}; + +/* COMMON DATA THAT POINT TO MDNIE TUNING DATA */ +struct mdnie_lite_tune_data { + char *DSI_COLOR_BLIND_MDNIE_1; + char *DSI_COLOR_BLIND_MDNIE_2; + char *DSI_COLOR_BLIND_MDNIE_SCR; + char *DSI_RGB_SENSOR_MDNIE_1; + char *DSI_RGB_SENSOR_MDNIE_2; + char *DSI_RGB_SENSOR_MDNIE_3; + char *DSI_RGB_SENSOR_MDNIE_SCR; + char *DSI_TRANS_DIMMING_MDNIE; + char *DSI_UI_DYNAMIC_MDNIE_2; + char *DSI_UI_STANDARD_MDNIE_2; + char *DSI_UI_AUTO_MDNIE_2; + char *DSI_VIDEO_DYNAMIC_MDNIE_2; + char *DSI_VIDEO_STANDARD_MDNIE_2; + char *DSI_VIDEO_AUTO_MDNIE_2; + char *DSI_CAMERA_MDNIE_2; + char *DSI_CAMERA_AUTO_MDNIE_2; + char *DSI_GALLERY_DYNAMIC_MDNIE_2; + char *DSI_GALLERY_STANDARD_MDNIE_2; + char *DSI_GALLERY_AUTO_MDNIE_2; + char *DSI_VT_DYNAMIC_MDNIE_2; + char *DSI_VT_STANDARD_MDNIE_2; + char *DSI_VT_AUTO_MDNIE_2; + char *DSI_BROWSER_DYNAMIC_MDNIE_2; + char *DSI_BROWSER_STANDARD_MDNIE_2; + char *DSI_BROWSER_AUTO_MDNIE_2; + char *DSI_EBOOK_DYNAMIC_MDNIE_2; + char *DSI_EBOOK_STANDARD_MDNIE_2; + char *DSI_EBOOK_AUTO_MDNIE_2; + char *DSI_TDMB_DYNAMIC_MDNIE_2; + char *DSI_TDMB_STANDARD_MDNIE_2; + char *DSI_TDMB_AUTO_MDNIE_2; + char *DSI_NIGHT_MODE_MDNIE_1; + char *DSI_NIGHT_MODE_MDNIE_2; + char *DSI_NIGHT_MODE_MDNIE_SCR; + char *DSI_COLOR_LENS_MDNIE_1; + char *DSI_COLOR_LENS_MDNIE_2; + char *DSI_COLOR_LENS_MDNIE_SCR; + char *DSI_AFC; + char *DSI_AFC_ON; + char *DSI_AFC_OFF; + + struct dsi_cmd_desc *DSI_BYPASS_MDNIE; + struct dsi_cmd_desc *DSI_NEGATIVE_MDNIE; + struct dsi_cmd_desc *DSI_COLOR_BLIND_MDNIE; + struct dsi_cmd_desc *DSI_HBM_CE_MDNIE; + struct dsi_cmd_desc *DSI_HBM_CE_D65_MDNIE; + struct dsi_cmd_desc *DSI_HBM_CE_TEXT_MDNIE; + struct dsi_cmd_desc *DSI_RGB_SENSOR_MDNIE; + struct dsi_cmd_desc *DSI_CURTAIN; + struct dsi_cmd_desc *DSI_GRAYSCALE_MDNIE; + struct dsi_cmd_desc *DSI_GRAYSCALE_NEGATIVE_MDNIE; + struct dsi_cmd_desc *DSI_UI_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_UI_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_UI_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_UI_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_UI_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_OUTDOOR_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_WARM_OUTDOOR_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_WARM_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_COLD_OUTDOOR_MDNIE; + struct dsi_cmd_desc *DSI_VIDEO_COLD_MDNIE; + struct dsi_cmd_desc *DSI_CAMERA_OUTDOOR_MDNIE; + struct dsi_cmd_desc *DSI_CAMERA_MDNIE; + struct dsi_cmd_desc *DSI_CAMERA_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_GALLERY_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_GALLERY_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_GALLERY_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_GALLERY_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_GALLERY_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_VT_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_VT_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_VT_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_VT_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_VT_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_BROWSER_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_BROWSER_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_BROWSER_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_BROWSER_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_BROWSER_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_EBOOK_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_EBOOK_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_EBOOK_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_EBOOK_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_EBOOK_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_EMAIL_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_GAME_LOW_MDNIE; + struct dsi_cmd_desc *DSI_GAME_MID_MDNIE; + struct dsi_cmd_desc *DSI_GAME_HIGH_MDNIE; + struct dsi_cmd_desc *DSI_TDMB_DYNAMIC_MDNIE; + struct dsi_cmd_desc *DSI_TDMB_STANDARD_MDNIE; + struct dsi_cmd_desc *DSI_TDMB_NATURAL_MDNIE; + struct dsi_cmd_desc *DSI_TDMB_MOVIE_MDNIE; + struct dsi_cmd_desc *DSI_TDMB_AUTO_MDNIE; + struct dsi_cmd_desc *DSI_NIGHT_MODE_MDNIE; + struct dsi_cmd_desc *DSI_COLOR_LENS_MDNIE; + + struct dsi_cmd_desc *(*mdnie_tune_value_dsi)[MAX_MODE][MAX_OUTDOOR_MODE]; + struct dsi_cmd_desc **hmt_color_temperature_tune_value_dsi; + struct dsi_cmd_desc **hdr_tune_value_dsi; + struct dsi_cmd_desc **light_notification_tune_value_dsi; + + int dsi_bypass_mdnie_size; + int mdnie_color_blinde_cmd_offset; + int mdnie_step_index[MDNIE_STEP_MAX]; + int address_scr_white[ADDRESS_SCR_WHITE_MAX]; + int dsi_rgb_sensor_mdnie_1_size; + int dsi_rgb_sensor_mdnie_2_size; + int dsi_rgb_sensor_mdnie_3_size; + int dsi_trans_dimming_data_index; + char **dsi_adjust_ldu_table; + int dsi_max_adjust_ldu; + char *dsi_night_mode_table; + int dsi_max_night_mode_index; + char *dsi_color_lens_table; + int dsi_scr_step_index; + char dsi_white_default_r; + char dsi_white_default_g; + char dsi_white_default_b; + char dsi_white_ldu_r; + char dsi_white_ldu_g; + char dsi_white_ldu_b; + int dsi_white_balanced_r; + int dsi_white_balanced_g; + int dsi_white_balanced_b; + int dsi_afc_size; + int dsi_afc_index; +}; + +/* COMMON FUNCTION*/ +struct mdnie_lite_tun_type *init_dsi_tcon_mdnie_class(struct samsung_display_driver_data *vdd); +int update_dsi_tcon_mdnie_register(struct samsung_display_driver_data *vdd); +void coordinate_tunning(struct samsung_display_driver_data *vdd, + char *coordinate_data, int scr_wr_addr, int data_size); +void coordinate_tunning_calculate(struct samsung_display_driver_data *vdd, + int x, int y, + char (*coordinate_data_multi[MAX_MODE])[COORDINATE_DATA_SIZE], + int *rgb_index, int scr_wr_addr, int data_size); + + +void coordinate_tunning_multi(struct samsung_display_driver_data *vdd, + char (*coordinate_data_multi[MAX_MODE])[COORDINATE_DATA_SIZE], int mdnie_tune_index, int scr_wr_addr, int data_size); + +/* COMMON FUNCTION END*/ + +#endif /*_DSI_TCON_MDNIE_H_*/ diff --git a/drivers/gpu/drm/msm/samsung/ss_dsi_panel_ap_pwm.h b/drivers/gpu/drm/msm/samsung/ss_dsi_panel_ap_pwm.h new file mode 100755 index 000000000000..702b54103e82 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dsi_panel_ap_pwm.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef SS_DSI_PANEL_AP_PWM_H +#define SS_DSI_PANEL_AP_PWM_H + +#include +#include + +#define GP_CLK_M_DEFAULT 1 +#define GP_CLK_N_DEFAULT 127 +#define GP_CLK_D_DEFAULT 127 + +extern void __iomem *virt_mmss_gp_base; + +#define __inp(port) ioread8(port) +#define __inpw(port) ioread16(port) +#define __inpdw(port) ioread32(port) +#define __outp(port, val) iowrite8(val, port) +#define __outpw(port, val) iowrite16(val, port) +#define __outpdw(port, val) iowrite32(val, port) + +#define in_dword(addr) (__inpdw(addr)) +#define in_dword_masked(addr, mask) (__inpdw(addr) & (mask)) +#define out_dword(addr, val) __outpdw(addr, val) +#define out_dword_masked(io, mask, val, shadow) { \ + (void) out_dword(io, \ + ((shadow & (unsigned int)(~(mask))) | ((unsigned int)((val) & (mask))))) \ +} + +#define out_dword_masked_ns(io, mask, val, current_reg_content) { \ + (void) out_dword(io, \ + ((current_reg_content & (unsigned int)(~(mask))) \ + | ((unsigned int)((val) & (mask))))) \ +} + +#define HWIO_CAMSS_GP0_CBCR_ADDR ((void __iomem *)(virt_mmss_gp_base + 0)) /* MMSS_CC_CAMSS_GP0_CBCR */ +#define HWIO_GP0_CMD_RCGR_ADDR ((void __iomem *)(virt_mmss_gp_base + 4)) /* MMSS_CC_GP0_CMD_RCGR */ +#define HWIO_GP0_CFG_RCGR_ADDR ((void __iomem *)(virt_mmss_gp_base + 8)) /* MMSS_CC_GP0_CFG_RCGR */ +#define HWIO_GP_M_REG_ADDR ((void __iomem *)(virt_mmss_gp_base + 0xC)) /* MMSS_CC_GP0_M */ +#define HWIO_GP_NS_REG_ADDR ((void __iomem *)(virt_mmss_gp_base + 0x10)) /* MMSS_CC_GP0_N */ +#define HWIO_GP_D_REG_ADDR ((void __iomem *)(virt_mmss_gp_base + 0x14)) /* MMSS_CC_GP0_D */ + +#define HWIO_GP_MD_REG_RMSK 0xffffffff +#define HWIO_GP_NS_REG_RMSK 0xffffffff +#define HWIO_GP_MD_REG_M_VAL_BMSK 0xff +#define HWIO_GP_MD_REG_M_VAL_SHFT 0 +#define HWIO_GP_MD_REG_D_VAL_BMSK 0xff +#define HWIO_GP_MD_REG_D_VAL_SHFT 0 +#define HWIO_GP_NS_REG_GP_N_VAL_BMSK 0xff +#define HWIO_GP_SRC_SEL_VAL_BMSK 0x700 +#define HWIO_GP_SRC_SEL_VAL1_SHFT 0 /*set 1*/ +#define HWIO_GP_SRC_SEL_VAL0_SHFT 8 /*set 0*/ +#define HWIO_GP_SRC_DIV_VAL_BMSK 0x1f +#define HWIO_GP_SRC_DIV_VAL_SHFT 0 +#define HWIO_GP_MODE_VAL_BMSK 0x3000 +#define HWIO_GP_MODE_VAL_SHFT 12 + +#define HWIO_CLK_ENABLE_VAL_BMSK 0x1 +#define HWIO_CLK_ENABLE_VAL_SHFT 0 +#define HWIO_UPDATE_VAL_BMSK 0x1 +#define HWIO_UPDATE_VAL_SHFT 0 +#define HWIO_ROOT_EN_VAL_BMSK 0x2 +#define HWIO_ROOT_EN_VAL_SHFT 1 + +#define HWIO_GP0_CMD_RCGR_IN in_dword_masked(HWIO_GP0_CMD_RCGR_ADDR, HWIO_GP_NS_REG_RMSK) +#define HWIO_GP0_CMD_RCGR_OUTM(m, v) out_dword_masked_ns(HWIO_GP0_CMD_RCGR_ADDR, m, v, HWIO_GP0_CMD_RCGR_IN) + +#define HWIO_GP0_CFG_RCGR_IN in_dword_masked(HWIO_GP0_CFG_RCGR_ADDR, HWIO_GP_NS_REG_RMSK) +#define HWIO_GP0_CFG_RCGR_OUTM(m, v) out_dword_masked_ns(HWIO_GP0_CFG_RCGR_ADDR, m, v, HWIO_GP0_CFG_RCGR_IN) + +#define HWIO_CAMSS_GP0_CBCR_IN in_dword_masked(HWIO_CAMSS_GP0_CBCR_ADDR, HWIO_GP_NS_REG_RMSK) +#define HWIO_CAMSS_GP0_CBCR_OUTM(m, v) out_dword_masked_ns(HWIO_CAMSS_GP0_CBCR_ADDR, m, v, HWIO_CAMSS_GP0_CBCR_IN) + +#define HWIO_GP_D_REG_IN in_dword_masked(HWIO_GP_D_REG_ADDR, HWIO_GP_MD_REG_RMSK) +#define HWIO_GP_D_REG_OUTM(m, v) out_dword_masked_ns(HWIO_GP_D_REG_ADDR, m, v, HWIO_GP_D_REG_IN) + +#define HWIO_GP_M_REG_IN in_dword_masked(HWIO_GP_M_REG_ADDR, HWIO_GP_MD_REG_RMSK) +#define HWIO_GP_M_REG_OUTM(m, v) out_dword_masked_ns(HWIO_GP_M_REG_ADDR, m, v, HWIO_GP_M_REG_IN) + +#define HWIO_GP_NS_REG_IN in_dword_masked(HWIO_GP_NS_REG_ADDR, HWIO_GP_NS_REG_RMSK) +#define HWIO_GP_NS_REG_OUTM(m, v) out_dword_masked_ns(HWIO_GP_NS_REG_ADDR, m, v, HWIO_GP_NS_REG_IN) + +#define __msmhwio_outm(hwiosym, mask, val) HWIO_##hwiosym##_OUTM(mask, val) +#define HWIO_OUTM(hwiosym, mask, val) __msmhwio_outm(hwiosym, mask, val) +#endif diff --git a/drivers/gpu/drm/msm/samsung/ss_dsi_panel_common.c b/drivers/gpu/drm/msm/samsung/ss_dsi_panel_common.c new file mode 100755 index 000000000000..24db06325384 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dsi_panel_common.c @@ -0,0 +1,5125 @@ +/* + * ================================================================= + * + * + * Description: samsung display common file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +Copyright (C) 2015, 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_common.h" + +static void ss_panel_recovery(struct samsung_display_driver_data *vdd); +static void ss_event_osc_te_fitting( + struct samsung_display_driver_data *vdd, int event, void *arg); +static irqreturn_t samsung_te_check_handler(int irq, void *handle); +static void samsung_te_check_done_work(struct work_struct *work); +static void ss_event_esd_recovery_init( + struct samsung_display_driver_data *vdd, int event, void *arg); +static void samsung_display_delay_disp_on_work(struct work_struct *work); +static void read_panel_data_work_fn(struct work_struct *work); + +struct samsung_display_driver_data vdd_data[MAX_DISPLAY_NDX]; + +void __iomem *virt_mmss_gp_base; + +LIST_HEAD(vdds_list); + +char ss_panel_id0_get(struct samsung_display_driver_data *vdd) +{ + return (vdd->manufacture_id_dsi & 0xFF0000) >> 16; +} + +char ss_panel_id1_get(struct samsung_display_driver_data *vdd) +{ + return (vdd->manufacture_id_dsi & 0xFF00) >> 8; +} + +char ss_panel_id2_get(struct samsung_display_driver_data *vdd) +{ + return vdd->manufacture_id_dsi & 0xFF; +} + +char ss_panel_rev_get(struct samsung_display_driver_data *vdd) +{ + return vdd->manufacture_id_dsi & 0x0F; +} + +int ss_panel_attach_get(struct samsung_display_driver_data *vdd) +{ + return vdd->panel_attach_status; +} + +int ss_panel_attach_set(struct samsung_display_driver_data *vdd, bool attach) +{ + /* 0bit->DSI0 1bit->DSI1 */ + /* check the lcd id for DISPLAY_1 and DISPLAY_2 */ + if (likely(ss_panel_attached(vdd->ndx) && attach)) + vdd->panel_attach_status = true; + else + vdd->panel_attach_status = false; + + LCD_INFO("panel_attach_status : %d\n", vdd->panel_attach_status); + + return vdd->panel_attach_status; +} + +/* + * Check the lcd id for DISPLAY_1 and DISPLAY_2 using the ndx + */ +int ss_panel_attached(int ndx) +{ + int lcd_id = 0; + + /* + * ndx 0 means DISPLAY_1 and ndx 1 means DISPLAY_2 + */ + if (ndx == PRIMARY_DISPLAY_NDX) + lcd_id = get_lcd_attached("GET"); + else if (ndx == SECONDARY_DISPLAY_NDX) + lcd_id = get_lcd_attached_secondary("GET"); + + /* + * The 0xFFFFFF is the id for PBA booting + * if the id is same with 0xFFFFFF, this function + * will return 0 + */ + return !(lcd_id == PBA_ID); +} + +static int ss_parse_panel_id(char *panel_id) +{ + char *pt; + int lcd_id = 0; + + if (!IS_ERR_OR_NULL(panel_id)) + pt = panel_id; + else + return lcd_id; + + for (pt = panel_id; *pt != 0; pt++) { + lcd_id <<= 4; + switch (*pt) { + case '0' ... '9': + lcd_id += *pt - '0'; + break; + case 'a' ... 'f': + lcd_id += 10 + *pt - 'a'; + break; + case 'A' ... 'F': + lcd_id += 10 + *pt - 'A'; + break; + } + } + + return lcd_id; +} + +int get_lcd_attached(char *mode) +{ + static int lcd_id; + + LCD_DEBUG("%s", mode); + + if (mode == NULL) + return true; + + if (!strncmp(mode, "GET", 3)) + goto end; + else + lcd_id = ss_parse_panel_id(mode); + + LCD_ERR("LCD_ID = 0x%X\n", lcd_id); +end: + return lcd_id; +} +EXPORT_SYMBOL(get_lcd_attached); +__setup("lcd_id=0x", get_lcd_attached); + +int get_lcd_attached_secondary(char *mode) +{ + static int lcd_id; + + LCD_DEBUG("%s", mode); + + if (mode == NULL) + return true; + + if (!strncmp(mode, "GET", 3)) + goto end; + else + lcd_id = ss_parse_panel_id(mode); + + LCD_ERR("LCD_ID = 0x%X\n", lcd_id); +end: + return lcd_id; +} +EXPORT_SYMBOL(get_lcd_attached_secondary); +__setup("lcd_id1=0x", get_lcd_attached_secondary); + +static void ss_event_frame_update_pre( + struct samsung_display_driver_data *vdd, void *arg) +{ + /* Block frame update during exclusive mode on.*/ + if (unlikely(vdd->exclusive_tx.enable)) { + if (unlikely(!vdd->exclusive_tx.permit_frame_update)) { + LCD_INFO("exclusive mode on, stop frame update\n"); + wait_event(vdd->exclusive_tx.ex_tx_waitq, + !vdd->exclusive_tx.enable); + LCD_INFO("continue frame update\n"); + } + } +} + +static void ss_event_frame_update( + struct samsung_display_driver_data *vdd, int event, void *arg) +{ + struct panel_func *panel_func = NULL; + static u8 frame_count = 1; + s64 cur_time_64; + int wait_time_32; + s64 wait_time_64; + int sleep_out_delay_32; + s64 sleep_out_delay_64; + + panel_func = &vdd->panel_func; + + sleep_out_delay_32 = vdd->dtsi_data.samsung_wait_after_sleep_out_delay; + sleep_out_delay_64 = (s64)sleep_out_delay_32; + + cur_time_64 = ktime_to_ms(ktime_get()); + + if (gpio_is_valid(vdd->dtsi_data.samsung_tcon_rdy_gpio)) + wait_time_64 = sleep_out_delay_64 - (cur_time_64 - vdd->reset_time_64); + else + wait_time_64 = sleep_out_delay_64 - (cur_time_64 - vdd->sleep_out_time_64); + + /* To protect 64bit overflow & underflow */ + if (wait_time_64 <= 0) + wait_time_32 = 0; + else if (wait_time_64 > sleep_out_delay_64) + wait_time_32 = sleep_out_delay_32; + else + wait_time_32 = (s32)wait_time_64; + + if (vdd->dtsi_data.samsung_osc_te_fitting && + !(vdd->te_fitting_info.status & TE_FITTING_DONE)) { + if (panel_func->ss_event_osc_te_fitting) + panel_func->ss_event_osc_te_fitting(vdd, event, arg); + } + + if (vdd->display_status_dsi.wait_disp_on) { + /* TODO: Add condition to check the susupend state + * insted of !msm_is_suspend_state(GET_DRM_DEV(vdd)) + */ + + /* Skip a number of frames to avoid garbage image output from wakeup */ + if (frame_count <= vdd->dtsi_data.samsung_delayed_display_on) { + LCD_DEBUG("Skip %d frame\n", frame_count); + frame_count++; + goto skip_display_on; + } + frame_count = 1; + + if (vdd->dtsi_data.samsung_wait_after_sleep_out_delay) { + if (wait_time_32 > 0) { + LCD_ERR("sleep_out_delay:%d sleep_out_t:%llu cur_t:%llu wait_t:%d [ms] start\n", sleep_out_delay_32, + vdd->sleep_out_time_64, cur_time_64, wait_time_32); + usleep_range(wait_time_32*1000, wait_time_32*1000); + LCD_ERR("wait_t: %d end\n", wait_time_32); + } else + LCD_ERR("sleep_out_delay:%d sleep_out_t:%llu cur_t:%llu wait_t:%d [ms] skip\n", sleep_out_delay_32, + vdd->sleep_out_time_64, cur_time_64, wait_time_32); + } + + ss_send_cmd(vdd, TX_DISPLAY_ON); + vdd->display_status_dsi.wait_disp_on = false; + + if (vdd->mdnie.support_mdnie) { + vdd->mdnie.lcd_on_notifiy = true; + update_dsi_tcon_mdnie_register(vdd); + if (vdd->mdnie.support_trans_dimming) + vdd->mdnie.disable_trans_dimming = false; + } + + if (vdd->panel_func.samsung_backlight_late_on) + vdd->panel_func.samsung_backlight_late_on(vdd); + + if (vdd->dtsi_data.hmt_enabled && + ss_is_panel_on(vdd)) { + if (vdd->hmt_stat.hmt_on) { + LCD_INFO("hmt reset ..\n"); + #if 0 + vdd->hmt_stat.hmt_enable(vdd); + vdd->hmt_stat.hmt_reverse_update(vdd, 1); + vdd->hmt_stat.hmt_bright_update(vdd); + #endif + } + } + + if (ss_is_esd_check_enabled(vdd)) + vdd->esd_recovery.is_enabled_esd_recovery = true; + + /* For read flash gamma data before first brightness set */ + if (vdd->dtsi_data.flash_gamma_support && !vdd->panel_br_info.flash_data.init_done) { + if (!work_busy(&vdd->flash_br_work.work)) { + queue_delayed_work(vdd->flash_br_workqueue, &vdd->flash_br_work, msecs_to_jiffies(0)); + } + } + + LCD_INFO("DISPLAY_ON\n"); + } + + /* copr - check actual display on (frame - copr > 0) debug */ + /* work thread will be stopped if copr is over 0 */ + if (vdd->display_status_dsi.wait_actual_disp_on && ss_is_panel_on(vdd)) { + if (vdd->copr.read_copr_wq && vdd->copr.copr_on) + queue_work(vdd->copr.read_copr_wq, &vdd->copr.read_copr_work); + } +skip_display_on: + return; +} + +static void ss_send_esd_recovery_cmd(struct samsung_display_driver_data *vdd) +{ + static bool toggle; + + if (toggle) + ss_send_cmd(vdd, TX_ESD_RECOVERY_1); + else + ss_send_cmd(vdd, TX_ESD_RECOVERY_2); + toggle = !toggle; +} + +static void ss_check_te(struct samsung_display_driver_data *vdd) +{ + unsigned int disp_te_gpio; + int rc, te_count = 0; + int te_max = 20000; /*sampling 200ms */ + + disp_te_gpio = ss_get_te_gpio(vdd); + + LCD_INFO("============ start waiting for TE ============\n"); + if (gpio_is_valid(disp_te_gpio)) { + for (te_count = 0 ; te_count < te_max ; te_count++) { + rc = gpio_get_value(disp_te_gpio); + if (rc == 1) { + LCD_INFO("LDI generate TE within = %d.%dms\n", te_count/100, te_count%100); + break; + } + /* usleep suspends the calling thread whereas udelay is a + * busy wait. Here the value of te_gpio is checked in a loop of + * max count = 250. If this loop has to iterate multiple + * times before the te_gpio is 1, the calling thread will end + * up in suspend/wakeup sequence multiple times if usleep is + * used, which is an overhead. So use udelay instead of usleep. + */ + udelay(10); + } + if (te_count == te_max) { + LCD_ERR("LDI doesn't generate TE"); + SS_XLOG(0xbad); + inc_dpui_u32_field(DPUI_KEY_QCT_NO_TE, 1); + } + } else + LCD_ERR("disp_te_gpio is not valid\n"); + LCD_INFO("============ end waiting for TE ============\n"); +} + +static void ss_event_fb_event_callback(struct samsung_display_driver_data *vdd, int event, void *arg) +{ + struct panel_func *panel_func = NULL; + + panel_func = &vdd->panel_func; + + if (IS_ERR_OR_NULL(panel_func)) { + LCD_ERR("Invalid data panel_func : 0x%zx\n", + (size_t)panel_func); + return; + } + + if (panel_func->ss_event_esd_recovery_init) + panel_func->ss_event_esd_recovery_init(vdd, event, arg); +} + +static int _ss_dsi_panel_event_handler( + struct samsung_display_driver_data *vdd, + enum mdss_intf_events event, void *arg) +{ + struct panel_func *panel_func = NULL; + + panel_func = &vdd->panel_func; + + if (IS_ERR_OR_NULL(panel_func)) { + LCD_ERR("Invalid data panel_func : 0x%zx\n", (size_t)panel_func); + return -EINVAL; + } + + switch (event) { + case SS_EVENT_FRAME_UPDATE_POST: + if (!IS_ERR_OR_NULL(panel_func->ss_event_frame_update)) + panel_func->ss_event_frame_update(vdd, event, arg); + break; + case SS_EVENT_FRAME_UPDATE_PRE: + ss_event_frame_update_pre(vdd, arg); + break; + case SS_EVENT_FB_EVENT_CALLBACK: + if (!IS_ERR_OR_NULL(panel_func->ss_event_fb_event_callback)) + panel_func->ss_event_fb_event_callback(vdd, event, arg); + break; + case SS_EVENT_PANEL_ON: + if (likely(!vdd->is_factory_mode)) + ss_panel_lpm_ctrl(vdd, false); + break; + case SS_EVENT_PANEL_OFF: + if (likely(!vdd->is_factory_mode)) + ss_panel_lpm_ctrl(vdd, true); + break; + case SS_EVENT_PANEL_RECOVERY: + ss_panel_recovery(vdd); + break; + case SS_EVENT_PANEL_ESD_RECOVERY: + if (vdd->esd_recovery.send_esd_recovery) + ss_send_esd_recovery_cmd(vdd); + break; + case SS_EVENT_CHECK_TE: + ss_check_te(vdd); + break; + case SS_EVENT_SDE_HW_CATALOG_INIT: + if (unlikely(vdd->panel_func.samsung_pba_config)) + vdd->panel_func.samsung_pba_config(vdd, arg); + break; + default: + LCD_DEBUG("unhandled event=%d\n", event); + break; + } + + return 0; +} + +int ss_dsi_panel_event_handler( + int display_ndx, enum mdss_intf_events event, void *arg) +{ + + struct samsung_display_driver_data *vdd = ss_get_vdd(display_ndx); + + if (unlikely(!vdd)) + return -EINVAL; + + return _ss_dsi_panel_event_handler(vdd, event, arg); +} + +/* CP notity format (HEX raw format) + * 10 00 AA BB 27 01 03 XX YY YY YY YY ZZ ZZ ZZ ZZ + * + * 00 10 (0x0010) - len + * AA BB - not used + * 27 - MAIN CMD (SYSTEM CMD : 0x27) + * 01 - SUB CMD (CP Channel Info : 0x01) + * 03 - NOTI CMD (0x03) + * XX - RAT MODE + * YY YY YY YY - BAND MODE + * ZZ ZZ ZZ ZZ - FREQ INFO + */ + +int ss_rf_info_notify_callback(struct notifier_block *nb, + unsigned long size, void *data) +{ + struct rf_info *rf_info = container_of(nb, struct rf_info, notifier); + struct rf_noti_info *info; + + LCD_INFO("RF Info Notifiy Callback, Size=%lu\n", size); + + if (size == sizeof(struct rf_noti_info)) { + info = (struct rf_noti_info *)data; + + if (info->main_cmd == 0x27 && info->sub_cmd == 0x01 && + info->cmd_type == 0x03) { + mutex_lock(&rf_info->vdd_dyn_mipi_lock); + rf_info->rat = info->rat; + rf_info->band = info->band; + rf_info->arfcn = info->arfcn; + mutex_unlock(&rf_info->vdd_dyn_mipi_lock); + } + } + + LCD_INFO("RF Info Notify RAT(%d), BAND(%d), ARFCN(%d)\n", + rf_info->rat, rf_info->band, rf_info->arfcn); + + return 0; +} + +static int ss_find_dyn_mipi_clk_timing_idx(struct samsung_display_driver_data *vdd) +{ + int idx = 0; + int loop; + int rat, band, arfcn; + struct clk_sel_table sel_table = vdd->dyn_mipi_clk.clk_sel_table; + + if (!sel_table.tab_size) { + LCD_ERR("Table is NULL"); + return 0; + } + + rat = vdd->rf_info.rat; + band = vdd->rf_info.band; + arfcn = vdd->rf_info.arfcn; + + for (loop = 0 ; loop < sel_table.tab_size ; loop++) { + if ((rat == sel_table.rat[loop]) && (band == sel_table.band[loop])) { + if ((arfcn >= sel_table.from[loop]) && (arfcn <= sel_table.end[loop])) { + idx = sel_table.target_clk_idx[loop]; + break; + } + } + } + + LCD_INFO("RAT(%d), BAND(%d), ARFCN(%d), Clock Index(%d)\n", + rat, band, arfcn, idx); + + return idx; + +} + +int ss_change_dyn_mipi_clk_timing(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + int idx = 0; + size_t loop; + u8 *ffc_pload = NULL; + u8 *ffc_set_pload = NULL; + struct dsi_panel_cmd_set *ffc_cmds = NULL; + struct dsi_panel_cmd_set *ffc_set_cmds = NULL; + struct clk_timing_table timing_table = vdd->dyn_mipi_clk.clk_timing_table; + struct dsi_display *dsi_disp = GET_DSI_DISPLAY(vdd); + struct dsi_mode_info *timing = &dsi_disp->config.video_timing; + + if (!vdd->dyn_mipi_clk.is_support) + LCD_DEBUG("Dynamic MIPI Clock does not support\n"); + + if (!timing_table.tab_size) { + LCD_ERR("Table is NULL"); + return -ENODEV; + } + + mutex_lock(&vdd->rf_info.vdd_dyn_mipi_lock); + + idx = ss_find_dyn_mipi_clk_timing_idx(vdd); + + LCD_DEBUG("Timing IDX = %d\n", idx); + + if (!idx) + LCD_ERR("Failed to find MIPI clock timing, idx=0\n"); + + /* Timing Change */ + timing->refresh_rate = timing_table.fps[idx]; + timing->h_front_porch = timing_table.hfp[idx]; + timing->h_back_porch = timing_table.hbp[idx]; + timing->h_sync_width = timing_table.hsw[idx]; + timing->h_skew = timing_table.hss[idx]; + timing->v_front_porch = timing_table.vfp[idx]; + timing->v_back_porch = timing_table.vbp[idx]; + timing->v_sync_width = timing_table.vsw[idx]; + + /* FFC Command Change */ + ffc_cmds = ss_get_cmds(vdd, TX_FFC); + if (SS_IS_CMDS_NULL(ffc_cmds)) { + LCD_ERR("No cmds for TX_FFC..\n"); + ret = -EINVAL; + goto err; + } + + ffc_set_cmds = ss_get_cmds(vdd, TX_DYNAMIC_FFC_SET); + if (SS_IS_CMDS_NULL(ffc_set_cmds)) { + LCD_ERR("No cmds for TX_DYNAMIC_FFC_SET..\n"); + ret = -EINVAL; + goto err; + } + + ffc_pload = ffc_cmds->cmds[1].msg.tx_buf; + ffc_set_pload = ffc_set_cmds->cmds[idx].msg.tx_buf; + + for (loop = 0; loop < ffc_cmds->cmds[1].msg.tx_len ; loop++) + ffc_pload[loop] = ffc_set_pload[loop]; + +err: + mutex_unlock(&vdd->rf_info.vdd_dyn_mipi_lock); + + LCD_INFO("FPS(%d), HFP(%d), HBP(%d), HSW(%d), HSKEW(%d), VFP(%d), VBP(%d), VSW(%d)\n", + timing->refresh_rate, timing->h_front_porch, + timing->h_back_porch, timing->h_sync_width, + timing->h_skew, timing->v_front_porch, + timing->v_back_porch, timing->v_sync_width); + + return ret; +} + +int ss_parse_dyn_mipi_clk_timing_table(struct device_node *np, + void *tbl, char *keystring) +{ + struct clk_timing_table *table = (struct clk_timing_table *) tbl; + const __be32 *data; + int len = 0, i = 0, data_offset = 0; + int col_size = 0; + + data = of_get_property(np, keystring, &len); + + if (data) + LCD_INFO("Success to read table %s\n", keystring); + else { + LCD_ERR("%d, Unable to read table %s ", __LINE__, keystring); + return -EINVAL; + } + + col_size = 8; + + if ((len % col_size) != 0) { + LCD_ERR("%d, Incorrect table entries for %s , len : %d", + __LINE__, keystring, len); + return -EINVAL; + } + + table->tab_size = len / (sizeof(int) * col_size); + + table->fps = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->fps) + return -ENOMEM; + table->hfp = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->hfp) + goto error; + table->hbp = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->hbp) + goto error; + table->hsw = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->hsw) + goto error; + table->hss = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->hss) + goto error; + table->vfp = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->vfp) + goto error; + table->vbp = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->vbp) + goto error; + table->vsw = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->vsw) + goto error; + + for (i = 0 ; i < table->tab_size; i++) { + table->fps[i] = be32_to_cpup(&data[data_offset++]); + table->hfp[i] = be32_to_cpup(&data[data_offset++]); + table->hbp[i] = be32_to_cpup(&data[data_offset++]); + table->hsw[i] = be32_to_cpup(&data[data_offset++]); + table->hss[i] = be32_to_cpup(&data[data_offset++]); + table->vfp[i] = be32_to_cpup(&data[data_offset++]); + table->vbp[i] = be32_to_cpup(&data[data_offset++]); + table->vsw[i] = be32_to_cpup(&data[data_offset++]); + } + + LCD_INFO("%s tab_size (%d)\n", keystring, table->tab_size); + + return 0; + +error: + LCD_ERR("Allocation Fail\n"); + kfree(table->fps); + kfree(table->hfp); + kfree(table->hbp); + kfree(table->hsw); + kfree(table->hss); + kfree(table->vfp); + kfree(table->vbp); + kfree(table->vsw); + + return -ENOMEM; +} + +int ss_parse_dyn_mipi_clk_sel_table(struct device_node *np, + void *tbl, char *keystring) +{ + struct clk_sel_table *table = (struct clk_sel_table *) tbl; + const __be32 *data; + int len = 0, i = 0, data_offset = 0; + int col_size = 0; + + data = of_get_property(np, keystring, &len); + + if (data) + LCD_INFO("Success to read table %s\n", keystring); + else { + LCD_ERR("%d, Unable to read table %s ", __LINE__, keystring); + return -EINVAL; + } + + col_size = 5; + + if ((len % col_size) != 0) { + LCD_ERR("%d, Incorrect table entries for %s , len : %d", + __LINE__, keystring, len); + return -EINVAL; + } + + table->tab_size = len / (sizeof(int) * col_size); + + table->rat = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->rat) + return -ENOMEM; + table->band = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->band) + goto error; + table->from = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->from) + goto error; + table->end = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->end) + goto error; + table->target_clk_idx = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->target_clk_idx) + goto error; + + for (i = 0 ; i < table->tab_size; i++) { + table->rat[i] = be32_to_cpup(&data[data_offset++]); + table->band[i] = be32_to_cpup(&data[data_offset++]); + table->from[i] = be32_to_cpup(&data[data_offset++]); + table->end[i] = be32_to_cpup(&data[data_offset++]); + table->target_clk_idx[i] = be32_to_cpup(&data[data_offset++]); + LCD_DEBUG("%dst : %d %d %d %d %d\n", + i, table->rat[i], table->band[i], table->from[i], + table->end[i], table->target_clk_idx[i]); + } + + LCD_INFO("%s tab_size (%d)\n", keystring, table->tab_size); + + return 0; + +error: + LCD_ERR("Allocation Fail\n"); + kfree(table->rat); + kfree(table->band); + kfree(table->from); + kfree(table->end); + kfree(table->target_clk_idx); + + return -ENOMEM; +} + +int ss_parse_candella_mapping_table(struct device_node *np, + void *tbl, char *keystring) +{ + struct candela_map_table *table = (struct candela_map_table *) tbl; + const __be32 *data; + int len = 0, i = 0, data_offset = 0; + int col_size = 0; + + data = of_get_property(np, keystring, &len); + if (!data) { + LCD_DEBUG("%d, Unable to read table %s ", __LINE__, keystring); + return -EINVAL; + } else + LCD_ERR("Success to read table %s\n", keystring); + + col_size = 4; + + if ((len % col_size) != 0) { + LCD_ERR("%d, Incorrect table entries for %s , len : %d", + __LINE__, keystring, len); + return -EINVAL; + } + + table->tab_size = len / (sizeof(int) * col_size); + + table->cd = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->cd) + goto error; + table->idx = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->idx) + goto error; + table->from = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->from) + goto error; + table->end = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->end) + goto error; + + for (i = 0 ; i < table->tab_size; i++) { + table->idx[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->from[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->end[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->cd[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + } + + table->min_lv = table->from[0]; + table->max_lv = table->end[table->tab_size-1]; + + LCD_INFO("tab_size (%d), hbm min/max lv (%d/%d)\n", table->tab_size, table->min_lv, table->max_lv); + + return 0; + +error: + kfree(table->cd); + kfree(table->idx); + kfree(table->from); + kfree(table->end); + + return -ENOMEM; +} + +int ss_parse_pac_candella_mapping_table(struct device_node *np, + void *tbl, char *keystring) +{ + struct candela_map_table *table = (struct candela_map_table *) tbl; + const __be32 *data; + int len = 0, i = 0, data_offset = 0; + int col_size = 0; + + data = of_get_property(np, keystring, &len); + if (!data) { + LCD_DEBUG("%d, Unable to read table %s ", __LINE__, keystring); + return -EINVAL; + } else + LCD_ERR("Success to read table %s\n", keystring); + + col_size = 6; + + if ((len % col_size) != 0) { + LCD_ERR("%d, Incorrect table entries for %s , len : %d", + __LINE__, keystring, len); + return -EINVAL; + } + + table->tab_size = len / (sizeof(int) * col_size); + + table->interpolation_cd = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->interpolation_cd) + return -ENOMEM; + table->cd = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->cd) + goto error; + table->scaled_idx = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->scaled_idx) + goto error; + table->idx = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->idx) + goto error; + table->from = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->from) + goto error; + table->end = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->end) + goto error; +/* + table->auto_level = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->auto_level) + goto error; +*/ + for (i = 0 ; i < table->tab_size; i++) { + table->scaled_idx[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->idx[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->from[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->end[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->cd[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + table->interpolation_cd[i] = be32_to_cpup(&data[data_offset++]); /* field => */ + } + + table->min_lv = table->from[0]; + table->max_lv = table->end[table->tab_size-1]; + + LCD_INFO("tab_size (%d), hbm min/max lv (%d/%d)\n", table->tab_size, table->min_lv, table->max_lv); + + return 0; + +error: + kfree(table->interpolation_cd); + kfree(table->cd); + kfree(table->scaled_idx); + kfree(table->idx); + kfree(table->from); + kfree(table->end); + + return -ENOMEM; +} + +int ss_parse_hbm_candella_mapping_table(struct device_node *np, + void *tbl, char *keystring) +{ + struct candela_map_table *table = (struct candela_map_table *) tbl; + const __be32 *data; + int data_offset = 0, len = 0, i = 0; + + data = of_get_property(np, keystring, &len); + if (!data) { + LCD_DEBUG("%d, Unable to read table %s ", __LINE__, keystring); + return -EINVAL; + } else + LCD_ERR("Success to read table %s\n", keystring); + + if ((len % 4) != 0) { + LCD_ERR("%d, Incorrect table entries for %s", + __LINE__, keystring); + return -EINVAL; + } + + table->tab_size = len / (sizeof(int)*5); + + table->interpolation_cd = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->interpolation_cd) + return -ENOMEM; + table->cd = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->cd) + goto error; + table->scaled_idx = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->scaled_idx) + goto error; + table->idx = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->idx) + goto error; + table->from = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->from) + goto error; + table->end = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->end) + goto error; + table->auto_level = kzalloc((sizeof(int) * table->tab_size), GFP_KERNEL); + if (!table->auto_level) + goto error; + + for (i = 0 ; i < table->tab_size; i++) { + table->idx[i] = be32_to_cpup(&data[data_offset++]); /* 1st field => */ + table->from[i] = be32_to_cpup(&data[data_offset++]); /* 2nd field => */ + table->end[i] = be32_to_cpup(&data[data_offset++]); /* 3rd field => */ + table->cd[i] = be32_to_cpup(&data[data_offset++]); /* 4th field => */ + table->auto_level[i] = be32_to_cpup(&data[data_offset++]); /* 5th field => */ + } + + table->min_lv = table->from[0]; + table->max_lv = table->end[table->tab_size-1]; + + LCD_INFO("tab_size (%d), hbm min/max lv (%d/%d)\n", table->tab_size, table->min_lv, table->max_lv); + + return 0; +error: + kfree(table->cd); + kfree(table->idx); + kfree(table->from); + kfree(table->end); + kfree(table->auto_level); + + return -ENOMEM; +} + +int ss_parse_panel_table(struct device_node *np, + void *tbl, char *keystring) +{ + struct cmd_map *table = (struct cmd_map *) tbl; + const __be32 *data; + int data_offset, len = 0, i = 0; + + data = of_get_property(np, keystring, &len); + if (!data) { + LCD_DEBUG("%d, Unable to read table %s\n", __LINE__, keystring); + return -EINVAL; + } else + LCD_ERR("Success to read table %s\n", keystring); + + if ((len % 2) != 0) { + LCD_ERR("%d, Incorrect table entries for %s", + __LINE__, keystring); + return -EINVAL; + } + + table->size = len / (sizeof(int)*2); + table->bl_level = kzalloc((sizeof(int) * table->size), GFP_KERNEL); + if (!table->bl_level) + return -ENOMEM; + + table->cmd_idx = kzalloc((sizeof(int) * table->size), GFP_KERNEL); + if (!table->cmd_idx) + goto error; + + data_offset = 0; + for (i = 0 ; i < table->size; i++) { + table->bl_level[i] = be32_to_cpup(&data[data_offset++]); + table->cmd_idx[i] = be32_to_cpup(&data[data_offset++]); + } + + return 0; +error: + kfree(table->cmd_idx); + + return -ENOMEM; +} + + +int ss_send_cmd(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type type) +{ + struct dsi_panel *panel = GET_DSI_PANEL(vdd); + struct dsi_display *dsi_display = GET_DSI_DISPLAY(vdd); + struct dsi_panel_cmd_set *set; + bool is_vdd_locked = false; + int rc = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("vdd is null\n"); + return -ENODEV; + } + + if (!ss_panel_attach_get(vdd)) { + LCD_ERR("ss_panel_attach_get(%d) : %d\n", + vdd->ndx, ss_panel_attach_get(vdd)); + return -EAGAIN; + } + + /* Skip to lock vdd_lock for commands that has exclusive_pass token + * to prevent deadlock between vdd_lock and ex_tx_waitq. + * cmd_lock guarantees exclusive tx cmds without vdd_lock. + */ + set = ss_get_cmds(vdd, type); + /* + if (SS_IS_CMDS_NULL(set)) { + LCD_ERR("No cmds for type(%d) \n", type); + return -EINVAL; + } +*/ + if (likely(!vdd->exclusive_tx.enable || !set->exclusive_pass)) { + mutex_lock(&vdd->vdd_lock); + is_vdd_locked = true; + } + + if (ss_is_panel_off(vdd) || (vdd->panel_func.samsung_lvds_write_reg)) { + LCD_ERR("skip to tx cmd (%d), panel off (%d)\n", + type, ss_is_panel_off(vdd)); + goto error; + } + + if (vdd->debug_data->print_cmds) + LCD_INFO("Send cmd(%d): %s ++\n", type, ss_get_cmd_name(type)); + else + LCD_DEBUG("Send cmd(%d): %s ++\n", type, ss_get_cmd_name(type)); + + /* case 03063186: + * To prevent deadlock between phandle->phandle_lock and panel->panel_lock, + * dsi_display_clk_ctrl() should be called without locking panel_lock. + */ + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_ALL_CLKS, DSI_CLK_ON); + if (rc) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } + + dsi_panel_tx_cmd_set(panel, type); + + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_ALL_CLKS, DSI_CLK_OFF); + if (rc) { + pr_err("[%s] failed to disable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } + + if (vdd->debug_data->print_cmds) + LCD_INFO("Send cmd(%d): %s --\n", type, ss_get_cmd_name(type)); +error: + if (likely(is_vdd_locked)) + mutex_unlock(&vdd->vdd_lock); + + return rc; +} + +#include +#define CPUFREQ_MAX 100 +#define CPUFREQ_ALT_HIGH 70 +struct cluster_cpu_info { + int cpu_man; /* cpu number that manages the policy */ + int max_freq; +}; + +static void __ss_set_cpufreq_cpu(struct samsung_display_driver_data *vdd, + int enable, int cpu, int max_percent) +{ + struct cpufreq_policy *policy; + static unsigned int org_min[NR_CPUS + 1]; + static unsigned int org_max[NR_CPUS + 1]; + + policy = cpufreq_cpu_get(cpu); + if (policy == NULL) { + LCD_ERR("policy is null..\n"); + return; + } + + if (enable) { + org_min[cpu] = policy->min; + org_max[cpu] = policy->max; + + /* max_freq's unit is kHz, and it should not cause overflow. + * After applying user_policy, cpufreq driver will find closest + * frequency in freq_table, and set the frequency. + */ + policy->user_policy.min = policy->user_policy.max = + policy->cpuinfo.max_freq * max_percent / 100; + } else { + policy->user_policy.min = org_min[cpu]; + policy->user_policy.max = org_max[cpu]; + } + + cpufreq_update_policy(cpu); + cpufreq_cpu_put(policy); + + LCD_INFO("en=%d, cpu=%d, per=%d, min=%d, org=(%d-%d)\n", + enable, cpu, max_percent, policy->user_policy.min, + org_min[cpu], org_max[cpu]); +} + +void ss_set_max_cpufreq(struct samsung_display_driver_data *vdd, + int enable, enum mdss_cpufreq_cluster cluster) +{ + struct cpufreq_policy *policy; + int cpu; + struct cluster_cpu_info cluster_info[NR_CPUS]; + int cnt_cluster; + int big_cpu_man; + int little_cpu_man; + + LCD_INFO("en=%d, cluster=%d\n", enable, cluster); + get_online_cpus(); + + /* find clusters */ + cnt_cluster = 0; + for_each_online_cpu(cpu) { + policy = cpufreq_cpu_get(cpu); + /* In big-little octa core system, + * cpu0 ~ cpu3 has same policy (policy0) and + * policy->cpu would be 0. (cpu0 manages policy0) + * cpu4 ~ cpu7 has same policy (policy4) and + * policy->cpu would be 4. (cpu4 manages policy0) + * In this case, cnt_cluster would be two, and + * cluster_info[0].cpu_man = 0, cluster_info[1].cpu_man = 4. + */ + if (policy && policy->cpu == cpu) { + cluster_info[cnt_cluster].cpu_man = cpu; + cluster_info[cnt_cluster].max_freq = + policy->cpuinfo.max_freq; + cnt_cluster++; + } + cpufreq_cpu_put(policy); + } + + if (!cnt_cluster || cnt_cluster > 2) { + LCD_ERR("cluster count err (%d)\n", cnt_cluster); + goto end; + } + + if (cnt_cluster == 1) { + /* single policy (none big-little case) */ + LCD_INFO("cluster count is one...\n"); + + if (cluster == CPUFREQ_CLUSTER_ALL) + /* set all cores' freq to max*/ + __ss_set_cpufreq_cpu(vdd, enable, + cluster_info[0].cpu_man, CPUFREQ_MAX); + else + /* set all cores' freq to max * 70 percent */ + __ss_set_cpufreq_cpu(vdd, enable, + cluster_info[0].cpu_man, CPUFREQ_ALT_HIGH); + goto end; + } + + /* big-little */ + if (cluster_info[0].max_freq > cluster_info[1].max_freq) { + big_cpu_man = cluster_info[0].cpu_man; + little_cpu_man = cluster_info[1].cpu_man; + } else { + big_cpu_man = cluster_info[1].cpu_man; + little_cpu_man = cluster_info[0].cpu_man; + } + + if (cluster == CPUFREQ_CLUSTER_BIG) { + __ss_set_cpufreq_cpu(vdd, enable, big_cpu_man, + CPUFREQ_MAX); + } else if (cluster == CPUFREQ_CLUSTER_LITTLE) { + __ss_set_cpufreq_cpu(vdd, enable, little_cpu_man, + CPUFREQ_MAX); + } else if (cluster == CPUFREQ_CLUSTER_ALL) { + __ss_set_cpufreq_cpu(vdd, enable, big_cpu_man, + CPUFREQ_MAX); + __ss_set_cpufreq_cpu(vdd, enable, little_cpu_man, + CPUFREQ_MAX); + } + +end: + put_online_cpus(); +} + +/* ss_set_exclusive_packet(): + * This shuold be called in ex_tx_lock lock. + */ +void ss_set_exclusive_tx_packet( + struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type cmd, int pass) +{ + struct dsi_panel_cmd_set *pcmds = NULL; + + pcmds = ss_get_cmds(vdd, cmd); + pcmds->exclusive_pass = pass; +} + +/* + * ex_tx_lock should be locked before panel_lock if there is a dsi_panel_tx_cmd_set after panel_lock is locked. + * because of there is a "ex_tx_lock -> panel lock" routine at the sequence of ss_gct_write. + * So we need to add ex_tx_lock to protect all "dsi_panel_tx_cmd_set after panel_lock". + */ + +void ss_set_exclusive_tx_lock_from_qct(struct samsung_display_driver_data *vdd, bool lock) +{ + if (lock) + mutex_lock(&vdd->exclusive_tx.ex_tx_lock); + else + mutex_unlock(&vdd->exclusive_tx.ex_tx_lock); +} + +#define _ALIGN_DOWN(addr, size) ((addr)&(~((size)-1))) +//#define MAX_DSI_FIFO_SIZE_HS_MODE (480) /* AP limitation */ +/* TODO: find MAX_DSI_FIFO_SIZE_HS_MODE for sdm845... if set to 480, cause fail of tx mipi */ +#define MAX_DSI_FIFO_SIZE_HS_MODE (50) /* AP limitation */ +#define MAX_SIZE_IMG_PAYLOAD _ALIGN_DOWN((MAX_DSI_FIFO_SIZE_HS_MODE - 1), 12) + +#define GRAM_WR_MEM_START 0x2c +#define GRAM_WR_MEM_CONT 0x3c +#define SIDERAM_WR_MEM_START 0x4c +#define SIDERAM_WR_MEM_CONT 0x5c + +int ss_write_ddi_ram(struct samsung_display_driver_data *vdd, + int target, u8 *buffer, int len) +{ + u8 hdr_start; + u8 hdr_continue; + struct dsi_panel_cmd_set *set; + struct dsi_cmd_desc *cmds; + u8 *tx_buf; + u8 *org_tx_buf; + int loop; + int remain; + int psize; + + LCD_INFO("+\n"); + + set = ss_get_cmds(vdd, TX_DDI_RAM_IMG_DATA); + + if (target == MIPI_TX_TYPE_GRAM) { + hdr_start = GRAM_WR_MEM_START; + hdr_continue = GRAM_WR_MEM_CONT; + } else if (target == MIPI_TX_TYPE_SIDERAM) { + hdr_start = SIDERAM_WR_MEM_START; + hdr_continue = SIDERAM_WR_MEM_CONT; + } else { + LCD_ERR("invalid target (%d)\n", target); + return -EINVAL; + } + + set->state = DSI_CMD_SET_STATE_HS; + set->count = DIV_ROUND_UP(len, MAX_SIZE_IMG_PAYLOAD); + + cmds = vzalloc(sizeof(struct dsi_cmd_desc) * set->count); + tx_buf = vzalloc((MAX_SIZE_IMG_PAYLOAD + 1) * set->count); + org_tx_buf = tx_buf; + set->cmds = cmds; + + + LCD_INFO("len=%d, cmds count=%d\n", len, set->count); + + /* split and fill image data to TX_DDI_RAM_IMG_DATA packet */ + loop = 0; + remain = len; + while (remain > 0) { + cmds[loop].msg.type = MIPI_DSI_GENERIC_LONG_WRITE; + cmds[loop].last_command = 1; + cmds[loop].msg.tx_buf = tx_buf; + + if (loop == 0) + *tx_buf = hdr_start; + else + *tx_buf = hdr_continue; + tx_buf++; + + if (remain > MAX_SIZE_IMG_PAYLOAD) + psize = MAX_SIZE_IMG_PAYLOAD; + else + psize = remain; + + memcpy(tx_buf, buffer, psize); + cmds[loop].msg.tx_len = psize + 1; + + tx_buf += psize; + buffer += psize; + remain -= psize; + loop++; + } + + LCD_INFO("start tx gram\n"); + ss_send_cmd(vdd, TX_DDI_RAM_IMG_DATA); + LCD_INFO("fin tx gram\n"); + + vfree(org_tx_buf); + vfree(cmds); + LCD_INFO("-\n"); + + return 0; +} + +/** + * controller have 4 registers can hold 16 bytes of rxed data + * dcs packet: 4 bytes header + payload + 2 bytes crc + * 1st read: 4 bytes header + 10 bytes payload + 2 crc + * 2nd read: 14 bytes payload + 2 crc + * 3rd read: 14 bytes payload + 2 crc + */ +#define MAX_LEN_RX_BUF 200 +#define RX_SIZE_LIMIT 10 +int ss_panel_data_read_no_gpara(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type type, u8 *buffer, int level_key) +{ + struct dsi_panel_cmd_set *set; + char show_buffer[MAX_LEN_RX_BUF] = {0,}; + char *temp_buffer; + int orig_rx_len = 0; + int orig_offset = 0; + int pos = 0; + int i; + + if (!ss_panel_attach_get(vdd)) { + LCD_ERR("ss_panel_attach_get(%d) : %d\n", + vdd->ndx, ss_panel_attach_get(vdd)); + return -EPERM; + } + + if (ss_is_panel_off(vdd)) { + LCD_ERR("skip to rx cmd (%d), panel off (%d)\n", + type, ss_is_panel_off(vdd)); + return -EPERM; + } + + /* To block read operation at esd-recovery */ + if (vdd->panel_dead) { + LCD_ERR("esd recovery, skip %s\n", ss_get_cmd_name(type)); + return -EPERM; + } + + /* samsung mipi rx cmd feature supports only one command */ + set = ss_get_cmds(vdd, type); + if (!ss_is_read_cmd(type) || set->count != 1) { + LCD_ERR("invalid set(%d): %s, count (%d)\n", + type, ss_get_cmd_name(type), set->count); + return -EINVAL; + } + + /* enable level key */ + if (level_key & LEVEL1_KEY) + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + if (level_key & LEVEL2_KEY) + ss_send_cmd(vdd, TX_LEVEL2_KEY_ENABLE); + + orig_rx_len = set->cmds[0].msg.rx_len; + orig_offset = set->read_startoffset; + + /* reset rx len/offset */ + set->cmds[0].msg.rx_len += orig_offset; + set->read_startoffset = 0; + + temp_buffer = kmalloc(set->cmds[0].msg.rx_len, GFP_KERNEL); + if (temp_buffer == NULL) { + LCD_ERR("fail to alloc temp_buffer..\n"); + return 0; + } + + set->cmds[0].msg.rx_buf = temp_buffer; + + LCD_DEBUG("orig_rx_len (%d) , orig_offset (%d) \n", orig_rx_len, orig_offset); +// LCD_DEBUG("new_rx_len (%d) , new_offset (%d) \n", set->cmds[0].msg.rx_len, set->read_startoffset); + + /* RX */ + ss_send_cmd(vdd, type); + + /* oopy to buffer from original offset */ + memcpy(buffer, temp_buffer + orig_offset, orig_rx_len); + + /* show buffer */ + for (i = 0; i < orig_rx_len; i++) + pos += snprintf(show_buffer + pos, sizeof(show_buffer) - pos, "%02x ", + buffer[i]); + + /* restore rx len/offset */ + set->cmds[0].msg.rx_len = orig_rx_len; + set->read_startoffset = orig_offset; + + /* disable level key */ + if (level_key & LEVEL1_KEY) + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + if (level_key & LEVEL2_KEY) + ss_send_cmd(vdd, TX_LEVEL2_KEY_DISABLE); + + if (type != RX_FLASH_GAMMA) + LCD_INFO("[%d]%s, addr: 0x%x, off: %d, len: %d, buf: %s\n", + type, ss_get_cmd_name(type), + set->cmds[0].msg.tx_buf[0], orig_offset, orig_rx_len, + show_buffer); + + if (temp_buffer) + kfree(temp_buffer); + + return 0; +} + +int ss_panel_data_read_gpara(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type type, u8 *buffer, int level_key) +{ + struct dsi_panel_cmd_set *set; + struct dsi_panel_cmd_set *read_pos_cmd; + static u8 rx_buffer[MAX_LEN_RX_BUF] = {0,}; + char show_buffer[MAX_LEN_RX_BUF] = {0,}; + char temp_buffer[MAX_LEN_RX_BUF] = {0,}; + char rx_addr = 0; + int orig_rx_len = 0; + int new_rx_len = 0; + int orig_offset = 0; + int new_offset = 0; + int loop_limit = 0; + int show_cnt = 0; + int pos = 0; + int i, j; + + if (!ss_panel_attach_get(vdd)) { + LCD_ERR("ss_panel_attach_get(%d) : %d\n", + vdd->ndx, ss_panel_attach_get(vdd)); + return -EPERM; + } + + if (ss_is_panel_off(vdd)) { + LCD_ERR("skip to rx cmd (%d), panel off (%d)\n", + type, ss_is_panel_off(vdd)); + return -EPERM; + } + + /* To block read operation at esd-recovery */ + if (vdd->panel_dead) { + LCD_ERR("esd recovery, skip %s\n", ss_get_cmd_name(type)); + return -EPERM; + } + + /* samsung mipi rx cmd feature supports only one command */ + set = ss_get_cmds(vdd, type); + if (!ss_is_read_cmd(type) || set->count != 1) { + LCD_ERR("invalid set(%d): %s, count (%d)\n", + type, ss_get_cmd_name(type), set->count); + return -EINVAL; + } + + /* enable level key */ + if (level_key & LEVEL1_KEY) + ss_send_cmd(vdd, TX_LEVEL1_KEY_ENABLE); + if (level_key & LEVEL2_KEY) + ss_send_cmd(vdd, TX_LEVEL2_KEY_ENABLE); + + set->cmds[0].msg.rx_buf = rx_buffer; + + read_pos_cmd = ss_get_cmds(vdd, TX_REG_READ_POS); + if (SS_IS_CMDS_NULL(read_pos_cmd)) { + LCD_ERR("No cmds for TX_REG_READ_POS.. \n"); + return -EINVAL; + } + + rx_addr = set->cmds[0].msg.tx_buf[0]; + orig_rx_len = set->cmds[0].msg.rx_len; + orig_offset = new_offset = set->read_startoffset; + + loop_limit = (orig_rx_len + RX_SIZE_LIMIT - 1) / RX_SIZE_LIMIT; + + /* set pointing gpara */ + if (read_pos_cmd->cmds->msg.tx_len == 3) + read_pos_cmd->cmds->msg.tx_buf[2] = rx_addr; + + LCD_DEBUG("orig_rx_len (%d) , orig_offset (%d) loop_limit (%d)\n", orig_rx_len, orig_offset, loop_limit); + + for (i = 0; i < loop_limit; i++) { + /* gPara */ + read_pos_cmd->cmds->msg.tx_buf[1] = new_offset; + new_rx_len = ((orig_rx_len - new_offset + orig_offset) < RX_SIZE_LIMIT) ? + (orig_rx_len - new_offset + orig_offset) : RX_SIZE_LIMIT; + + LCD_DEBUG("new_offset (%d) new_rx_len (%d) \n", new_offset, new_rx_len); + + ss_send_cmd(vdd, TX_REG_READ_POS); + + set->cmds[0].msg.rx_len = new_rx_len; + + /* RX */ + ss_send_cmd(vdd, type); + + /* oopy to buffer */ + memcpy(&buffer[show_cnt], rx_buffer, new_rx_len); + + /* snprint */ + memcpy(temp_buffer, set->cmds[0].msg.rx_buf, new_rx_len); + for (j = 0; j < new_rx_len; j++, show_cnt++) { + pos += snprintf(show_buffer + pos, sizeof(show_buffer) - pos, "%02x ", + temp_buffer[j]); + } + + /* increase gPara offset */ + new_offset += new_rx_len; + + if (new_offset - orig_offset >= orig_rx_len) + break; + } + + /* restore rx len */ + set->cmds[0].msg.rx_len = orig_rx_len; + + /* disable level key */ + if (level_key & LEVEL1_KEY) + ss_send_cmd(vdd, TX_LEVEL1_KEY_DISABLE); + if (level_key & LEVEL2_KEY) + ss_send_cmd(vdd, TX_LEVEL2_KEY_DISABLE); + + if (type != RX_FLASH_GAMMA) + LCD_INFO("[%d] %s, addr: 0x%x, off: %d, len: %d, buf: %s\n", + type, ss_get_cmd_name(type), + set->cmds[0].msg.tx_buf[0], orig_offset, orig_rx_len, + show_buffer); + + return 0; +} + +int ss_panel_data_read(struct samsung_display_driver_data *vdd, + enum dsi_cmd_set_type type, u8 *buffer, int level_key) +{ + int ret; + + if (vdd->gpara) + ret = ss_panel_data_read_gpara(vdd, type, buffer, level_key); + else + ret = ss_panel_data_read_no_gpara(vdd, type, buffer, level_key); + + return ret; +} + +struct STM_REG_OSSET stm_offset_list[] = { + {.name = "stm_ctrl_en=", .offset = offsetof(struct STM_CMD, STM_CTRL_EN)}, + {.name = "stm_max_opt=", .offset = offsetof(struct STM_CMD, STM_MAX_OPT)}, + {.name = "stm_default_opt=", .offset = offsetof(struct STM_CMD, STM_DEFAULT_OPT)}, + {.name = "stm_dim_step=", .offset = offsetof(struct STM_CMD, STM_DIM_STEP)}, + {.name = "stm_frame_period=", .offset = offsetof(struct STM_CMD, STM_FRAME_PERIOD)}, + {.name = "stm_min_sect=", .offset = offsetof(struct STM_CMD, STM_MIN_SECT)}, + {.name = "stm_pixel_period=", .offset = offsetof(struct STM_CMD, STM_PIXEL_PERIOD)}, + {.name = "stm_line_period=", .offset = offsetof(struct STM_CMD, STM_LINE_PERIOD)}, + {.name = "stm_min_move=", .offset = offsetof(struct STM_CMD, STM_MIN_MOVE)}, + {.name = "stm_m_thres=", .offset = offsetof(struct STM_CMD, STM_M_THRES)}, + {.name = "stm_v_thres=", .offset = offsetof(struct STM_CMD, STM_V_THRES)}, +}; + +int ss_stm_set_cmd_offset(struct STM_CMD *cmd, char* p) +{ + int list_size = ARRAY_SIZE(stm_offset_list); + const char *name; + int i, val; + int *offset; + + for (i = 0; i < list_size; i++) { + name = stm_offset_list[i].name; + if (!strncmp(name, p, strlen(name))) { + sscanf(p + strlen(name), "%d", &val); + offset = (int *)((void*)cmd + stm_offset_list[i].offset); + *offset = val; + return 0; + } + } + + return -1; +} + +void print_stm_cmd(struct STM_CMD cmd) +{ + LCD_INFO("CTRL_EN(%d) MAX_OPT(%d) DEFAULT_OPT(%d) DIM_STEP(%d)\n", + cmd.STM_CTRL_EN, cmd.STM_MAX_OPT, cmd.STM_DEFAULT_OPT, cmd.STM_DIM_STEP); + LCD_INFO("FRAME_PERIOD(%d) MIN_SECT(%d) PIXEL_PERIOD(%d) LINE_PEROID(%d) \n", + cmd.STM_FRAME_PERIOD, cmd.STM_MIN_SECT, cmd.STM_PIXEL_PERIOD, cmd.STM_LINE_PERIOD); + LCD_INFO("MIN_MOVE(%d) M_THRES(%d) V_THRES(%d)\n", + cmd.STM_MIN_MOVE, cmd.STM_M_THRES, cmd.STM_V_THRES); +} + +/** + * ss_get_stm_orig_cmd - get original stm cmd from panel dtsi. + */ +int ss_get_stm_orig_cmd(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *pcmds = NULL; + struct STM_CMD *cmd = &vdd->stm.orig_cmd; + u8 *cmd_pload; + int cmd_len; + char buf[256]; + int i, len = 0; + + pcmds = ss_get_cmds(vdd, TX_STM_ENABLE); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_STM_ENABLE..\n"); + return -EINVAL; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + cmd_len = pcmds->cmds[1].msg.tx_len; + + cmd->STM_CTRL_EN = (cmd_pload[1] & 0x01) ? 1 : 0; + cmd->STM_MAX_OPT = (cmd_pload[3] & 0xF0) >> 4; + + cmd->STM_DEFAULT_OPT = (cmd_pload[3] & 0x0F); + + cmd->STM_DIM_STEP = (cmd_pload[4] & 0x0F); + + cmd->STM_FRAME_PERIOD = (cmd_pload[5] & 0xF0) >> 4; + cmd->STM_MIN_SECT = (cmd_pload[5] & 0x0F); + + cmd->STM_PIXEL_PERIOD = (cmd_pload[6] & 0xF0) >> 4; + cmd->STM_LINE_PERIOD = (cmd_pload[6] & 0x0F); + + cmd->STM_MIN_MOVE = cmd_pload[7]; + cmd->STM_M_THRES = cmd_pload[8]; + cmd->STM_V_THRES = cmd_pload[9]; + + /* init current cmd with origianl cmd */ + memcpy(&vdd->stm.cur_cmd, cmd, sizeof(struct STM_CMD)); + + print_stm_cmd(vdd->stm.cur_cmd); + + for (i = 0; i < cmd_len; i++) + len += snprintf(buf + len, sizeof(buf) - len, + "%02x ", cmd_pload[i]); + LCD_ERR("cmd[%d] : %s\n", cmd_len, buf); + + return 1; +} + +/** + * ss_stm_set_cmd - set stm mipi cmd using STM_CMD para. + * + * stm_cmd : new stm cmd to update. + */ +void ss_stm_set_cmd(struct samsung_display_driver_data *vdd, struct STM_CMD *cmd) +{ + struct dsi_panel_cmd_set *pcmds = NULL; + int cmd_len; + u8 *cmd_pload; + char buf[256]; + int i, len = 0; + + pcmds = ss_get_cmds(vdd, TX_STM_ENABLE); + if (SS_IS_CMDS_NULL(pcmds)) { + LCD_ERR("No cmds for TX_STM_ENABLE..\n"); + return; + } + + cmd_pload = pcmds->cmds[1].msg.tx_buf; + cmd_len = pcmds->cmds[1].msg.tx_len; + + cmd_pload[1] = cmd->STM_CTRL_EN ? 0x01 : 0x00; + + cmd_pload[3] = ((cmd->STM_MAX_OPT & 0x0F) << 4); + cmd_pload[3] |= (cmd->STM_DEFAULT_OPT & 0x0F); + + cmd_pload[4] |= (cmd->STM_DIM_STEP & 0x0F); + + cmd_pload[5] = ((cmd->STM_FRAME_PERIOD & 0x0F) << 4); + cmd_pload[5] |= (cmd->STM_MIN_SECT & 0x0F); + + cmd_pload[6] = ((cmd->STM_PIXEL_PERIOD & 0x0F) << 4); + cmd_pload[6] |= (cmd->STM_LINE_PERIOD & 0x0F); + + cmd_pload[7] = cmd->STM_MIN_MOVE & 0xFF; + cmd_pload[8] = cmd->STM_M_THRES & 0xFF; + cmd_pload[9] = cmd->STM_V_THRES & 0xFF; + + for (i = 0; i < cmd_len; i++) + len += snprintf(buf + len, sizeof(buf) - len, + "%02x ", cmd_pload[i]); + LCD_ERR("cmd[%d] : %s\n", cmd_len, buf); + + /* reset current stm cmds */ + memcpy(&vdd->stm.cur_cmd, cmd, sizeof(struct STM_CMD)); + + return; +} + +int ss_panel_on_pre(struct samsung_display_driver_data *vdd) +{ + int rddpm, rddsm, errfg, dsierror; + + /* At this time, it already enabled SDE clock/power and display power. + * It is possible to send mipi comamnd to display. + * To send mipi command, like mdnie setting or brightness setting, + * change panel_state: PANEL_PWR_OFF -> PANEL_PWR_ON_READY, here. + */ + vdd->panel_state = PANEL_PWR_ON_READY; + + vdd->display_status_dsi.disp_on_pre = 1; + + if (vdd->other_line_panel_work_cnt) + vdd->other_line_panel_work_cnt = 0; /*stop open otherline dat file*/ + +#if !defined(CONFIG_SEC_FACTORY) + /* LCD ID read every wake_up time incase of factory binary */ + if (vdd->dtsi_data.tft_common_support) + return false; +#endif + + if (!ss_panel_attach_get(vdd)) { + LCD_ERR("ss_panel_attach_get NG\n"); + return false; + } + + LCD_INFO("+\n"); + + if (unlikely(!vdd->esd_recovery.esd_recovery_init)) + ss_event_esd_recovery_init(vdd, 0, NULL); + + if (unlikely(vdd->is_factory_mode) && + vdd->dtsi_data.samsung_support_factory_panel_swap) { + /* LCD ID read every wake_up time incase of factory binary */ + vdd->manufacture_id_dsi = PBA_ID; + + /* Factory Panel Swap*/ + vdd->manufacture_date_loaded_dsi = 0; + vdd->ddi_id_loaded_dsi = 0; + vdd->cell_id_loaded_dsi = 0; + vdd->octa_id_loaded_dsi = 0; + vdd->hbm_loaded_dsi = 0; + vdd->mdnie_loaded_dsi = 0; + vdd->smart_dimming_loaded_dsi = 0; + vdd->smart_dimming_hmt_loaded_dsi = 0; + vdd->table_interpolation_loaded = 0; + } + + if (vdd->manufacture_id_dsi == PBA_ID) { + u8 recv_buf[3]; + + /* + * At this time, panel revision it not selected. + * So last index(SUPPORT_PANEL_REVISION-1) used. + */ + vdd->panel_revision = SUPPORT_PANEL_REVISION-1; + + /* + * Some panel needs to update register at init time to read ID & MTP + * Such as, dual-dsi-control or sleep-out so on. + */ + if ((ss_get_cmds(vdd, RX_MANUFACTURE_ID)->count)) { + ss_panel_data_read(vdd, RX_MANUFACTURE_ID, + recv_buf, LEVEL1_KEY); + } else { + LCD_INFO("manufacture_read_pre_tx_cmds\n"); + ss_send_cmd(vdd, TX_MANUFACTURE_ID_READ_PRE); + + ss_panel_data_read(vdd, RX_MANUFACTURE_ID0, + recv_buf, LEVEL1_KEY); + ss_panel_data_read(vdd, RX_MANUFACTURE_ID1, + recv_buf + 1, LEVEL1_KEY); + ss_panel_data_read(vdd, RX_MANUFACTURE_ID2, + recv_buf + 2, LEVEL1_KEY); + } + + vdd->manufacture_id_dsi = + (recv_buf[0] << 16) | (recv_buf[1] << 8) | recv_buf[2]; + LCD_INFO("manufacture id: 0x%x\n", vdd->manufacture_id_dsi); + + /* Panel revision selection */ + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_revision)) + LCD_ERR("no panel_revision_selection_error fucntion\n"); + else + vdd->panel_func.samsung_panel_revision(vdd); + + LCD_INFO("Panel_Revision = %c %d\n", vdd->panel_revision + 'A', vdd->panel_revision); + } + + /* Read panel status to check panel is ok from bootloader */ + if (!vdd->read_panel_status_from_lk) { + rddpm = ss_read_rddpm(vdd); + rddsm = ss_read_rddsm(vdd); + errfg = ss_read_errfg(vdd); + dsierror = ss_read_dsierr(vdd); + ss_read_pps_data(vdd); + + SS_XLOG(rddpm, rddsm, errfg, dsierror); + LCD_INFO("panel dbg: %x %x %x %x\n", rddpm, rddsm, errfg, dsierror); + + vdd->read_panel_status_from_lk = 1; + } + + /* Manufacture date */ + if (!vdd->manufacture_date_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_manufacture_date_read)) + LCD_ERR("no samsung_manufacture_date_read function\n"); + else + vdd->manufacture_date_loaded_dsi = vdd->panel_func.samsung_manufacture_date_read(vdd); + } + + /* DDI ID */ + if (!vdd->ddi_id_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_ddi_id_read)) + LCD_ERR("no samsung_ddi_id_read function\n"); + else + vdd->ddi_id_loaded_dsi = vdd->panel_func.samsung_ddi_id_read(vdd); + } + + /* Panel Unique OCTA ID */ + if (!vdd->octa_id_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_octa_id_read)) + LCD_ERR("no samsung_octa_id_read function\n"); + else + vdd->octa_id_loaded_dsi = vdd->panel_func.samsung_octa_id_read(vdd); + } + + /* ELVSS read */ + if (!vdd->elvss_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_elvss_read)) + LCD_ERR("no samsung_elvss_read function\n"); + else + vdd->elvss_loaded_dsi = vdd->panel_func.samsung_elvss_read(vdd); + } + + /* IRC read */ + if (!vdd->irc_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_irc_read)) + LCD_ERR("no samsung_irc_read function\n"); + else + vdd->irc_loaded_dsi = vdd->panel_func.samsung_irc_read(vdd); + } + + /* HBM */ + if (!vdd->hbm_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_read)) + LCD_ERR("no samsung_hbm_read function\n"); + else + vdd->hbm_loaded_dsi = vdd->panel_func.samsung_hbm_read(vdd); + } + + /* MDNIE X,Y */ + if (!vdd->mdnie_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_mdnie_read)) + LCD_ERR("no samsung_mdnie_read function\n"); + else + vdd->mdnie_loaded_dsi = vdd->panel_func.samsung_mdnie_read(vdd); + } + + /* Panel Unique Cell ID */ + if (!vdd->cell_id_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_cell_id_read)) + LCD_ERR("no samsung_cell_id_read function\n"); + else + vdd->cell_id_loaded_dsi = vdd->panel_func.samsung_cell_id_read(vdd); + } + + /* Smart dimming*/ + if (!vdd->smart_dimming_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_smart_dimming_init)) + LCD_ERR("no samsung_smart_dimming_init function\n"); + else + vdd->smart_dimming_loaded_dsi = vdd->panel_func.samsung_smart_dimming_init(vdd); + } + + /* Smart dimming for hmt */ + if (vdd->dtsi_data.hmt_enabled) { + if (!vdd->smart_dimming_hmt_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_smart_dimming_hmt_init)) + LCD_ERR("no samsung_smart_dimming_hmt_init function\n"); + else + vdd->smart_dimming_hmt_loaded_dsi = vdd->panel_func.samsung_smart_dimming_hmt_init(vdd); + } + } + + /* self display */ + if (!vdd->self_display_loaded_dsi) { + if (IS_ERR_OR_NULL(vdd->self_disp.data_init)) + LCD_ERR("no self display data init function\n"); + else + vdd->self_display_loaded_dsi = vdd->self_disp.data_init(vdd); + } + + /* copr */ + if (!vdd->copr_load_init_cmd && vdd->copr.copr_on) { + vdd->copr_load_init_cmd = ss_get_copr_orig_cmd(vdd); + } + + /* stm */ + if (!vdd->stm_load_init_cmd) { + vdd->stm_load_init_cmd = ss_get_stm_orig_cmd(vdd); + } + + if (!vdd->table_interpolation_loaded) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_interpolation_init)) { + table_br_func(vdd); + vdd->table_interpolation_loaded = vdd->panel_func.samsung_interpolation_init(vdd, TABLE_INTERPOLATION); + } + } + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_on_pre)) + vdd->panel_func.samsung_panel_on_pre(vdd); + + LCD_INFO("-\n"); + + return true; +} + +int ss_panel_on_post(struct samsung_display_driver_data *vdd) +{ + if (!ss_panel_attach_get(vdd)) { + LCD_ERR("ss_panel_attach_get NG\n"); + return false; + } + + LCD_INFO("+\n"); + +/* TODO: enable debug file and activate below... + ss_read_self_diag(vdd); +*/ + + if (vdd->support_cabc && !vdd->br.auto_level) + ss_cabc_update(vdd); + else if (vdd->ss_panel_tft_outdoormode_update && vdd->br.auto_level) + vdd->ss_panel_tft_outdoormode_update(vdd); + else if (vdd->support_cabc && vdd->br.auto_level) + ss_tft_autobrightness_cabc_update(vdd); + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_on_post)) + vdd->panel_func.samsung_panel_on_post(vdd); + +#if 0 // ccb is set by mdnie cmd. + if ((vdd->panel_func.color_weakness_ccb_on_off) && vdd->color_weakness_mode) + vdd->panel_func.color_weakness_ccb_on_off(vdd, vdd->color_weakness_mode); +#endif + + if (vdd->support_hall_ic) { + /* + * Brightenss cmds sent by samsung_display_hall_ic_status() at panel switching operation + */ + if (ss_is_bl_dcs(vdd) && + (vdd->hall_ic_mode_change_trigger == false)) + ss_brightness_dcs(vdd, vdd->br.bl_level); + } else { + if (ss_is_bl_dcs(vdd)) { + struct backlight_device *bd = GET_SDE_BACKLIGHT_DEVICE(vdd); + + /* In case of backlight update in panel off, + * dsi_display_set_backlight() returns error + * without updating vdd->br.bl_level. + * Update bl_level from bd->props.brightness. + */ + if (bd && vdd->br.bl_level != bd->props.brightness) { + LCD_INFO("update bl_level: %d -> %d\n", + vdd->br.bl_level, bd->props.brightness); + vdd->br.bl_level = bd->props.brightness; + } + + ss_brightness_dcs(vdd, vdd->br.bl_level); + } + } + + if (vdd->mdnie.support_mdnie) { + vdd->mdnie.lcd_on_notifiy = true; + update_dsi_tcon_mdnie_register(vdd); + if (vdd->mdnie.support_trans_dimming) + vdd->mdnie.disable_trans_dimming = false; + } + + /* + * Update Cover Control Status every Normal sleep & wakeup + * Do not update Cover_control at this point in case of AOD. + * Because, below update is done before entering AOD. + */ + if (vdd->panel_func.samsung_cover_control && vdd->cover_control + && !ss_is_panel_lpm(vdd)) + vdd->panel_func.samsung_cover_control(vdd); + + /* Work around: For folder model, the status bar resizes itself and old UI appears in sub-panel + * before premium watch or screen lock is on, so it needs to skip old UI. + */ + if (!vdd->lcd_flip_not_refresh && + vdd->support_hall_ic && + vdd->hall_ic_mode_change_trigger && + vdd->lcd_flip_delay_ms) { + schedule_delayed_work(&vdd->delay_disp_on_work, msecs_to_jiffies(vdd->lcd_flip_delay_ms)); + } else + vdd->display_status_dsi.wait_disp_on = true; + + vdd->display_status_dsi.wait_actual_disp_on = true; + vdd->display_status_dsi.aod_delay = true; + + if (ss_is_esd_check_enabled(vdd)) { + if (vdd->esd_recovery.esd_irq_enable) + vdd->esd_recovery.esd_irq_enable(true, true, (void *)vdd); + } + + if (vdd->dyn_mipi_clk.is_support) + ss_send_cmd(vdd, TX_FFC); + + if (vdd->copr.copr_on) + ss_send_cmd(vdd, TX_COPR_ENABLE); + + if (vdd->stm.stm_on) + ss_send_cmd(vdd, TX_STM_ENABLE); + + vdd->panel_state = PANEL_PWR_ON; + + LCD_INFO("-\n"); + + return true; +} + +int ss_panel_off_pre(struct samsung_display_driver_data *vdd) +{ + int rddpm, rddsm, errfg, dsierror; + int ret = 0; + + LCD_INFO("+\n"); + rddpm = ss_read_rddpm(vdd); + rddsm = ss_read_rddsm(vdd); + errfg = ss_read_errfg(vdd); + dsierror = ss_read_dsierr(vdd); + ss_read_pps_data(vdd); + SS_XLOG(rddpm, rddsm, errfg, dsierror); + LCD_INFO("panel dbg: %x %x %x %x\n", rddpm, rddsm, errfg, dsierror); + + if (ss_is_esd_check_enabled(vdd)) { + vdd->esd_recovery.is_wakeup_source = false; + if (vdd->esd_recovery.esd_irq_enable) + vdd->esd_recovery.esd_irq_enable(false, true, (void *)vdd); + vdd->esd_recovery.is_enabled_esd_recovery = false; + } + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_off_pre)) + vdd->panel_func.samsung_panel_off_pre(vdd); + + mutex_lock(&vdd->vdd_lock); + vdd->panel_state = PANEL_PWR_OFF; + mutex_unlock(&vdd->vdd_lock); + vdd->panel_dead = false; + + LCD_INFO("-\n"); + + return ret; +} + +/* + * Do not call ss_send_cmd() or ss_panel_data_read() here. + * Any MIPI Tx/Rx can not be alowed in here. + */ +int ss_panel_off_post(struct samsung_display_driver_data *vdd) +{ + int ret = 0; + + LCD_INFO("+\n"); + + if (vdd->mdnie.support_trans_dimming) + vdd->mdnie.disable_trans_dimming = true; + + if (vdd->support_hall_ic && vdd->lcd_flip_delay_ms) + cancel_delayed_work(&vdd->delay_disp_on_work); + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_off_post)) + vdd->panel_func.samsung_panel_off_post(vdd); + + /* gradual acl on/off - not used */ +// vdd->gradual_pre_acl_on = GRADUAL_ACL_UNSTABLE; + + /* Reset Self Display Status */ + 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; + + LCD_INFO("-\n"); + SS_XLOG(SS_XLOG_FINISH); + + return ret; +} + +/************************************************************* +* +* TFT BACKLIGHT GPIO FUNCTION BELOW. +* +**************************************************************/ +int ss_backlight_tft_request_gpios(struct samsung_display_driver_data *vdd) +{ + int rc = 0, i; + /* + * gpio_name[] named as gpio_name + num(recomend as 0) + * because of the num will increase depend on number of gpio + */ + char gpio_name[17] = "disp_bcklt_gpio0"; + static u8 gpio_request_status = -EINVAL; + + if (!gpio_request_status) + goto end; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)vdd); + goto end; + } + + for (i = 0; i < MAX_BACKLIGHT_TFT_GPIO; i++) { + if (gpio_is_valid(vdd->dtsi_data.backlight_tft_gpio[i])) { + rc = gpio_request(vdd->dtsi_data.backlight_tft_gpio[i], + gpio_name); + if (rc) { + LCD_ERR("request %s failed, rc=%d\n", gpio_name, rc); + goto tft_backlight_gpio_err; + } + } + } + + gpio_request_status = rc; +end: + return rc; +tft_backlight_gpio_err: + if (i) { + do { + if (gpio_is_valid(vdd->dtsi_data.backlight_tft_gpio[i])) + gpio_free(vdd->dtsi_data.backlight_tft_gpio[i--]); + LCD_ERR("i = %d\n", i); + } while (i > 0); + } + + return rc; +} + +int ss_backlight_tft_gpio_config(struct samsung_display_driver_data *vdd, int enable) +{ + int ret = 0, i = 0, add_value = 1; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)vdd); + goto end; + } + + LCD_INFO("++ enable(%d) ndx(%d)\n", enable, ss_get_display_ndx(vdd)); + + if (ss_backlight_tft_request_gpios(vdd)) { + LCD_ERR("fail to request tft backlight gpios"); + goto end; + } + + LCD_DEBUG("%s tft backlight gpios\n", enable ? "enable" : "disable"); + + /* + * The order of backlight_tft_gpio enable/disable + * 1. Enable : backlight_tft_gpio[0], [1], ... [MAX_BACKLIGHT_TFT_GPIO - 1] + * 2. Disable : backlight_tft_gpio[MAX_BACKLIGHT_TFT_GPIO - 1], ... [1], [0] + */ + if (!enable) { + add_value = -1; + i = MAX_BACKLIGHT_TFT_GPIO - 1; + } + + do { + if (gpio_is_valid(vdd->dtsi_data.backlight_tft_gpio[i])) { + gpio_set_value(vdd->dtsi_data.backlight_tft_gpio[i], enable); + LCD_DEBUG("set backlight tft gpio[%d] to %s\n", + vdd->dtsi_data.backlight_tft_gpio[i], + enable ? "high" : "low"); + usleep_range(500, 500); + } + } while (((i += add_value) < MAX_BACKLIGHT_TFT_GPIO) && (i >= 0)); + +end: + LCD_INFO("--\n"); + return ret; +} + +/************************************************************* +* +* ESD RECOVERY RELATED FUNCTION BELOW. +* +**************************************************************/ + +#if 0 +static int ss_esd_check_status(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("lcd esd - check_ststus\n"); + return 0; +} +static int ss_esd_read_status(struct mdss_dsi_ctrl_pdata *ctrl) +{ + LCD_INFO("lcd esd - check_read_status\n"); + // esd status must return 0 to go status_dead(blank->unblnk) in ss_check_dsi_ctrl_status. + return 0; +} +#endif + +/* + * esd_irq_enable() - Enable or disable esd irq. + * + * @enable : flag for enable or disabled + * @nosync : flag for disable irq with nosync + * @data : point ot struct ss_panel_info + */ +#define IRQS_PENDING 0x00000200 +#define istate core_internal_state__do_not_mess_with_it +static void esd_irq_enable(bool enable, bool nosync, void *data) +{ + /* The irq will enabled when do the request_threaded_irq() */ + static bool is_enabled = true; + static bool is_wakeup_source; + int gpio; + int irq[MAX_ESD_GPIO] = {0,}; + unsigned long flags; + struct samsung_display_driver_data *vdd = + (struct samsung_display_driver_data *)data; + struct irq_desc *desc = NULL; + u8 i = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd); + return; + } + + for (i = 0; i < vdd->esd_recovery.num_of_gpio; i++) { + gpio = vdd->esd_recovery.esd_gpio[i]; + + if (!gpio_is_valid(gpio)) { + LCD_ERR("Invalid ESD_GPIO : %d\n", gpio); + continue; + } + irq[i] = gpio_to_irq(gpio); + } + + spin_lock_irqsave(&vdd->esd_recovery.irq_lock, flags); + + if (enable == is_enabled) { + LCD_INFO("ESD irq already %s\n", + enable ? "enabled" : "disabled"); + goto config_wakeup_source; + } + + + for (i = 0; i < vdd->esd_recovery.num_of_gpio; i++) { + gpio = vdd->esd_recovery.esd_gpio[i]; + desc = irq_to_desc(irq[i]); + if (enable) { + /* clear esd irq triggered while it was disabled. */ + if (desc->istate & IRQS_PENDING) { + LCD_INFO("clear esd irq pending status\n"); + desc->istate &= ~IRQS_PENDING; + } + } + + if (!gpio_is_valid(gpio)) { + LCD_ERR("Invalid ESD_GPIO : %d\n", gpio); + continue; + } + + if (enable) { + is_enabled = true; + enable_irq(irq[i]); + } else { + if (nosync) + disable_irq_nosync(irq[i]); + else + disable_irq(irq[i]); + is_enabled = false; + } + } + + /* TODO: Disable log if the esd function stable */ + LCD_DEBUG("ESD irq %s with %s\n", + enable ? "enabled" : "disabled", + nosync ? "nosync" : "sync"); + +config_wakeup_source: + if (vdd->esd_recovery.is_wakeup_source == is_wakeup_source) { + LCD_DEBUG("[ESD] IRQs are already irq_wake %s\n", + is_wakeup_source ? "enabled" : "disabled"); + goto end; + } + + for (i = 0; i < vdd->esd_recovery.num_of_gpio; i++) { + gpio = vdd->esd_recovery.esd_gpio[i]; + + if (!gpio_is_valid(gpio)) { + LCD_ERR("Invalid ESD_GPIO : %d\n", gpio); + continue; + } + + is_wakeup_source = + vdd->esd_recovery.is_wakeup_source; + + if (is_wakeup_source) + enable_irq_wake(irq[i]); + else + disable_irq_wake(irq[i]); + } + + LCD_DEBUG("[ESD] IRQs are set to irq_wake %s\n", + is_wakeup_source ? "enabled" : "disabled"); + +end: + spin_unlock_irqrestore(&vdd->esd_recovery.irq_lock, flags); +} + +static irqreturn_t esd_irq_handler(int irq, void *handle) +{ + struct samsung_display_driver_data *vdd = + (struct samsung_display_driver_data *) handle; + struct sde_connector *conn = GET_SDE_CONNECTOR(vdd); + + if (!vdd->esd_recovery.is_enabled_esd_recovery) { + LCD_ERR("esd recovery is not enabled yet"); + goto end; + } + LCD_INFO("++\n"); + + esd_irq_enable(false, true, (void *)vdd); + + vdd->panel_lpm.esd_recovery = true; + + schedule_work(&conn->status_work.work); + + LCD_INFO("--\n"); + +end: + return IRQ_HANDLED; +} + +// refer to dsi_display_res_init() and dsi_panel_get(&display->pdev->dev, display->panel_of); +static void ss_panel_parse_dt_esd(struct device_node *np, + struct samsung_display_driver_data *vdd) +{ + int rc = 0; + const char *data; + char esd_irq_gpio[] = "samsung,esd-irq-gpio1"; + char esd_irqflag[] = "qcom,mdss-dsi-panel-status-irq-trigger1"; + struct esd_recovery *esd = NULL; + struct dsi_panel *panel = NULL; + struct drm_panel_esd_config *esd_config = NULL; + u8 i = 0; + + panel = (struct dsi_panel *)vdd->msm_private; + esd_config = &panel->esd_config; + + esd = &vdd->esd_recovery; + esd->num_of_gpio = 0; + + esd_config->esd_enabled = of_property_read_bool(np, + "qcom,esd-check-enabled"); + + if (!esd_config->esd_enabled) + goto end; + + for (i = 0; i < MAX_ESD_GPIO; i++) { + esd_irq_gpio[strlen(esd_irq_gpio) - 1] = '1' + i; + esd->esd_gpio[esd->num_of_gpio] = of_get_named_gpio(np, + esd_irq_gpio, 0); + + if (gpio_is_valid(esd->esd_gpio[esd->num_of_gpio])) { + LCD_INFO("[ESD] gpio : %d, irq : %d\n", + esd->esd_gpio[esd->num_of_gpio], + gpio_to_irq(esd->esd_gpio[esd->num_of_gpio])); + esd->num_of_gpio++; + } + } + + rc = of_property_read_string(np, "qcom,mdss-dsi-panel-status-check-mode", &data); + if (!rc) { + if (!strcmp(data, "irq_check")) + esd_config->status_mode = ESD_MODE_PANEL_IRQ; + else + LCD_ERR("No valid panel-status-check-mode string\n"); + } + + for (i = 0; i < esd->num_of_gpio; i++) { + esd_irqflag[strlen(esd_irqflag) - 1] = '1' + i; + rc = of_property_read_string(np, esd_irqflag, &data); + if (!rc) { + esd->irqflags[i] = + IRQF_ONESHOT | IRQF_NO_SUSPEND; + + if (!strcmp(data, "rising")) + esd->irqflags[i] |= IRQF_TRIGGER_RISING; + else if (!strcmp(data, "falling")) + esd->irqflags[i] |= IRQF_TRIGGER_FALLING; + else if (!strcmp(data, "high")) + esd->irqflags[i] |= IRQF_TRIGGER_HIGH; + else if (!strcmp(data, "low")) + esd->irqflags[i] |= IRQF_TRIGGER_LOW; + } + } + +end: + LCD_INFO("samsung esd %s mode (%d)\n", + esd_config->esd_enabled ? "enabled" : "disabled", + esd_config->status_mode); + return; +} + +static void ss_event_esd_recovery_init( + struct samsung_display_driver_data *vdd, int event, void *arg) +{ + // TODO: implement after qcomm esd bsp appllied... + int ret; + u8 i; + int gpio, irqflags; + struct esd_recovery *esd = NULL; + struct dsi_panel *panel = NULL; + struct drm_panel_esd_config *esd_config = NULL; + + panel = (struct dsi_panel *)vdd->msm_private; + esd_config = &panel->esd_config; + + esd = &vdd->esd_recovery; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd); + return; + } + + LCD_INFO("++\n"); + + if (unlikely(!esd->esd_recovery_init)) { + esd->esd_recovery_init = true; + esd->esd_irq_enable = esd_irq_enable; + if (esd_config->status_mode == ESD_MODE_PANEL_IRQ) { + for (i = 0; i < esd->num_of_gpio; i++) { + /* Set gpio num and irqflags */ + gpio = esd->esd_gpio[i]; + irqflags = esd->irqflags[i]; + if (!gpio_is_valid(gpio)) { + LCD_ERR("[ESD] Invalid GPIO : %d\n", gpio); + continue; + } + + gpio_request(gpio, "esd_recovery"); + ret = request_threaded_irq( + gpio_to_irq(gpio), + NULL, + esd_irq_handler, + irqflags, + "esd_recovery", + (void *)vdd); + if (ret) + LCD_ERR("Failed to request_irq, ret=%d\n", + ret); + else + LCD_INFO("request esd irq !!\n"); + } + esd_irq_enable(false, true, (void *)vdd); + } + } + + LCD_INFO("--\n"); +} + +static void ss_panel_recovery(struct samsung_display_driver_data *vdd) +{ + struct sde_connector *conn = GET_SDE_CONNECTOR(vdd); + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd); + return; + } + + if (!vdd->esd_recovery.is_enabled_esd_recovery) { + LCD_ERR("esd recovery is not enabled yet"); + return; + } + LCD_INFO("Panel Recovery, Trial Count = %d\n", vdd->panel_recovery_cnt++); + SS_XLOG(vdd->panel_recovery_cnt); + inc_dpui_u32_field(DPUI_KEY_QCT_RCV_CNT, 1); + + esd_irq_enable(false, true, (void *)vdd); + vdd->panel_lpm.esd_recovery = true; + schedule_work(&conn->status_work.work); + + LCD_INFO("Panel Recovery --\n"); + +} + +/************************************************************* +* +* OSC TE FITTING RELATED FUNCTION BELOW. +* +**************************************************************/ +static void ss_event_osc_te_fitting( + struct samsung_display_driver_data *vdd, int event, void *arg) +{ + struct osc_te_fitting_info *te_info = NULL; + int ret, i, lut_count; + unsigned int disp_te_gpio; + + te_info = &vdd->te_fitting_info; + + if (IS_ERR_OR_NULL(te_info)) { + LCD_ERR("Invalid te data : 0x%zx\n", + (size_t)te_info); + return; + } + + if (ss_is_seamless_mode(vdd)) { + LCD_ERR("cont splash enabled\n"); + return; + } + + if (!ss_is_vsync_enabled(vdd)) { + LCD_DEBUG("vsync handler does not enabled yet\n"); + return; + } + + te_info->status |= TE_FITTING_DONE; + + LCD_DEBUG("++\n"); + + disp_te_gpio = ss_get_te_gpio(vdd); + + if (!(te_info->status & TE_FITTING_REQUEST_IRQ)) { + te_info->status |= TE_FITTING_REQUEST_IRQ; + + + ret = request_threaded_irq( + gpio_to_irq(disp_te_gpio), + samsung_te_check_handler, + NULL, + IRQF_TRIGGER_FALLING, + "VSYNC_GPIO", + (void *) vdd); + if (ret) + LCD_ERR("Failed to request_irq, ret=%d\n", + ret); + else + disable_irq(gpio_to_irq(disp_te_gpio)); + te_info->te_time = + kzalloc(sizeof(long long) * te_info->sampling_rate, GFP_KERNEL); + INIT_WORK(&te_info->work, samsung_te_check_done_work); + } + + for (lut_count = 0; lut_count < OSC_TE_FITTING_LUT_MAX; lut_count++) { + init_completion(&te_info->te_check_comp); + te_info->status |= TE_CHECK_ENABLE; + te_info->te_duration = 0; + + LCD_DEBUG("osc_te_fitting _irq : %d\n", + gpio_to_irq(disp_te_gpio)); + + enable_irq(gpio_to_irq(disp_te_gpio)); + ret = wait_for_completion_timeout( + &te_info->te_check_comp, 1000); + + if (ret <= 0) + LCD_ERR("timeout\n"); + + for (i = 0; i < te_info->sampling_rate; i++) { + te_info->te_duration += + (i != 0 ? (te_info->te_time[i] - te_info->te_time[i-1]) : 0); + LCD_DEBUG("vsync time : %lld, sum : %lld\n", + te_info->te_time[i], te_info->te_duration); + } + do_div(te_info->te_duration, te_info->sampling_rate - 1); + LCD_INFO("ave vsync time : %lld\n", + te_info->te_duration); + te_info->status &= ~TE_CHECK_ENABLE; + + if (vdd->panel_func.samsung_osc_te_fitting) + ret = vdd->panel_func.samsung_osc_te_fitting(vdd); + + if (!ret) + ss_send_cmd(vdd, TX_OSC_TE_FITTING); + else + break; + } + LCD_DEBUG("--\n"); +} + +static void samsung_te_check_done_work(struct work_struct *work) +{ + struct osc_te_fitting_info *te_info = NULL; + + te_info = container_of(work, struct osc_te_fitting_info, work); + + if (IS_ERR_OR_NULL(te_info)) { + LCD_ERR("Invalid TE tuning data\n"); + return; + } + + complete_all(&te_info->te_check_comp); +} + +static irqreturn_t samsung_te_check_handler(int irq, void *handle) +{ + struct samsung_display_driver_data *vdd = NULL; + struct osc_te_fitting_info *te_info = NULL; + static bool skip_first_te = true; + static u8 count; + + if (skip_first_te) { + skip_first_te = false; + goto end; + } + + if (IS_ERR_OR_NULL(handle)) { + LCD_ERR("handle is null\n"); + goto end; + } + + te_info = &vdd->te_fitting_info; + + + if (!(te_info->status & TE_CHECK_ENABLE)) + goto end; + + if (count < te_info->sampling_rate) { + te_info->te_time[count++] = + ktime_to_us(ktime_get()); + } else { + disable_irq_nosync(gpio_to_irq(ss_get_te_gpio(vdd))); + schedule_work(&te_info->work); + skip_first_te = true; + count = 0; + } + +end: + return IRQ_HANDLED; +} + +/************************************************************* +* +* LDI FPS RELATED FUNCTION BELOW. +* +**************************************************************/ +int ldi_fps(struct samsung_display_driver_data *vdd, unsigned int input_fps) +{ + int rc = 0; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd); + return 0; + } + + LCD_INFO("input_fps = %d\n", input_fps); + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_change_ldi_fps)) + rc = vdd->panel_func.samsung_change_ldi_fps(vdd, input_fps); + else { + LCD_ERR("samsung_change_ldi_fps function is NULL\n"); + return 0; + } + + if (rc) + ss_send_cmd(vdd, TX_LDI_FPS_CHANGE); + + return rc; +} +EXPORT_SYMBOL(ldi_fps); + +int ss_set_backlight(struct samsung_display_driver_data *vdd, u32 bl_lvl) +{ + struct dsi_panel *panel = NULL; + int ret = 0; + + if (IS_ERR_OR_NULL(vdd)) { + ret = -EINVAL; + goto end; + } + + panel = GET_DSI_PANEL(vdd); + if (IS_ERR_OR_NULL(panel)) { + LCD_ERR("panel is NULL\n"); + ret = -EINVAL; + goto end; + } + + ret = dsi_panel_set_backlight(panel, bl_lvl); + +end: + return ret; +} + +/************************************************************* +* +* HMT RELATED FUNCTION BELOW. +* +**************************************************************/ +int hmt_bright_update(struct samsung_display_driver_data *vdd) +{ + if (vdd->hmt_stat.hmt_on) { + ss_brightness_dcs_hmt(vdd, vdd->hmt_stat.hmt_bl_level); + } else { + ss_brightness_dcs(vdd, vdd->br.bl_level); + LCD_INFO("hmt off state!\n"); + } + + return 0; +} + +int hmt_enable(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("[HMT] HMT %s\n", vdd->hmt_stat.hmt_on ? "ON" : "OFF"); + + if (vdd->hmt_stat.hmt_on) { + ss_send_cmd(vdd, TX_HMT_ENABLE); + } else { + ss_send_cmd(vdd, TX_HMT_DISABLE); + } + + return 0; +} + +int hmt_reverse_update(struct samsung_display_driver_data *vdd, int enable) +{ + LCD_INFO("[HMT] HMT %s\n", enable ? "REVERSE" : "FORWARD"); + + if (enable) + ss_send_cmd(vdd, TX_HMT_REVERSE); + else + ss_send_cmd(vdd, TX_HMT_FORWARD); + + return 0; +} + +static void parse_dt_data(struct device_node *np, void *data, size_t size, + char *cmd_string, char panel_rev, + int (*fnc)(struct device_node *, void *, char *)) +{ + char string[PARSE_STRING]; + int ret = 0; + + /* Generate string to parsing from DT */ + snprintf(string, PARSE_STRING, "%s%c", cmd_string, 'A' + panel_rev); + + ret = fnc(np, data, string); + + /* If there is no dtsi data for panel rev B ~ T, + * use valid previous panel rev dtsi data. + * TODO: Instead of copying all data from previous panel rev, + * copy only the pointer... + */ + if (ret && (panel_rev > 0)) + memcpy(data, (u8 *) data - size, size); +} + +static void ss_panel_parse_dt_bright_tables(struct device_node *np, + struct samsung_display_driver_data *vdd) +{ + struct samsung_display_dtsi_data *dtsi_data = &vdd->dtsi_data; + int panel_rev; + + for (panel_rev = 0; panel_rev < SUPPORT_PANEL_REVISION; panel_rev++) { + parse_dt_data(np, &dtsi_data->vint_map_table[panel_rev], + sizeof(struct cmd_map), + "samsung,vint_map_table_rev", panel_rev, + ss_parse_panel_table); /* VINT TABLE */ + + parse_dt_data(np, &dtsi_data->candela_map_table[NORMAL][panel_rev], + sizeof(struct candela_map_table), + "samsung,candela_map_table_rev", panel_rev, + ss_parse_candella_mapping_table); + + parse_dt_data(np, &dtsi_data->candela_map_table[AOD][panel_rev], + sizeof(struct candela_map_table), + "samsung,aod_candela_map_table_rev", panel_rev, + ss_parse_candella_mapping_table); + + parse_dt_data(np, &dtsi_data->candela_map_table[HBM][panel_rev], + sizeof(struct candela_map_table), + "samsung,hbm_candela_map_table_rev", panel_rev, + ss_parse_hbm_candella_mapping_table); + + parse_dt_data(np, &dtsi_data->candela_map_table[PAC_NORMAL][panel_rev], + sizeof(struct candela_map_table), + "samsung,pac_candela_map_table_rev", panel_rev, + ss_parse_pac_candella_mapping_table); + + parse_dt_data(np, &dtsi_data->candela_map_table[PAC_HBM][panel_rev], + sizeof(struct candela_map_table), + "samsung,pac_hbm_candela_map_table_rev", panel_rev, + ss_parse_hbm_candella_mapping_table); + + /* ACL */ + parse_dt_data(np, &dtsi_data->acl_map_table[panel_rev], + sizeof(struct cmd_map), + "samsung,acl_map_table_rev", panel_rev, + ss_parse_panel_table); /* ACL TABLE */ + + parse_dt_data(np, &dtsi_data->elvss_map_table[panel_rev], + sizeof(struct cmd_map), + "samsung,elvss_map_table_rev", panel_rev, + ss_parse_panel_table); /* ELVSS TABLE */ + + parse_dt_data(np, &dtsi_data->aid_map_table[panel_rev], + sizeof(struct cmd_map), + "samsung,aid_map_table_rev", panel_rev, + ss_parse_panel_table); /* AID TABLE */ + + parse_dt_data(np, &dtsi_data->smart_acl_elvss_map_table[panel_rev], + sizeof(struct cmd_map), + "samsung,smart_acl_elvss_map_table_rev", panel_rev, + ss_parse_panel_table); /* TABLE */ + + parse_dt_data(np, &dtsi_data->hmt_reverse_aid_map_table[panel_rev], + sizeof(struct cmd_map), + "samsung,hmt_reverse_aid_map_table_rev", panel_rev, + ss_parse_panel_table); /* TABLE */ + + parse_dt_data(np, &dtsi_data->candela_map_table[HMT][panel_rev], + sizeof(struct candela_map_table), + "samsung,hmt_candela_map_table_rev", panel_rev, + ss_parse_candella_mapping_table); +#if 0 + parse_dt_data(np, &dtsi_data->scaled_level_map_table[panel_rev], + sizeof(struct candela_map_table), + "samsung,scaled_level_map_table_rev", panel_rev, + ss_parse_candella_mapping_table); +#endif + } +} + +static void ss_panel_pbaboot_config(struct device_node *np, + struct samsung_display_driver_data *vdd) +{ + // TODO: set appropriate value to drm display members... not pinfo... +#if 0 + struct ss_panel_info *pinfo = NULL; + struct samsung_display_driver_data *vdd = NULL; + bool need_to_force_vidoe_mode = false; + + pinfo = &ctrl->panel_data.panel_info; + vdd = check_valid_ctrl(ctrl); + + if (vdd->support_hall_ic) { + /* check the lcd id for DISPLAY_1 and DISPLAY_2 */ + if (!ss_panel_attached(DISPLAY_1) && !ss_panel_attached(DISPLAY_2)) + need_to_force_vidoe_mode = true; + } else { + /* check the lcd id for DISPLAY_1 */ + if (!ss_panel_attached(DISPLAY_1)) + need_to_force_vidoe_mode = true; + } + + /* Support PBA boot without lcd */ + if (need_to_force_vidoe_mode && + !IS_ERR_OR_NULL(pinfo) && + !IS_ERR_OR_NULL(vdd) && + (pinfo->mipi.mode == DSI_CMD_MODE)) { + LCD_ERR("force VIDEO_MODE : %d\n", vdd->ndx); + pinfo->type = MIPI_VIDEO_PANEL; + pinfo->mipi.mode = DSI_VIDEO_MODE; + pinfo->mipi.traffic_mode = DSI_BURST_MODE; + pinfo->mipi.bllp_power_stop = true; + pinfo->mipi.te_sel = 0; + pinfo->mipi.vsync_enable = 0; + pinfo->mipi.hw_vsync_mode = 0; + pinfo->mipi.force_clk_lane_hs = true; + pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + +#if 0 + pinfo->cont_splash_enabled = false; +#else + ss_set_seamless_mode(vdd); +#endif + pinfo->mipi.lp11_init = false; + + vdd->mdnie.support_mdnie = false; + vdd->mdnie.lcd_on_notifiy = false; + vdd->mdnie.support_trans_dimming = false; + vdd->mdnie.disable_trans_dimming = false; + + if (!IS_ERR_OR_NULL(vdd->panel_func.parsing_otherline_pdata) && ss_panel_attached(DISPLAY_1)) { + vdd->panel_func.parsing_otherline_pdata = NULL; + destroy_workqueue(vdd->other_line_panel_support_workq); + } + + pinfo->esd_check_enabled = false; + ctrl->on_cmds.link_state = DSI_LP_MODE; + ctrl->off_cmds.link_state = DSI_LP_MODE; + +#if 0 + /* To avoid underrun panic*/ + mdd->logd.xlog_enable = 0; +#else +#endif + vdd->dtsi_data.samsung_osc_te_fitting = false; + } +#endif +} + +static void ss_panel_parse_dt(struct samsung_display_driver_data *vdd) +{ + int rc, i; + u32 tmp[2]; + int len; + char backlight_tft_gpio[] = "samsung,panel-backlight-tft-gpio1"; + const char *data; + const __be32 *data_32; + struct device_node *np; + + np = ss_get_panel_of(vdd); + + if (!np) + return; + + /* Set LP11 init flag */ + vdd->dtsi_data.samsung_lp11_init = of_property_read_bool(np, "samsung,dsi-lp11-init"); + LCD_ERR("LP11 init %s\n", + vdd->dtsi_data.samsung_lp11_init ? "enabled" : "disabled"); + + rc = of_property_read_u32(np, "samsung,mdss-power-on-reset-delay-us", tmp); + vdd->dtsi_data.samsung_power_on_reset_delay = (!rc ? tmp[0] : 0); + + rc = of_property_read_u32(np, "samsung,mdss-dsi-off-reset-delay-us", tmp); + vdd->dtsi_data.samsung_dsi_off_reset_delay = (!rc ? tmp[0] : 0); + + /* Set esc clk 128M */ + vdd->dtsi_data.samsung_esc_clk_128M = of_property_read_bool(np, "samsung,esc-clk-128M"); + LCD_ERR("ESC CLK 128M %s\n", + vdd->dtsi_data.samsung_esc_clk_128M ? "enabled" : "disabled"); + + vdd->dtsi_data.panel_lpm_enable = of_property_read_bool(np, "samsung,panel-lpm-enable"); + LCD_ERR("alpm enable %s\n", + vdd->dtsi_data.panel_lpm_enable ? "enabled" : "disabled"); + + /* Set HALL IC */ + vdd->support_hall_ic = of_property_read_bool(np, "samsung,mdss_dsi_hall_ic_enable"); + LCD_ERR("hall_ic %s\n", vdd->support_hall_ic ? "enabled" : "disabled"); + + rc = of_property_read_u32(np, "samsung,mdss_dsi_lcd_flip_delay_ms", tmp); + vdd->lcd_flip_delay_ms = (!rc ? tmp[0] : 0); + LCD_ERR("lcd_flip_delay_ms %d\n", vdd->lcd_flip_delay_ms); + + /*Set OSC TE fitting flag */ + vdd->dtsi_data.samsung_osc_te_fitting = + of_property_read_bool(np, "samsung,osc-te-fitting-enable"); + + if (vdd->dtsi_data.samsung_osc_te_fitting) { + rc = of_property_read_u32_array(np, "samsung,osc-te-fitting-cmd-index", tmp, 2); + if (!rc) { + vdd->dtsi_data.samsung_osc_te_fitting_cmd_index[0] = + tmp[0]; + vdd->dtsi_data.samsung_osc_te_fitting_cmd_index[1] = + tmp[1]; + } + + rc = of_property_read_u32(np, "samsung,osc-te-fitting-sampling-rate", tmp); + + vdd->te_fitting_info.sampling_rate = !rc ? tmp[0] : 2; + + } + + LCD_INFO("OSC TE fitting %s\n", + vdd->dtsi_data.samsung_osc_te_fitting ? "enabled" : "disabled"); + + /* Set HMT flag */ + vdd->dtsi_data.hmt_enabled = of_property_read_bool(np, "samsung,hmt_enabled"); + if (vdd->dtsi_data.hmt_enabled) + vdd->dtsi_data.hmt_enabled = true; + + LCD_INFO("hmt %s\n", + vdd->dtsi_data.hmt_enabled ? "enabled" : "disabled"); + + /* TCON Clk On Support */ + vdd->dtsi_data.samsung_tcon_clk_on_support = + of_property_read_bool(np, "samsung,tcon-clk-on-support"); + LCD_INFO("tcon clk on support: %s\n", + vdd->dtsi_data.samsung_tcon_clk_on_support ? + "enabled" : "disabled"); + + vdd->dtsi_data.samsung_tcon_rdy_gpio = + of_get_named_gpio(np, "samsung,tcon-rdy-gpio", 0); + + if (gpio_is_valid(vdd->dtsi_data.samsung_tcon_rdy_gpio)) + gpio_request(vdd->dtsi_data.samsung_tcon_rdy_gpio, "tcon_rdy"); + + LCD_DEBUG("tcon rdy gpio: %d\n", vdd->dtsi_data.samsung_tcon_rdy_gpio); + + /* Set unicast flags for whole mipi cmds for dual DSI panel + * If display->ctrl_count is 2, it broadcasts. + * To prevent to send mipi cmd thru mipi dsi1, set unicast flag + */ + vdd->dtsi_data.samsung_cmds_unicast = + of_property_read_bool(np, "samsung,cmds-unicast"); + LCD_INFO("mipi cmds unicast flag: %d\n", + vdd->dtsi_data.samsung_cmds_unicast); + + /* anapass DDI power sequence: VDD on -> LP11 -> reset -> wait TCON RDY high -> HS clock. + * To keep above unique sequence, use anapass_power_seq flag.. + */ + vdd->dtsi_data.samsung_anapass_power_seq = + of_property_read_bool(np, "samsung,anapass-power-seq"); + LCD_DEBUG("anapass ddi power seq. flag: %d\n", + vdd->dtsi_data.samsung_anapass_power_seq ); + + /* Set TFT flag */ + vdd->mdnie.tuning_enable_tft = of_property_read_bool(np, + "samsung,mdnie-tuning-enable-tft"); + vdd->dtsi_data.tft_common_support = of_property_read_bool(np, + "samsung,tft-common-support"); + + LCD_INFO("tft_common_support %s\n", + vdd->dtsi_data.tft_common_support ? "enabled" : "disabled"); + + vdd->dtsi_data.tft_module_name = of_get_property(np, + "samsung,tft-module-name", NULL); /* for tft tablet */ + + vdd->dtsi_data.panel_vendor = of_get_property(np, + "samsung,panel-vendor", NULL); + + vdd->dtsi_data.disp_model = of_get_property(np, + "samsung,disp-model", NULL); + + vdd->dtsi_data.backlight_gpio_config = of_property_read_bool(np, + "samsung,backlight-gpio-config"); + + LCD_INFO("backlight_gpio_config %s\n", + vdd->dtsi_data.backlight_gpio_config ? "enabled" : "disabled"); + + /* Factory Panel Swap*/ + vdd->dtsi_data.samsung_support_factory_panel_swap = of_property_read_bool(np, + "samsung,support_factory_panel_swap"); + + /* Set tft backlight gpio */ + for (i = 0; i < MAX_BACKLIGHT_TFT_GPIO; i++) { + backlight_tft_gpio[strlen(backlight_tft_gpio) - 1] = '1' + i; + vdd->dtsi_data.backlight_tft_gpio[i] = + of_get_named_gpio(np, + backlight_tft_gpio, 0); + if (!gpio_is_valid(vdd->dtsi_data.backlight_tft_gpio[i])) + LCD_ERR("%d, backlight_tft_gpio gpio%d not specified\n", + __LINE__, i+1); + else + LCD_ERR("tft gpio num : %d\n", vdd->dtsi_data.backlight_tft_gpio[i]); + } + + /* Set Mdnie lite HBM_CE_TEXT_MDNIE mode used */ + vdd->dtsi_data.hbm_ce_text_mode_support = of_property_read_bool(np, "samsung,hbm_ce_text_mode_support"); + + /* Set Backlight IC discharge time */ + rc = of_property_read_u32(np, "samsung,blic-discharging-delay-us", tmp); + vdd->dtsi_data.blic_discharging_delay_tft = (!rc ? tmp[0] : 6); + + /* Set cabc delay time */ + rc = of_property_read_u32(np, "samsung,cabc-delay-us", tmp); + vdd->dtsi_data.cabc_delay = (!rc ? tmp[0] : 6); + + /* IRC */ + vdd->br.support_irc = of_property_read_bool(np, "samsung,support_irc"); + + /* Gram Checksum Test */ + vdd->gct.is_support = of_property_read_bool(np, "samsung,support_gct"); + LCD_DEBUG("vdd->gct.is_support = %d\n", vdd->gct.is_support); + + /* POC Driver */ + vdd->poc_driver.is_support = of_property_read_bool(np, "samsung,support_poc_driver"); + LCD_INFO("vdd->poc_drvier.is_support = %d\n", vdd->poc_driver.is_support); + rc = of_property_read_u32(np, "samsung,poc_erase_delay_ms", tmp); + vdd->poc_driver.erase_delay_ms = (!rc ? tmp[0] : 0); + rc = of_property_read_u32(np, "samsung,poc_write_delay_us", tmp); + vdd->poc_driver.write_delay_us = (!rc ? tmp[0] : 0); + LCD_INFO("erase_delay_ms(%d) write_delay_us (%d)\n", + vdd->poc_driver.erase_delay_ms, vdd->poc_driver.write_delay_us); + + /* PAC */ + vdd->br.pac = of_property_read_bool(np, "samsung,support_pac"); + LCD_INFO("vdd->br.pac = %d\n", vdd->br.pac); + + /* Global Para */ + vdd->gpara = of_property_read_bool(np, "samsung,support_gpara"); + LCD_INFO("vdd->support_gpara = %d\n", vdd->gpara); + + /* Self Display */ + vdd->self_disp.is_support = of_property_read_bool(np, "samsung,support_self_display"); + LCD_INFO("vdd->self_disp.is_support = %d\n", vdd->self_disp.is_support); + + /* DDI SPI */ + vdd->samsung_support_ddi_spi = of_property_read_bool(np, "samsung,support_ddi_spi"); + if (vdd->samsung_support_ddi_spi) { + rc = of_property_read_u32(np, "samsung,ddi_spi_cs_high_gpio_for_gpara", tmp); + vdd->ddi_spi_cs_high_gpio_for_gpara = (!rc ? tmp[0] : -1); + + LCD_INFO("vdd->ddi_spi_cs_high_gpio_for_gpara = %d\n", vdd->ddi_spi_cs_high_gpio_for_gpara); + } + + /* Elvss_compensation Support */ + vdd->dtsi_data.samsung_elvss_compensation = of_property_read_bool(np, "samsung,elvss_compensation"); + LCD_INFO("vdd->samsung_elvss_compensation: %s\n", vdd->dtsi_data.samsung_elvss_compensation ? "enabled" : "disabled"); + + /* Set elvss_interpolation_temperature */ + data_32 = of_get_property(np, "samsung,elvss_interpolation_temperature", NULL); + + if (data_32) + vdd->br.elvss_interpolation_temperature = (int)(be32_to_cpup(data_32)); + else + vdd->br.elvss_interpolation_temperature = ELVSS_INTERPOLATION_TEMPERATURE; + + /* Set lux value for mdnie HBM */ + data_32 = of_get_property(np, "samsung,enter_hbm_ce_lux", NULL); + if (data_32) + vdd->mdnie.enter_hbm_ce_lux = (int)(be32_to_cpup(data_32)); + else + vdd->mdnie.enter_hbm_ce_lux = ENTER_HBM_CE_LUX; + + /* Power Control for LPM */ + vdd->panel_lpm.lpm_pwr.support_lpm_pwr_ctrl = of_property_read_bool(np, "samsung,lpm-power-control"); + LCD_INFO("lpm_power_control %s\n", vdd->panel_lpm.lpm_pwr.support_lpm_pwr_ctrl ? "enabled" : "disabled"); + + if (vdd->panel_lpm.lpm_pwr.support_lpm_pwr_ctrl) { + rc = of_property_read_string(np, "samsung,lpm-power-control-supply-name", &data); + if (rc) + LCD_ERR("error reading lpm-power name. rc=%d\n", rc); + else + snprintf(vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_name, + ARRAY_SIZE((vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_name)), "%s", data); + + data_32 = of_get_property(np, "samsung,lpm-power-control-supply-min-voltage", NULL); + if (data_32) + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_min_v = (int)(be32_to_cpup(data_32)); + else + LCD_ERR("error reading lpm-power min_voltage\n"); + + data_32 = of_get_property(np, "samsung,lpm-power-control-supply-max-voltage", NULL); + if (data_32) + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_max_v = (int)(be32_to_cpup(data_32)); + else + LCD_ERR("error reading lpm-power max_voltage\n"); + + LCD_INFO("lpm_power_control Supply Name=%s, Min=%d, Max=%d\n", + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_name, vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_min_v, + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_supply_max_v); + + rc = of_property_read_string(np, "samsung,lpm-power-control-elvss-name", &data); + if (rc) + LCD_ERR("error reading lpm-power-elvss name. rc=%d\n", rc); + else + snprintf(vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_name, + ARRAY_SIZE((vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_name)), "%s", data); + + data_32 = of_get_property(np, "samsung,lpm-power-control-elvss-lpm-voltage", NULL); + if (data_32) + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_lpm_v = (int)(be32_to_cpup(data_32)); + else + LCD_ERR("error reading lpm-power-elvss lpm voltage\n"); + + data_32 = of_get_property(np, "samsung,lpm-power-control-elvss-normal-voltage", NULL); + if (data_32) + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_normal_v = (int)(be32_to_cpup(data_32)); + else + LCD_ERR("error reading lpm-power-elvss normal voltage\n"); + + LCD_INFO("lpm_power_control ELVSS Name=%s, lpm=%d, normal=%d\n", + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_name, vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_lpm_v, + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_normal_v); + } + + /* To reduce DISPLAY ON time */ + vdd->dtsi_data.samsung_reduce_display_on_time = of_property_read_bool(np,"samsung,reduce_display_on_time"); + vdd->dtsi_data.samsung_dsi_force_clock_lane_hs = of_property_read_bool(np,"samsung,dsi_force_clock_lane_hs"); + rc = of_property_read_u32(np, "samsung,wait_after_reset_delay", tmp); + vdd->dtsi_data.samsung_wait_after_reset_delay = (!rc ? tmp[0] : 0); + rc = of_property_read_u32(np, "samsung,wait_after_sleep_out_delay", tmp); + vdd->dtsi_data.samsung_wait_after_sleep_out_delay = (!rc ? tmp[0] : 0); + + if (vdd->dtsi_data.samsung_reduce_display_on_time) { + /* + Please Check interrupt gpio is general purpose gpio + 1. pmic gpio or gpio-expender is not permitted. + 2. gpio should have unique interrupt number. + */ + + if(gpio_is_valid(of_get_named_gpio(np, "samsung,home_key_irq_gpio", 0))) { + vdd->dtsi_data.samsung_home_key_irq_num = + gpio_to_irq(of_get_named_gpio(np, "samsung,home_key_irq_gpio", 0)); + LCD_ERR("%s gpio : %d (irq : %d)\n", + np->name, of_get_named_gpio(np, "samsung,home_key_irq_gpio", 0), + vdd->dtsi_data.samsung_home_key_irq_num); + } + + if(gpio_is_valid(of_get_named_gpio(np, "samsung,fingerprint_irq_gpio", 0))) { + vdd->dtsi_data.samsung_finger_print_irq_num = + gpio_to_irq(of_get_named_gpio(np, "samsung,fingerprint_irq_gpio", 0)); + LCD_ERR("%s gpio : %d (irq : %d)\n", + np->name, of_get_named_gpio(np, "samsung,fingerprint_irq_gpio", 0), + vdd->dtsi_data.samsung_finger_print_irq_num); + } + } + + /* Dynamic MIPI Clock */ + vdd->dyn_mipi_clk.is_support = of_property_read_bool(np, "samsung,support_dynamic_mipi_clk"); + LCD_INFO("vdd->dyn_mipi_clk.is_support = %d\n", vdd->dyn_mipi_clk.is_support); + + if (vdd->dyn_mipi_clk.is_support) { + ss_parse_dyn_mipi_clk_sel_table(np, &vdd->dyn_mipi_clk.clk_sel_table, + "samsung,dynamic_mipi_clk_sel_table"); + ss_parse_dyn_mipi_clk_timing_table(np, &vdd->dyn_mipi_clk.clk_timing_table, + "samsung,dynamic_mipi_clk_timing_table"); + vdd->rf_info.notifier.priority = 0; + vdd->rf_info.notifier.notifier_call = ss_rf_info_notify_callback; + // KR : no dev_ril_bridge.h + // register_dev_ril_bridge_event_notifier(&vdd->rf_info.notifier); + } + + ss_panel_parse_dt_bright_tables(np, vdd); +#if KERNEL_VER == 414 + ss_dsi_panel_parse_cmd_sets(vdd->dtsi_data.cmd_sets, GET_DSI_PANEL(vdd)); +#else + ss_dsi_panel_parse_cmd_sets(vdd->dtsi_data.cmd_sets, np); +#endif + + if (vdd->support_hall_ic) { + vdd->hall_ic_notifier_display.priority = 0; /* Tsp is 1, Touch key is 2 */ + vdd->hall_ic_notifier_display.notifier_call = samsung_display_hall_ic_status; +#if defined(CONFIG_FOLDER_HALL) + hall_ic_register_notify(&vdd->hall_ic_notifier_display); +#endif + } + + // LCD SELECT + vdd->select_panel_gpio = of_get_named_gpio(np, + "samsung,mdss_dsi_lcd_sel_gpio", 0); + if (gpio_is_valid(vdd->select_panel_gpio)) + gpio_request(vdd->select_panel_gpio, "lcd_sel_gpio"); + + vdd->select_panel_use_expander_gpio = of_property_read_bool(np, "samsung,mdss_dsi_lcd_sel_use_expander_gpio"); + + /* Panel LPM */ + rc = of_property_read_u32(np, "samsung,lpm-init-delay-ms", tmp); + vdd->dtsi_data.samsung_lpm_init_delay = (!rc ? tmp[0] : 0); + + rc = of_property_read_u32(np, "samsung,delayed-display-on", tmp); + vdd->dtsi_data.samsung_delayed_display_on = (!rc ? tmp[0] : 0); + + ss_panel_parse_dt_esd(np, vdd); + ss_panel_pbaboot_config(np, vdd); + + /* AOR & IRC INTERPOLATION (FLASH or NO FLASH SUPPORT)*/ + data_32 = of_get_property(np, "samsung,hbm_brightness", &len); + vdd->dtsi_data.hbm_brightness_step = (data_32 ? len/sizeof(len) : 0); + data_32 = of_get_property(np, "samsung,normal_brightness", &len); + vdd->dtsi_data.normal_brightness_step = (data_32 ? len/sizeof(len) : 0); + data_32 = of_get_property(np, "samsung,hmd_brightness", &len); + vdd->dtsi_data.hmd_brightness_step = (data_32 ? len/sizeof(len) : 0); + LCD_INFO("gamma_step hbm: %d normal: %d hmt: %d\n", + vdd->dtsi_data.hbm_brightness_step, + vdd->dtsi_data.normal_brightness_step, + vdd->dtsi_data.hmd_brightness_step); + + rc = of_property_read_u32(np, "samsung,gamma_size", tmp); + vdd->dtsi_data.gamma_size = (!rc ? tmp[0] : 34); + rc = of_property_read_u32(np, "samsung,aor_size", tmp); + vdd->dtsi_data.aor_size = (!rc ? tmp[0] : 2); + rc = of_property_read_u32(np, "samsung,vint_size", tmp); + vdd->dtsi_data.vint_size = (!rc ? tmp[0] : 1); + rc = of_property_read_u32(np, "samsung,elvss_size", tmp); + vdd->dtsi_data.elvss_size = (!rc ? tmp[0] : 3); + rc = of_property_read_u32(np, "samsung,irc_size", tmp); + vdd->dtsi_data.irc_size = (!rc ? tmp[0] : 17); + LCD_INFO("flash_gamma_size gamma:%d aor:%d vint:%d elvss:%d irc:%d\n", + vdd->dtsi_data.gamma_size, + vdd->dtsi_data.aor_size, + vdd->dtsi_data.vint_size, + vdd->dtsi_data.elvss_size, + vdd->dtsi_data.irc_size); + + rc = of_property_read_u32(np, "samsung,flash_table_hbm_aor_offset", tmp); + vdd->dtsi_data.flash_table_hbm_aor_offset = (!rc ? tmp[0] : 0x09D4); + rc = of_property_read_u32(np, "samsung,flash_table_hbm_vint_offset", tmp); + vdd->dtsi_data.flash_table_hbm_vint_offset = (!rc ? tmp[0] : 0x0A80); + rc = of_property_read_u32(np, "samsung,flash_table_hbm_elvss_offset", tmp); + vdd->dtsi_data.flash_table_hbm_elvss_offset = (!rc ? tmp[0] : 0x0AD6); + rc = of_property_read_u32(np, "samsung,flash_table_hbm_irc_offset", tmp); + vdd->dtsi_data.flash_table_hbm_irc_offset = (!rc ? tmp[0] : 0x0AD6); + LCD_INFO("flash_table_hbm_addr aor: 0x%x vint:0x%x elvss 0x%x hbm:0x%x\n", + vdd->dtsi_data.flash_table_hbm_aor_offset, + vdd->dtsi_data.flash_table_hbm_vint_offset, + vdd->dtsi_data.flash_table_hbm_elvss_offset, + vdd->dtsi_data.flash_table_hbm_irc_offset); + + rc = of_property_read_u32(np, "samsung,flash_table_normal_gamma_offset", tmp); + vdd->dtsi_data.flash_table_normal_gamma_offset = (!rc ? tmp[0] : 0x0000); + rc = of_property_read_u32(np, "samsung,flash_table_normal_aor_offset", tmp); + vdd->dtsi_data.flash_table_normal_aor_offset = (!rc ? tmp[0] : 0x09EC); + rc = of_property_read_u32(np, "samsung,flash_table_normal_vint_offset", tmp); + vdd->dtsi_data.flash_table_normal_vint_offset = (!rc ? tmp[0] : 0x0A8C); + rc = of_property_read_u32(np, "samsung,flash_table_normal_elvss_offset", tmp); + vdd->dtsi_data.flash_table_normal_elvss_offset = (!rc ? tmp[0] : 0x0AFA); + rc = of_property_read_u32(np, "samsung,flash_table_normal_irc_offset", tmp); + vdd->dtsi_data.flash_table_normal_irc_offset = (!rc ? tmp[0] : 0x0CA4); + LCD_INFO("flash_table__normal_addr gamma:0x%x aor: 0x%x vint:0x%x elvss 0x%x hbm:0x%x\n", + vdd->dtsi_data.flash_table_normal_gamma_offset, + vdd->dtsi_data.flash_table_normal_aor_offset, + vdd->dtsi_data.flash_table_normal_vint_offset, + vdd->dtsi_data.flash_table_normal_elvss_offset, + vdd->dtsi_data.flash_table_normal_irc_offset); + + rc = of_property_read_u32(np, "samsung,flash_table_hmd_gamma_offset", tmp); + vdd->dtsi_data.flash_table_hmd_gamma_offset = (!rc ? tmp[0] : 0x118E); + rc = of_property_read_u32(np, "samsung,flash_table_hmd_aor_offset", tmp); + vdd->dtsi_data.flash_table_hmd_aor_offset = (!rc ? tmp[0] : 0x1678); + LCD_INFO("flash_table_hmt_addr gamma:0x%x aor: 0x%x\n", + vdd->dtsi_data.flash_table_hmd_gamma_offset, + vdd->dtsi_data.flash_table_hmd_aor_offset); + + /* ONLY FLASH GAMMA */ + vdd->dtsi_data.flash_gamma_support = of_property_read_bool(np, "samsung,support_flash_gamma"); + + if (vdd->dtsi_data.flash_gamma_support) { + vdd->flash_br_workqueue = create_singlethread_workqueue("flash_br_workqueue"); + INIT_DELAYED_WORK(&vdd->flash_br_work, flash_br_work_func); + + data_32 = of_get_property(np, "samsung,flash_gamma_data_read_addr", &len); + /* 3byte address cover 15Mbyte */ + vdd->dtsi_data.flash_gamma_data_read_addr_len = (data_32 ? len/sizeof(len) : 3); + vdd->dtsi_data.flash_gamma_data_read_addr = + kzalloc(vdd->dtsi_data.flash_gamma_data_read_addr_len * sizeof(int *), GFP_KERNEL); + rc = of_property_read_u32_array(np, "samsung,flash_gamma_data_read_addr", + vdd->dtsi_data.flash_gamma_data_read_addr, vdd->dtsi_data.flash_gamma_data_read_addr_len); + if (rc) { + vdd->dtsi_data.flash_gamma_data_read_addr[0] = 8; + vdd->dtsi_data.flash_gamma_data_read_addr[1] = 9; + vdd->dtsi_data.flash_gamma_data_read_addr[2] = 10; + LCD_INFO("fail to get flash_gamma_data_read_addr\n"); + } + + rc = of_property_read_u32(np, "samsung,flash_gamma_write_check_addr", tmp); + vdd->dtsi_data.flash_gamma_write_check_address = (!rc ? tmp[0] : 0x0A16C4); + LCD_INFO("write_check_addr: 0x%x \n", vdd->dtsi_data.flash_gamma_write_check_address); + + data_32 = of_get_property(np, "samsung,flash_gamma_start_bank", &len); + vdd->dtsi_data.flash_gamma_bank_start_len = (data_32 ? len/sizeof(len) : 3); + data_32 = of_get_property(np, "samsung,flash_gamma_end_bank", &len); + vdd->dtsi_data.flash_gamma_bank_end_len = (data_32 ? len/sizeof(len) : 3); + + vdd->dtsi_data.flash_gamma_bank_start = kzalloc(vdd->dtsi_data.flash_gamma_bank_start_len * sizeof(int *), GFP_KERNEL); + vdd->dtsi_data.flash_gamma_bank_end = kzalloc(vdd->dtsi_data.flash_gamma_bank_end_len * sizeof(int *), GFP_KERNEL); + rc = of_property_read_u32_array(np, "samsung,flash_gamma_start_bank", + vdd->dtsi_data.flash_gamma_bank_start , + vdd->dtsi_data.flash_gamma_bank_start_len); + if (rc) + LCD_INFO("fail to get samsung,flash_gamma_start_bank\n"); + + rc = of_property_read_u32_array(np, "samsung,flash_gamma_end_bank", + vdd->dtsi_data.flash_gamma_bank_end, + vdd->dtsi_data.flash_gamma_bank_end_len); + if (rc) + LCD_INFO("fail to get samsung,flash_gamma_end_bank\n"); + + LCD_INFO("start_bank[0] : 0x%x end_bank[0]: 0x%x\n", + vdd->dtsi_data.flash_gamma_bank_start[0], + vdd->dtsi_data.flash_gamma_bank_end[0]); + + rc = of_property_read_u32(np, "samsung,flash_gamma_check_sum_start_offset", tmp); + vdd->dtsi_data.flash_gamma_check_sum_start_offset = (!rc ? tmp[0] : 0x16C2); + rc = of_property_read_u32(np, "samsung,flash_gamma_check_sum_end_offset", tmp); + vdd->dtsi_data.flash_gamma_check_sum_end_offset = (!rc ? tmp[0] : 0x16C3); + LCD_INFO("check_sum_start_offset : 0x%x check_sum_end_offset: 0x%x\n", + vdd->dtsi_data.flash_gamma_check_sum_start_offset, + vdd->dtsi_data.flash_gamma_check_sum_end_offset); + + rc = of_property_read_u32(np, "samsung,flash_gamma_0xc8_start_offset", tmp); + vdd->dtsi_data.flash_gamma_0xc8_start_offset = (!rc ? tmp[0] : 0x2000); + rc = of_property_read_u32(np, "samsung,flash_gamma_0xc8_end_offset", tmp); + vdd->dtsi_data.flash_gamma_0xc8_end_offset = (!rc ? tmp[0] : 0x2021); + rc = of_property_read_u32(np, "samsung,flash_gamma_0xc8_size", tmp); + vdd->dtsi_data.flash_gamma_0xc8_size = (!rc ? tmp[0] : 34); + rc = of_property_read_u32(np, "samsung,flash_gamma_0xc8_check_sum_start_offset", tmp); + vdd->dtsi_data.flash_gamma_0xc8_check_sum_start_offset = (!rc ? tmp[0] : 0x2022); + rc = of_property_read_u32(np, "samsung,flash_gamma_0xc8_check_sum_end_offset", tmp); + vdd->dtsi_data.flash_gamma_0xc8_check_sum_end_offset = (!rc ? tmp[0] : 0x2023); + LCD_INFO("flash_gamma 0xC8 start_addr:0x%x end_addr: 0x%x size: 0x%x check_sum_start : 0x%x check_sum_end: 0x%x\n", + vdd->dtsi_data.flash_gamma_0xc8_start_offset, + vdd->dtsi_data.flash_gamma_0xc8_end_offset, + vdd->dtsi_data.flash_gamma_0xc8_size, + vdd->dtsi_data.flash_gamma_0xc8_check_sum_start_offset, + vdd->dtsi_data.flash_gamma_0xc8_check_sum_end_offset); + + rc = of_property_read_u32(np, "samsung,flash_MCD1_R_addr", tmp); + vdd->dtsi_data.flash_MCD1_R_address = (!rc ? tmp[0] : 0xB8000); + rc = of_property_read_u32(np, "samsung,flash_MCD2_R_addr", tmp); + vdd->dtsi_data.flash_MCD2_R_address = (!rc ? tmp[0] : 0xB8001); + rc = of_property_read_u32(np, "samsung,flash_MCD1_L_addr", tmp); + vdd->dtsi_data.flash_MCD1_L_address = (!rc ? tmp[0] : 0xB8004); + rc = of_property_read_u32(np, "samsung,flash_MCD2_L_addr", tmp); + vdd->dtsi_data.flash_MCD2_L_address = (!rc ? tmp[0] : 0xB8005); + LCD_INFO("flash_gamma MCD1_R:0x%x MCD2_R:0x%x MCD1_L:0x%x MCD2_L:0x%x\n", + vdd->dtsi_data.flash_MCD1_R_address, + vdd->dtsi_data.flash_MCD2_R_address, + vdd->dtsi_data.flash_MCD1_L_address, + vdd->dtsi_data.flash_MCD2_L_address); + } +} + + +/***********/ +/* A2 line */ +/***********/ + +#define OTHER_PANEL_FILE "/efs/FactoryApp/a2_line.dat" + +int read_line(char *src, char *buf, int *pos, int len) +{ + int idx = 0; + + LCD_DEBUG("(%d) ++\n", *pos); + + while (*(src + *pos) != 10 && *(src + *pos) != 13) { + buf[idx] = *(src + *pos); + + idx++; + (*pos)++; + + if (idx > MAX_READ_LINE_SIZE) { + LCD_ERR("overflow!\n"); + return idx; + } + + if (*pos >= len) { + LCD_ERR("End of File (%d) / (%d)\n", *pos, len); + return idx; + } + } + + while (*(src + *pos) == 10 || *(src + *pos) == 13) + (*pos)++; + + LCD_DEBUG("--\n"); + + return idx; +} + +int ss_read_otherline_panel_data(struct samsung_display_driver_data *vdd) +{ + struct file *filp; + char *dp; + long l; + loff_t pos; + int ret = 0; + mm_segment_t fs; + + fs = get_fs(); + set_fs(get_ds()); + + filp = filp_open(OTHER_PANEL_FILE, O_RDONLY, 0); + if (IS_ERR(filp)) { + printk(KERN_ERR "%s File open failed\n", __func__); + + if (!IS_ERR_OR_NULL(vdd->panel_func.set_panel_fab_type)) + vdd->panel_func.set_panel_fab_type(BASIC_FB_PANLE_TYPE);/*to work as original line panel*/ + ret = -ENOENT; + goto err; + } + + l = filp->f_path.dentry->d_inode->i_size; + LCD_INFO("Loading File Size : %ld(bytes)", l); + + dp = kmalloc(l + 10, GFP_KERNEL); + if (dp == NULL) { + LCD_INFO("Can't not alloc memory for tuning file load\n"); + filp_close(filp, current->files); + ret = -1; + goto err; + } + pos = 0; + memset(dp, 0, l); + + LCD_INFO("before vfs_read()\n"); + ret = vfs_read(filp, (char __user *)dp, l, &pos); + LCD_INFO("after vfs_read()\n"); + + if (ret != l) { + LCD_INFO("vfs_read() filed ret : %d\n", ret); + kfree(dp); + filp_close(filp, current->files); + ret = -1; + goto err; + } + + if (!IS_ERR_OR_NULL(vdd->panel_func.parsing_otherline_pdata)) + ret = vdd->panel_func.parsing_otherline_pdata(filp, vdd, dp, l); + + filp_close(filp, current->files); + + set_fs(fs); + + kfree(dp); + + return ret; +err: + set_fs(fs); + return ret; +} + +static void read_panel_data_work_fn(struct work_struct *work) +{ + struct samsung_display_driver_data *vdd = + container_of(work, struct samsung_display_driver_data, + other_line_panel_support_work.work); + int ret = 1; + + ret = ss_read_otherline_panel_data(vdd); + + if (ret && vdd->other_line_panel_work_cnt) { + queue_delayed_work(vdd->other_line_panel_support_workq, + &vdd->other_line_panel_support_work, + msecs_to_jiffies(OTHERLINE_WORKQ_DEALY)); + vdd->other_line_panel_work_cnt--; + } else + destroy_workqueue(vdd->other_line_panel_support_workq); + + if (vdd->other_line_panel_work_cnt == 0) + LCD_ERR(" cnt (%d)\n", vdd->other_line_panel_work_cnt); +} + +/*********************/ +/* LPM control */ +/*********************/ +void ss_panel_low_power_config(struct samsung_display_driver_data *vdd, int enable) +{ + if (!vdd->dtsi_data.panel_lpm_enable) { + LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n"); + return; + } + + ss_panel_lpm_power_ctrl(vdd, enable); + + ss_panel_lpm_ctrl(vdd, enable); + + if (enable) { + vdd->esd_recovery.is_wakeup_source = true; + } else { + vdd->esd_recovery.is_wakeup_source = false; + } + + if (vdd->esd_recovery.esd_irq_enable) + vdd->esd_recovery.esd_irq_enable(true, true, (void *)vdd); +} + +/* + * ss_find_reg_offset() + * This function find offset for reg value + * reg_list[X][0] is reg value + * reg_list[X][1] is offset for reg value + * cmd_list is the target cmds for searching reg value + */ +int ss_find_reg_offset(int (*reg_list)[2], + struct dsi_panel_cmd_set *cmd_list[], int list_size) +{ + struct dsi_panel_cmd_set *lpm_cmds = NULL; + int i = 0, j = 0, max_cmd_cnt; + + if (IS_ERR_OR_NULL(reg_list) || IS_ERR_OR_NULL(cmd_list)) + goto end; + + for (i = 0; i < list_size; i++) { + lpm_cmds = cmd_list[i]; + max_cmd_cnt = lpm_cmds->count; + + for (j = 0; j < max_cmd_cnt; j++) { + if (lpm_cmds->cmds[j].msg.tx_buf && + lpm_cmds->cmds[j].msg.tx_buf[0] == reg_list[i][0]) { + reg_list[i][1] = j; + break; + } + } + } + +end: + for (i = 0; i < list_size; i++) + LCD_DEBUG("offset[%d] : %d\n", i, reg_list[i][1]); + return 0; +} + +static bool is_new_lpm_version(struct samsung_display_driver_data *vdd) +{ + if (vdd->panel_lpm.ver == LPM_VER1) + return true; + else + return false; +} + +static void set_lpm_br_values(struct samsung_display_driver_data *vdd) +{ + int from, end; + int left, right, p = 0; + int loop = 0; + struct candela_map_table *table; + int bl_level = vdd->br.bl_level; + + table = &vdd->dtsi_data.candela_map_table[AOD][vdd->panel_revision]; + + if (IS_ERR_OR_NULL(table->cd)) { + LCD_ERR("No aod candela_map_table..\n"); + return; + } + + LCD_DEBUG("table size (%d)\n", table->tab_size); + + if (bl_level > table->max_lv) + bl_level = table->max_lv; + + left = 0; + right = table->tab_size - 1; + + while (left <= right) { + loop++; + p = (left + right) / 2; + from = table->from[p]; + end = table->end[p]; + LCD_DEBUG("[%d] from(%d) end(%d) / %d\n", p, from, end, bl_level); + + if (bl_level >= from && bl_level <= end) + break; + if (bl_level < from) + right = p - 1; + else + left = p + 1; + + if (loop > table->tab_size) { + pr_err("can not find (%d) level in table!\n", bl_level); + p = table->tab_size - 1; + break; + } + }; + vdd->panel_lpm.lpm_bl_level = table->cd[p]; + + LCD_DEBUG("%s: (%d)->(%d)\n", + __func__, vdd->br.bl_level, vdd->panel_lpm.lpm_bl_level); + +} + +int ss_panel_lpm_power_ctrl(struct samsung_display_driver_data *vdd, int enable) +{ + int i; + int rc = 0; + int get_voltage; + struct regulator *elvss = NULL; + struct dsi_panel *panel = NULL; + struct dsi_vreg *target_vreg = NULL; + struct dsi_regulator_info regs; + struct lpm_pwr_ctrl *lpm_pwr; + + if (!vdd->dtsi_data.panel_lpm_enable) { + LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n"); + return -ENODEV; + } + + panel = GET_DSI_PANEL(vdd); + if (IS_ERR_OR_NULL(panel)) { + pr_err("No Panel Data\n"); + return -ENODEV; + } + + LCD_DEBUG("%s ++\n", enable == true ? "Enable" : "Disable"); + + regs = panel->power_info; + lpm_pwr = &vdd->panel_lpm.lpm_pwr; + + if (lpm_pwr->support_lpm_pwr_ctrl) { + pr_err("%s: No panel power control for LPM\n", __func__); + return -ENODEV; + } + + mutex_lock(&vdd->panel_lpm.lpm_lock); + + /* Find vreg for LPM setting */ + for (i = 0; i < regs.count; i++) { + target_vreg = ®s.vregs[i]; + if (!strcmp(target_vreg->vreg_name, lpm_pwr->lpm_pwr_ctrl_supply_name)) { + LCD_INFO("Found Voltage(%d)\n", i); + break; + } + } + + /* To check previous voltage */ + get_voltage = regulator_get_voltage(target_vreg->vreg); + + if (enable) { /* AOD ON(Enter) */ + if (get_voltage != lpm_pwr->lpm_pwr_ctrl_supply_min_v) { + rc = regulator_set_voltage( + target_vreg->vreg, + lpm_pwr->lpm_pwr_ctrl_supply_min_v, + lpm_pwr->lpm_pwr_ctrl_supply_max_v); + if (rc < 0) { + LCD_ERR("Voltage Set Fail enable=%d voltage : %d rc : %d\n", + enable, lpm_pwr->lpm_pwr_ctrl_supply_min_v, rc); + } else { + get_voltage = regulator_get_voltage(target_vreg->vreg); + LCD_INFO("enable=%d, current get_voltage=%d rc : %d\n", enable, get_voltage, rc); + } + + if (strlen(lpm_pwr->lpm_pwr_ctrl_elvss_name) > 0) { + LCD_INFO("ELVSS Name(%s), Level(%d)\n", lpm_pwr->lpm_pwr_ctrl_elvss_name, + lpm_pwr->lpm_pwr_ctrl_elvss_lpm_v); + /* Elvss Regulator for Short Detection*/ + elvss = regulator_get(NULL, lpm_pwr->lpm_pwr_ctrl_elvss_name); +#if KERNEL_VER == 414 + if (elvss) { + rc = regulator_set_short_detection(elvss, true, + vdd->panel_lpm.lpm_pwr.lpm_pwr_ctrl_elvss_lpm_v); + if (rc < 0) + LCD_ERR("Regulator Set for AOD Short Detection Fail\n"); + regulator_put(elvss); + } else + LCD_ERR("ELVSS Regulator Get Fail\n"); +#endif + } else + LCD_ERR("No elvss name for lpm power control\n"); + } else + LCD_DEBUG("enable=%d, previous voltage : %d\n", enable, get_voltage); + + } else { /* AOD OFF(Exit) */ + if (get_voltage != target_vreg->min_voltage) { + rc = regulator_set_voltage( + target_vreg->vreg, + target_vreg->min_voltage, + target_vreg->max_voltage); + if (rc < 0) { + LCD_ERR("Voltage Set Fail enable=%d voltage : %d rc : %d\n", + enable, target_vreg->min_voltage, rc); + panic("Voltage Set Fail to NORMAL"); + } else { + get_voltage = regulator_get_voltage(target_vreg->vreg); + LCD_INFO("enable=%d, current get_voltage=%d\n", enable, get_voltage); + + if (get_voltage != target_vreg->min_voltage) + panic("Voltage Set Fail to NORMAL"); + } + + if (strlen(lpm_pwr->lpm_pwr_ctrl_elvss_name) > 0) { + LCD_INFO("ELVSS Name(%s), Level(%d)\n", lpm_pwr->lpm_pwr_ctrl_elvss_name, + lpm_pwr->lpm_pwr_ctrl_elvss_normal_v); + /* Elvss Regulator for Short Detection*/ + elvss = regulator_get(NULL, lpm_pwr->lpm_pwr_ctrl_elvss_name); +#if KERNEL_VER == 414 + if (elvss) { + rc = regulator_set_short_detection(elvss, true, + lpm_pwr->lpm_pwr_ctrl_elvss_normal_v); + if (rc < 0) + LCD_ERR("Regulator Set for Normal Short Detection Fail\n"); + regulator_put(elvss); + } else + LCD_ERR("ELVSS Regulator Get Fail\n"); +#endif + } else + LCD_ERR("No elvss name for lpm power control\n"); + } else + LCD_DEBUG("enable=%d, previous voltage : %d\n", enable, get_voltage); + } + + mutex_unlock(&vdd->panel_lpm.lpm_lock); + LCD_DEBUG("[Panel LPM] --\n"); + + return rc; +} + +void ss_panel_lpm_ctrl(struct samsung_display_driver_data *vdd, int enable) +{ + static int stored_bl_level; /* Used for factory mode only */ + int current_bl_level = 0; + u32 lpm_init_delay = 0; + + LCD_INFO("[Panel LPM] ++\n"); + + if (!vdd->dtsi_data.panel_lpm_enable) { + LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n"); + return; + } + + if (ss_is_panel_off(vdd)) { + LCD_INFO("[Panel LPM] Do not change mode\n"); + goto end; + } + + lpm_init_delay = vdd->dtsi_data.samsung_lpm_init_delay; + + mutex_lock(&vdd->panel_lpm.lpm_lock); + + if (enable) { /* AOD ON(Enter) */ + if (unlikely(vdd->is_factory_mode) && !ss_is_panel_lpm(vdd)) { + LCD_INFO("[Panel LPM] Set low brightness for factory mode (%d) \n", vdd->br.bl_level); + stored_bl_level = vdd->br.bl_level; + ss_brightness_dcs(vdd, 0); + } + + if (!ss_is_panel_lpm(vdd)) { + ss_send_cmd(vdd, TX_DISPLAY_OFF); + LCD_INFO("[Panel LPM] Send panel DISPLAY_OFF cmds\n"); + } else { + LCD_INFO("[Panel LPM] skip DISPLAY_OFF cmds\n"); + } + + if (vdd->panel_func.samsung_update_lpm_ctrl_cmd) { + vdd->panel_func.samsung_update_lpm_ctrl_cmd(vdd); + LCD_INFO("[Panel LPM] update lpm cmd done\n"); + } + + /* lpm init delay */ + if (vdd->display_status_dsi.aod_delay == true) { + vdd->display_status_dsi.aod_delay = false; + if (lpm_init_delay) + msleep(lpm_init_delay); + LCD_INFO("%ums delay before turn on lpm mode", + lpm_init_delay); + } + + /* Self Display Setting */ + if (vdd->self_disp.aod_enter) + vdd->self_disp.aod_enter(vdd); + + ss_send_cmd(vdd, TX_LPM_ON); + LCD_INFO("[Panel LPM] Send panel LPM cmds\n"); + + if (unlikely(vdd->is_factory_mode)) + ss_send_cmd(vdd, TX_DISPLAY_ON); + else { + /* The display_on cmd will be sent on next commit */ + vdd->display_status_dsi.wait_disp_on = true; + vdd->display_status_dsi.wait_actual_disp_on = true; + LCD_INFO("[Panel LPM] Set wait_disp_on to true\n"); + } + + vdd->panel_state = PANEL_PWR_LPM; + + /* + Update mdnie to disable mdnie operation by scenario at AOD display status. + */ + if (vdd->mdnie.support_mdnie) { + update_dsi_tcon_mdnie_register(vdd); + } + + + } else { /* AOD OFF(Exit) */ + /* Self Display Setting */ + if (vdd->self_disp.aod_exit) + vdd->self_disp.aod_exit(vdd); + + /* Turn Off ALPM Mode */ + ss_send_cmd(vdd, TX_LPM_OFF); + + LCD_INFO("[Panel LPM] Send panel LPM off cmds\n"); + + if (unlikely(vdd->is_factory_mode)) { + LCD_INFO("[Panel LPM] restore bl_level for factory (%d) \n", stored_bl_level); + current_bl_level = stored_bl_level; + } else { + current_bl_level = vdd->br.bl_level; + } + + LCD_INFO("[Panel LPM] Restore brightness level (%d) \n", current_bl_level); + + vdd->panel_state = PANEL_PWR_ON; + + ss_brightness_dcs(vdd, current_bl_level); + + if (vdd->mdnie.support_mdnie) { + vdd->mdnie.lcd_on_notifiy = true; + update_dsi_tcon_mdnie_register(vdd); + if (vdd->mdnie.support_trans_dimming) + vdd->mdnie.disable_trans_dimming = false; + } + + if (vdd->panel_func.samsung_cover_control && vdd->cover_control) + vdd->panel_func.samsung_cover_control(vdd); + + if (unlikely(vdd->is_factory_mode)) { + ss_send_cmd(vdd, TX_DISPLAY_ON); + vdd->panel_state = PANEL_PWR_ON; + } else { + /* The display_on cmd will be sent on next commit */ + vdd->display_status_dsi.wait_disp_on = true; + vdd->display_status_dsi.wait_actual_disp_on = true; + LCD_INFO("[Panel LPM] Set wait_disp_on to true\n"); + } + + /* 1Frame Delay(33.4ms - 30FPS) Should be added */ + usleep_range(34*1000, 34*1000); + } + + LCD_INFO("[Panel LPM] En/Dis : %s, LPM_MODE : %s, Hz : 30Hz, bl_level : %s\n", + /* Enable / Disable */ + enable ? "Enable" : "Disable", + /* Check LPM mode */ + vdd->panel_lpm.mode == ALPM_MODE_ON ? "ALPM" : + vdd->panel_lpm.mode == HLPM_MODE_ON ? "HLPM" : + vdd->panel_lpm.mode == LPM_MODE_OFF ? "MODE_OFF" : "UNKNOWN", + /* Check current brightness level */ + vdd->panel_lpm.lpm_bl_level == LPM_2NIT ? "2NIT" : + vdd->panel_lpm.lpm_bl_level == LPM_10NIT ? "10NIT" : + vdd->panel_lpm.lpm_bl_level == LPM_30NIT ? "30NIT" : + vdd->panel_lpm.lpm_bl_level == LPM_60NIT ? "60NIT" : "UNKNOWN"); + + mutex_unlock(&vdd->panel_lpm.lpm_lock); +end: + LCD_INFO("[Panel LPM] --\n"); +} + +/* This is depricated.. Get vdd pointer not from global variable. */ +struct samsung_display_driver_data *ss_get_vdd(enum ss_display_ndx ndx) +{ + if (ndx >= MAX_DISPLAY_NDX || ndx < 0) { + LCD_ERR("invalid ndx(%d)\n", ndx); + return NULL; + } + + return &vdd_data[ndx]; +} + +/* + * @param: + * D0: hall_ic (the hall ic status, 0: foder open. 1: folder close) + * D8: flip_not_refresh (0: refresh after flipping. 1: don't refresh after flipping) + */ +int samsung_display_hall_ic_status(struct notifier_block *nb, + unsigned long param, void *data) +{ + struct samsung_display_driver_data *vdd = container_of(nb, + struct samsung_display_driver_data, hall_ic_notifier_display); + bool hall_ic = (bool)(param & 0x1); + //bool flip_not_refresh = (bool)(!!(param & LCD_FLIP_NOT_REFRESH)); + struct sde_connector *conn = GET_SDE_CONNECTOR(vdd); + struct drm_event event; + bool panel_dead = false; + + /* + previous panel off -> current panel on + foder open : 0, close : 1 + */ + + if (!vdd->support_hall_ic) + return 0; + + LCD_ERR("mdss hall_ic : %s, start\n", hall_ic ? "CLOSE" : "OPEN"); + +#if 0 + if (ss_panel_attached(PRIMARY_DISPLAY_NDX) && ss_panel_attached(SECONDARY_DISPLAY_NDX)) { + /* To check current blank mode */ + if ((ss_is_panel_on(vdd) || + ss_is_panel_lpm(vdd)) && + vdd->hall_ic_status != hall_ic) { + + /* set flag */ + vdd->hall_ic_mode_change_trigger = true; + vdd->lcd_flip_not_refresh = flip_not_refresh; + + /* panel off */ + // TODO: off panel.. + // call msm_disable_outputs()..??? + LCD_ERR("should implement panel off...\n"); + + /* set status */ + vdd->hall_ic_status = hall_ic; + + /* panel on */ + // TODO: off panel.. + // call complete_commit()..??? + LCD_ERR("should implement panel on...\n"); + + /* clear flag */ + vdd->hall_ic_mode_change_trigger = false; + vdd->lcd_flip_not_refresh = false; + + /* Brightness setting */ + // TODO: check if it controls right display in dual dsi or dual display... + if (ss_is_bl_dcs(vdd)) + ss_brightness_dcs(vdd, vdd->br.bl_level); + + /* display on */ + if (ss_is_video_mode(vdd)) + ss_send_cmd(vdd, TX_DISPLAY_ON); + + /* refresh a frame to panel */ + if (ss_is_cmd_mode(vdd) && !flip_not_refresh) { + /* TODO: refresh a frame to panel.. with drm msm code... + call complete_commit()..?? + fbi->fbops->fb_pan_display(&fbi->var, fbi); + */ + LCD_ERR("need to refresh a frame to panel.. with drm msm code...\n"); + } + } else { + vdd->hall_ic_status = hall_ic; + LCD_ERR("mdss skip display changing\n"); + } + } else { +#endif + /* check the lcd id for DISPLAY_1 and DISPLAY_2 */ + if (ss_panel_attached(PRIMARY_DISPLAY_NDX) && ss_panel_attached(SECONDARY_DISPLAY_NDX)) { + /* set status */ + vdd->hall_ic_status_unhandled = hall_ic; + //vdd->panel_dead = true; panel switch, should not set panel_dead, else MTP is not read + + panel_dead = true; + event.type = DRM_EVENT_PANEL_DEAD; + event.length = sizeof(bool); + msm_mode_object_event_notify(&conn->base.base, + conn->base.dev, &event, (u8 *)&panel_dead); + /* TODO: send flip_not_refresh to HAL, and let HAL to handle it. */ + } else { + /* check the lcd id for DISPLAY_1 */ + if (ss_panel_attached(PRIMARY_DISPLAY_NDX)) + vdd->hall_ic_status = HALL_IC_OPEN; + + /* check the lcd id for DISPLAY_2 */ + if (ss_panel_attached(SECONDARY_DISPLAY_NDX)) + vdd->hall_ic_status = HALL_IC_CLOSE; + } + + return 0; +} + +static void samsung_display_delay_disp_on_work(struct work_struct *work) +{ + struct samsung_display_driver_data *vdd = + container_of(work, struct samsung_display_driver_data, + delay_disp_on_work.work); + + LCD_INFO("wait_disp_on is set\n"); + vdd->display_status_dsi.wait_disp_on = true; + ss_send_cmd(vdd, TX_DISPLAY_ON); +} + +int get_hall_ic_status(char *mode) +{ + struct samsung_display_driver_data *vdd; + int status; + + if (mode == NULL) + return true; + + if (*mode - '0') + status = HALL_IC_CLOSE; + else + status = HALL_IC_OPEN; + + + vdd = ss_get_vdd(PRIMARY_DISPLAY_NDX); + vdd->hall_ic_status = status; + + vdd = ss_get_vdd(SECONDARY_DISPLAY_NDX); + vdd->hall_ic_status = status; + + LCD_ERR("hall_ic : %s\n", status ? "CLOSE" : "OPEN"); + + return true; +} +EXPORT_SYMBOL(get_hall_ic_status); +__setup("hall_ic=0x", get_hall_ic_status); + +/*************************************************************************************************** +* BRIGHTNESS RELATED FUNCTION. +****************************************************************************************************/ +static void set_normal_br_values(struct samsung_display_driver_data *vdd) +{ + int from, end; + int left, right, p = 0; + int loop = 0; + struct candela_map_table *table; + + + if (vdd->br.pac) + table = &vdd->dtsi_data.candela_map_table[PAC_NORMAL][vdd->panel_revision]; + else + table = &vdd->dtsi_data.candela_map_table[NORMAL][vdd->panel_revision]; + + if (IS_ERR_OR_NULL(table->cd)) { + LCD_ERR("No candela_map_table.. \n"); + return; + } + + LCD_DEBUG("table size (%d)\n", table->tab_size); + + if (vdd->br.bl_level > table->max_lv) + vdd->br.bl_level = table->max_lv; + + left = 0; + right = table->tab_size - 1; + + while (left <= right) { + loop++; + p = (left + right) / 2; + from = table->from[p]; + end = table->end[p]; + LCD_DEBUG("[%d] from(%d) end(%d) / %d\n", p, from, end, vdd->br.bl_level); + + if (vdd->br.bl_level >= from && vdd->br.bl_level <= end) + break; + if (vdd->br.bl_level < from) + right = p - 1; + else + left = p + 1; + LCD_DEBUG("left(%d) right(%d)\n", left, right); + + if (loop > table->tab_size) { + pr_err("can not find (%d) level in table!\n", vdd->br.bl_level); + p = table->tab_size - 1; + break; + } + }; + + // set values.. + vdd->br.cd_idx = table->idx[p]; + vdd->br.cd_level = table->cd[p]; + + if (vdd->br.pac) { + vdd->br.pac_cd_idx = table->scaled_idx[p]; + vdd->br.interpolation_cd = table->interpolation_cd[p]; + + LCD_INFO("[%d] pac_cd_idx (%d) cd_idx (%d) cd (%d) interpolation_cd (%d)\n", + p, vdd->br.pac_cd_idx, vdd->br.cd_idx, vdd->br.cd_level, vdd->br.interpolation_cd); + } else + LCD_INFO("[%d] cd_idx (%d) cd (%d) interpolation_cd (%d)\n", + p, vdd->br.cd_idx, vdd->br.cd_level, vdd->br.interpolation_cd); + return; +} + +static void set_hbm_br_values(struct samsung_display_driver_data *vdd) +{ + int from, end; + int left, right, p = 0; + int loop = 0; + struct candela_map_table *table; + + if (vdd->br.pac) + table = &vdd->dtsi_data.candela_map_table[PAC_HBM][vdd->panel_revision]; + else + table = &vdd->dtsi_data.candela_map_table[HBM][vdd->panel_revision]; + + if (IS_ERR_OR_NULL(table->cd)) { + LCD_ERR("No hbm candela_map_table..\n"); + return; + } + + if (vdd->br.bl_level > table->max_lv) + vdd->br.bl_level = table->max_lv; + + if (vdd->dtsi_data.samsung_elvss_compensation) { + if (vdd->br.bl_level >= 317) { + LCD_INFO("Forced SET from [%d] to [317] \n", vdd->br.bl_level); + vdd->br.bl_level = 317; + } + } + + left = 0; + right = table->tab_size - 1; + + while (left <= right) { + loop++; + p = (left + right) / 2; + from = table->from[p]; + end = table->end[p]; + LCD_DEBUG("[%d] from(%d) end(%d) / %d\n", p, from, end, vdd->br.bl_level); + + if (vdd->br.bl_level >= from && vdd->br.bl_level <= end) + break; + if (vdd->br.bl_level < from) + right = p - 1; + else + left = p + 1; + LCD_DEBUG("left(%d) right(%d)\n", left, right); + + if (loop > table->tab_size) { + pr_err("can not find (%d) level in table!\n", vdd->br.bl_level); + p = table->tab_size - 1; + break; + } + }; + + // set values.. + vdd->br.cd_idx = table->idx[p]; + vdd->br.cd_level = table->cd[p]; + vdd->br.auto_level = table->auto_level[p]; + + if (vdd->br.pac) { + vdd->br.pac_cd_idx = table->scaled_idx[p]; + vdd->br.interpolation_cd = table->interpolation_cd[p]; + + LCD_INFO("[%d] pac_cd_idx (%d) cd_idx (%d) cd (%d) interpolation_cd (%d) auto (%d)\n", + p, vdd->br.pac_cd_idx, vdd->br.cd_idx, vdd->br.cd_level, vdd->br.interpolation_cd, vdd->br.auto_level); + } else + LCD_INFO("[%d] cd_idx (%d) cd (%d) interpolation_cd (%d) auto (%d) \n", + p, vdd->br.cd_idx, vdd->br.cd_level, vdd->br.interpolation_cd, vdd->br.auto_level); + + return; +} + +static void ss_update_brightness_packet(struct dsi_cmd_desc *packet, + int *count, struct dsi_panel_cmd_set *tx_cmd) +{ + int loop = 0; + + if (IS_ERR_OR_NULL(packet)) { + LCD_ERR("%ps no packet\n", __builtin_return_address(0)); + return; + } + + if (SS_IS_CMDS_NULL(tx_cmd)) { + LCD_ERR("%ps no tx_cmd\n", __builtin_return_address(0)); + return; + } + + if (*count > (BRIGHTNESS_MAX_PACKET - 1)) + panic("over max brightness_packet size(%d).. !!", + BRIGHTNESS_MAX_PACKET); + + for (loop = 0; loop < tx_cmd->count; loop++) + packet[(*count)++] = tx_cmd->cmds[loop]; +} + +void update_packet_level_key_enable(struct samsung_display_driver_data *vdd, + struct dsi_cmd_desc *packet, int *cmd_cnt, int level_key) +{ + if (!level_key) + return; + else { + if (level_key & LEVEL0_KEY) + ss_update_brightness_packet(packet, cmd_cnt, ss_get_cmds(vdd, TX_LEVEL0_KEY_ENABLE)); + + if (level_key & LEVEL1_KEY) + ss_update_brightness_packet(packet, cmd_cnt, ss_get_cmds(vdd, TX_LEVEL1_KEY_ENABLE)); + + if (level_key & LEVEL2_KEY) + ss_update_brightness_packet(packet, cmd_cnt, ss_get_cmds(vdd, TX_LEVEL2_KEY_ENABLE)); + } +} + +void update_packet_level_key_disable(struct samsung_display_driver_data *vdd, + struct dsi_cmd_desc *packet, int *cmd_cnt, int level_key) +{ + if (!level_key) + return; + else { + if (level_key & LEVEL0_KEY) + ss_update_brightness_packet(packet, cmd_cnt, ss_get_cmds(vdd, TX_LEVEL0_KEY_DISABLE)); + + if (level_key & LEVEL1_KEY) + ss_update_brightness_packet(packet, cmd_cnt, ss_get_cmds(vdd, TX_LEVEL1_KEY_DISABLE)); + + if (level_key & LEVEL2_KEY) + ss_update_brightness_packet(packet, cmd_cnt, ss_get_cmds(vdd, TX_LEVEL2_KEY_DISABLE)); + } +} + +static int ss_hbm_brightness_packet_set( + struct samsung_display_driver_data *vdd) +{ + int cmd_cnt = 0; + int level_key = 0; + struct dsi_panel_cmd_set *set; + struct dsi_cmd_desc *packet = NULL; + struct dsi_panel_cmd_set *tx_cmd = NULL; + + /* init packet */ + set = ss_get_cmds(vdd, TX_BRIGHT_CTRL); + if (SS_IS_CMDS_NULL(set)) { + LCD_ERR("No cmds for TX_BRIGHT_CTRL.. \n"); + return -EINVAL; + } + + packet = set->cmds; + + set_hbm_br_values(vdd); + + /* IRC */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_irc)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_hbm_irc(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* Gamma */ + if (ss_get_cmds(vdd, TX_HBM_GAMMA)->count) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_gamma)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_hbm_gamma(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } + + /* hbm etc */ + if (ss_get_cmds(vdd, TX_HBM_ETC)->count) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_etc)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_hbm_etc(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } + + return cmd_cnt; +} + +static int ss_normal_brightness_packet_set( + struct samsung_display_driver_data *vdd) +{ + int cmd_cnt = 0; + int level_key = 0; + struct dsi_panel_cmd_set *set; + struct dsi_cmd_desc *packet = NULL; + struct dsi_panel_cmd_set *tx_cmd = NULL; + + /* init packet */ + set = ss_get_cmds(vdd, TX_BRIGHT_CTRL); + if (SS_IS_CMDS_NULL(set)) { + LCD_ERR("No cmds for TX_BRIGHT_CTRL.. \n"); + return -EINVAL; + } + packet = set->cmds; + + vdd->br.auto_level = 0; + + set_normal_br_values(vdd); + + if (vdd->smart_dimming_loaded_dsi) { /* OCTA PANEL */ + /* hbm off */ + if (vdd->display_status_dsi.hbm_mode) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_hbm_off)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_hbm_off(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } + + /* aid/aor */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_aid)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_aid(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* acl */ + if (vdd->acl_status || vdd->siop_status) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_acl_on)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_acl_on(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_pre_acl_percent)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_pre_acl_percent(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_acl_percent)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_acl_percent(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } else { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_acl_off)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_acl_off(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } + + /* elvss */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss)) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_pre_elvss)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_pre_elvss(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_elvss(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* elvss 2nd (ex : elvss_compensation, ..) */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_2)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_elvss_2(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* temperature elvss */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_temperature1)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_elvss_temperature1(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_temperature2)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_elvss_temperature2(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* caps*/ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_caps)) { + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_pre_caps)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_pre_caps(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_caps(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* vint */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_vint)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_vint(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* IRC */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_irc)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_irc(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* gamma */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_gamma)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_gamma(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } else { /* TFT PANEL */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_tft_pwm_ldi)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_tft_pwm_ldi(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } + + return cmd_cnt; +} + +int ss_single_transmission_packet(struct dsi_panel_cmd_set *cmds) +{ + int loop; + struct dsi_cmd_desc *packet = cmds->cmds; + int packet_cnt = cmds->count; + + for (loop = 0; (loop < packet_cnt) && (loop < BRIGHTNESS_MAX_PACKET); loop++) { + if (packet[loop].msg.type == MIPI_DSI_DCS_LONG_WRITE || + packet[loop].msg.type == MIPI_DSI_GENERIC_LONG_WRITE) + packet[loop].last_command = false; + else { + if (loop > 0) + packet[loop - 1].last_command = true; /*To ensure previous single tx packet */ + + packet[loop].last_command = true; + } + } + + if (loop == BRIGHTNESS_MAX_PACKET) + return false; + else { + packet[loop - 1].last_command = true; /* To make last packet flag */ + return true; + } +} + +static bool is_hbm_level(struct samsung_display_driver_data *vdd) +{ + + struct candela_map_table *table; + + if (vdd->br.pac) + table = &vdd->dtsi_data.candela_map_table[PAC_HBM][vdd->panel_revision]; + else + table = &vdd->dtsi_data.candela_map_table[HBM][vdd->panel_revision]; + + if (vdd->br.bl_level < table->min_lv) + return false; + + if (vdd->br.bl_level > table->max_lv) { + LCD_ERR("bl_level(%d) is over max_level (%d), force set to max\n", vdd->br.bl_level, table->max_lv); + vdd->br.bl_level = table->max_lv; + } + + return true; +} + +/* ss_brightness_dcs() is called not in locking status. + * Instead, calls ss_set_backlight() when you need to controll backlight + * in locking status. + */ +int ss_brightness_dcs(struct samsung_display_driver_data *vdd, int level) +{ + int cmd_cnt = 0; + int ret = 0; + int need_lpm_lock = 0; + struct dsi_panel_cmd_set *brightness_cmds = NULL; + struct dsi_panel *panel = GET_DSI_PANEL(vdd); + + /* FC2 change: set panle mode in SurfaceFlinger initialization, instead of kenrel booting... */ + if (!panel->cur_mode) { + LCD_ERR("err: no panel mode yet...\n"); + return false; + } + + /* + * panel_lpm.lpm_lock should be locked + * to avoid brightness mis-handling (LPM brightness <-> normal brightness) from other thread + * but running ss_panel_lpm_ctrl thread + */ + if (ss_is_panel_lpm(vdd)) need_lpm_lock = 1; + if (need_lpm_lock) mutex_lock(&vdd->panel_lpm.lpm_lock); + + // need bl_lock.. + mutex_lock(&vdd->bl_lock); + + vdd->br.bl_level = level; + + /* check the lcd id for DISPLAY_1 or DISPLAY_2 */ + if (!ss_panel_attached(vdd->ndx)) + goto skip_bl_update; + + if (vdd->dtsi_data.flash_gamma_support && + !vdd->panel_br_info.flash_data.init_done) { + LCD_ERR("flash_gamme not ready\n"); + goto skip_bl_update; + } + + if (vdd->dtsi_data.panel_lpm_enable && is_new_lpm_version(vdd)) { + set_lpm_br_values(vdd); + + if (ss_is_panel_lpm(vdd)) { + LCD_ERR("[Panel LPM]: set brightness.(%d)->(%d)\n", vdd->br.bl_level, vdd->panel_lpm.lpm_bl_level); + + if (vdd->panel_func.samsung_set_lpm_brightness) + vdd->panel_func.samsung_set_lpm_brightness(vdd); + goto skip_bl_update; + } + } + + if (vdd->dtsi_data.hmt_enabled && vdd->hmt_stat.hmt_on) { + LCD_ERR("HMT is on. do not set normal brightness..(%d)\n", level); + goto skip_bl_update; + } + + if (ss_is_seamless_mode(vdd)) { + LCD_ERR("splash is not done..\n"); + goto skip_bl_update; + } + + if (!vdd->dtsi_data.tft_common_support && is_hbm_level(vdd)) { + cmd_cnt = ss_hbm_brightness_packet_set(vdd); + cmd_cnt > 0 ? vdd->display_status_dsi.hbm_mode = true : false; + } else { + cmd_cnt = ss_normal_brightness_packet_set(vdd); + cmd_cnt > 0 ? vdd->display_status_dsi.hbm_mode = false : false; + } + + if (cmd_cnt) { + /* setting tx cmds cmt */ + brightness_cmds = ss_get_cmds(vdd, TX_BRIGHT_CTRL); + brightness_cmds->count = cmd_cnt; + + /* generate single tx packet */ + ret = ss_single_transmission_packet(brightness_cmds); + + /* sending tx cmds */ + if (ret) { + ss_send_cmd(vdd, TX_BRIGHT_CTRL); + + if (!IS_ERR_OR_NULL(ss_get_cmds(vdd, TX_BLIC_DIMMING)->cmds)) { + if (vdd->br.bl_level == 0) + ss_get_cmds(vdd, TX_BLIC_DIMMING)->cmds->msg.tx_buf[1] = 0x24; + else + ss_get_cmds(vdd, TX_BLIC_DIMMING)->cmds->msg.tx_buf[1] = 0x2C; + + ss_send_cmd(vdd, TX_BLIC_DIMMING); + } + + // copr sum after changing brightness to calculate brightness avg. + if (vdd->copr.copr_on) { + ss_set_copr_sum(vdd, COPR_CD_INDEX_0); + ss_set_copr_sum(vdd, COPR_CD_INDEX_1); + } + + LCD_INFO("level : %d candela : %dCD hbm : %d (%d) itp : %s\n", + vdd->br.bl_level, vdd->br.cd_level, vdd->display_status_dsi.hbm_mode, vdd->br.auto_level, + vdd->panel_br_info.itp_mode == 0 ? "TABLE" : "FLASH"); + + queue_work(vdd->br.br_wq, &vdd->br.br_work); + + } else + LCD_INFO("single_transmission_fail error\n"); + } else + LCD_INFO("level : %d skip\n", vdd->br.bl_level); + +skip_bl_update: + mutex_unlock(&vdd->bl_lock); + + if (need_lpm_lock) mutex_unlock(&vdd->panel_lpm.lpm_lock); + + return 0; +} + + +int ss_reading_mode_dcs(struct samsung_display_driver_data *vdd) +{ + struct dsi_panel_cmd_set *pcmds; + + if (!vdd->reading_mode.support_reading_mode) + return 0; + + pcmds = ss_get_cmds(vdd, TX_READING_MODE_TUNE); + pcmds->cmds = vdd->reading_mode.reading_mode_tune_dsi[vdd->reading_mode.cur_level]; + pcmds->count = vdd->reading_mode.dsi_tune_size; + + ss_send_cmd(vdd, TX_READING_MODE_TUNE); + + return 0; +} +// HMT brightness +static void set_hmt_br_values(struct samsung_display_driver_data *vdd) +{ + int from, end; + int left, right, p = 0; + struct candela_map_table *table; + + table = &vdd->dtsi_data.candela_map_table[HMT][vdd->panel_revision]; + + if (IS_ERR_OR_NULL(table->cd)) { + LCD_ERR("No candela_map_table..\n"); + return; + } + + LCD_DEBUG("table size (%d)\n", table->tab_size); + + if (vdd->hmt_stat.hmt_bl_level > table->max_lv) + vdd->hmt_stat.hmt_bl_level = table->max_lv; + + left = 0; + right = table->tab_size - 1; + + while (left <= right) { + p = (left + right) / 2; + from = table->from[p]; + end = table->end[p]; + LCD_DEBUG("[%d] from(%d) end(%d) / %d\n", p, from, end, vdd->hmt_stat.hmt_bl_level); + + if (vdd->hmt_stat.hmt_bl_level >= from && vdd->hmt_stat.hmt_bl_level <= end) + break; + if (vdd->hmt_stat.hmt_bl_level < from) + right = p - 1; + else + left = p + 1; + }; + + // for elvess, vint etc.. which are using 74 steps. + vdd->br.interpolation_cd = vdd->hmt_stat.candela_level_hmt = table->cd[p]; + vdd->hmt_stat.cmd_idx_hmt = table->idx[p]; + + LCD_INFO("cd_idx (%d) cd_level (%d) \n", vdd->hmt_stat.cmd_idx_hmt, vdd->hmt_stat.candela_level_hmt); + + return; +} + +int ss_hmt_brightenss_packet_set( + struct samsung_display_driver_data *vdd) +{ + int cmd_cnt = 0; + int level_key = 0; + struct dsi_panel_cmd_set *set; + struct dsi_cmd_desc *packet = NULL; + struct dsi_panel_cmd_set *tx_cmd = NULL; + + LCD_DEBUG("++\n"); + + set_hmt_br_values(vdd); + + /* init packet */ + set = ss_get_cmds(vdd, TX_BRIGHT_CTRL); + if (SS_IS_CMDS_NULL(set)) { + LCD_ERR("No cmds for TX_BRIGHT_CTRL.. \n"); + return -EINVAL; + } + packet = set->cmds; + + if (vdd->smart_dimming_hmt_loaded_dsi) { + /* aid/aor B2 */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_aid_hmt)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_aid_hmt(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* elvss B5 */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_hmt)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_elvss_hmt(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* vint F4 */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_vint_hmt)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_vint_hmt(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + + /* gamma CA */ + if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_gamma_hmt)) { + level_key = false; + tx_cmd = vdd->panel_func.samsung_brightness_gamma_hmt(vdd, &level_key); + + update_packet_level_key_enable(vdd, packet, &cmd_cnt, level_key); + ss_update_brightness_packet(packet, &cmd_cnt, tx_cmd); + update_packet_level_key_disable(vdd, packet, &cmd_cnt, level_key); + } + } + + LCD_DEBUG("--\n"); + + return cmd_cnt; +} + +int ss_brightness_dcs_hmt(struct samsung_display_driver_data *vdd, + int level) +{ + struct dsi_panel_cmd_set *brightness_cmds = NULL; + int cmd_cnt; + int ret = 0; + + brightness_cmds = ss_get_cmds(vdd, TX_BRIGHT_CTRL); + + vdd->hmt_stat.hmt_bl_level = level; + LCD_ERR("[HMT] hmt_bl_level(%d)\n", vdd->hmt_stat.hmt_bl_level); + + cmd_cnt = ss_hmt_brightenss_packet_set(vdd); + + /* sending tx cmds */ + if (cmd_cnt) { + /* setting tx cmds cmt */ + brightness_cmds->count = cmd_cnt; + + /* generate single tx packet */ + ret = ss_single_transmission_packet(brightness_cmds); + + if (ret) { + ss_send_cmd(vdd, TX_BRIGHT_CTRL); + + LCD_INFO("idx(%d), cd_level(%d), hmt_bl_level(%d) itp : %s", + vdd->hmt_stat.cmd_idx_hmt, vdd->hmt_stat.candela_level_hmt, vdd->hmt_stat.hmt_bl_level, + vdd->panel_br_info.itp_mode == 0 ? "TABLE" : "FLASH'"); + } else + LCD_DEBUG("single_transmission_fail error\n"); + } else + LCD_INFO("level : %d skip\n", vdd->br.bl_level); + + return cmd_cnt; +} + +// TFT brightness + +void ss_brightness_tft_pwm(struct samsung_display_driver_data *vdd, int level) +{ + if (vdd == NULL) { + LCD_ERR("no PWM\n"); + return; + } + + if (ss_is_panel_off(vdd)) + return; + + vdd->br.bl_level = level; + + if (vdd->panel_func.samsung_brightness_tft_pwm) + vdd->panel_func.samsung_brightness_tft_pwm(vdd, level); +} + +void ss_tft_autobrightness_cabc_update(struct samsung_display_driver_data *vdd) +{ + LCD_INFO("\n"); + + switch (vdd->br.auto_level) { + case 0: + ss_cabc_update(vdd); + break; + case 1: + case 2: + case 3: + case 4: + ss_send_cmd(vdd, TX_CABC_ON); + break; + case 5: + case 6: + ss_send_cmd(vdd, TX_CABC_OFF); + break; + } +} + +void ss_read_mtp(struct samsung_display_driver_data *vdd, int addr, int len, int pos, u8 *buf) +{ + struct dsi_panel_cmd_set *rx_cmds; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return; + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return; + } + + if (addr > 0xFF || pos > 0xFF || len > 0xFF) { + LCD_ERR("unvalind para addr(%x) pos(%d) len(%d)\n", addr, pos, len); + return; + } + + rx_cmds = ss_get_cmds(vdd, RX_MTP_READ_SYSFS); + if (SS_IS_CMDS_NULL(rx_cmds)) { + LCD_ERR("No cmds for RX_MTP_READ_SYSFS.. \n"); + return; + } + + rx_cmds->cmds[0].msg.tx_buf[0] = addr; + rx_cmds->cmds[0].msg.tx_buf[1] = len; + rx_cmds->cmds[0].msg.tx_buf[2] = pos; + + rx_cmds->cmds[0].msg.rx_len = len; + rx_cmds->read_startoffset = pos; + + mutex_lock(&vdd->exclusive_tx.ex_tx_lock); + vdd->exclusive_tx.permit_frame_update = 1; + vdd->exclusive_tx.enable = 1; + + ss_set_exclusive_tx_packet(vdd, RX_MTP_READ_SYSFS, 1); + ss_set_exclusive_tx_packet(vdd, TX_LEVEL1_KEY_ENABLE, 1); + ss_set_exclusive_tx_packet(vdd, TX_LEVEL1_KEY_DISABLE, 1); + ss_set_exclusive_tx_packet(vdd, TX_REG_READ_POS, 1); + + ss_panel_data_read(vdd, RX_MTP_READ_SYSFS, buf, LEVEL1_KEY); + + ss_set_exclusive_tx_packet(vdd, RX_MTP_READ_SYSFS, 0); + ss_set_exclusive_tx_packet(vdd, TX_LEVEL1_KEY_ENABLE, 0); + ss_set_exclusive_tx_packet(vdd, TX_LEVEL1_KEY_DISABLE, 0); + ss_set_exclusive_tx_packet(vdd, TX_REG_READ_POS, 0); + + vdd->exclusive_tx.permit_frame_update = 0; + vdd->exclusive_tx.enable = 0; + wake_up_all(&vdd->exclusive_tx.ex_tx_waitq); + mutex_unlock(&vdd->exclusive_tx.ex_tx_lock); + + return; +} + +void ss_write_mtp(struct samsung_display_driver_data *vdd, u32 txlen, u32 *txval) +{ + struct dsi_panel_cmd_set *tx_cmds; + int i; + + if (IS_ERR_OR_NULL(vdd)) { + LCD_ERR("no vdd"); + return; + } + + if (!ss_is_ready_to_send_cmd(vdd)) { + LCD_ERR("Panel is not ready. Panel State(%d)\n", vdd->panel_state); + return; + } + + tx_cmds = ss_get_cmds(vdd, TX_MTP_WRITE_SYSFS); + if (SS_IS_CMDS_NULL(tx_cmds)) { + LCD_ERR("No cmds for RX_MTP_READ_SYSFS.. \n"); + return; + } + + for (i = 0; i < txlen; i++) + tx_cmds->cmds[0].msg.tx_buf[i] = txval[i]; + tx_cmds->cmds[0].msg.tx_len = txlen; + + ss_send_cmd(vdd, TX_MTP_WRITE_SYSFS); + + return; +} + +static BLOCKING_NOTIFIER_HEAD(panel_notifier_list); + +/** + * panel_notifier_register - register a client notifier + * @nb: notifier block to callback on events + */ +int panel_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&panel_notifier_list, nb); +} +EXPORT_SYMBOL(panel_notifier_register); + +/** + * panel_notifier_unregister - unregister a client notifier + * @nb: notifier block to callback on events + */ +int panel_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&panel_notifier_list, nb); +} +EXPORT_SYMBOL(panel_notifier_unregister); + +/** + * panel_notifier_call_chain - notify clients + * + */ +int panel_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&panel_notifier_list, val, v); +} +EXPORT_SYMBOL_GPL(panel_notifier_call_chain); + +static void ss_brightness_work(struct work_struct *work) +{ + struct samsung_display_driver_data *vdd = NULL; + struct brightness_info *br; + struct panel_bl_event_data bl_evt_data; + + br = container_of(work, struct brightness_info, br_work); + vdd = container_of(br, struct samsung_display_driver_data, br); + + LCD_DEBUG("brightness work ++\n"); + + /* notify clients of brightness change */ + bl_evt_data.bl_level = vdd->br.bl_level; + bl_evt_data.aor_data = vdd->br.aor_data; + panel_notifier_call_chain(PANEL_EVENT_BL_CHANGED, &bl_evt_data); + + LCD_DEBUG("brightness work --\n"); + + return; +} + +void ss_panel_init(struct dsi_panel *panel) +{ + struct samsung_display_driver_data *vdd; + enum ss_display_ndx ndx; + char panel_name[MAX_CMDLINE_PARAM_LEN]; + char panel_secondary_name[MAX_CMDLINE_PARAM_LEN]; + + /* compare panel name in command line and dsi_panel. + * primary panel: ndx = 0 + * secondary panel: ndx = 1 + */ + + LCD_INFO("++ \n"); + + ss_get_primary_panel_name_cmdline(panel_name); + ss_get_secondary_panel_name_cmdline(panel_secondary_name); + + if (!strcmp(panel->name, panel_name)) { + ndx = PRIMARY_DISPLAY_NDX; + } else if (!strcmp(panel->name, panel_secondary_name)) { + ndx = SECONDARY_DISPLAY_NDX; + } else { + /* If it fails to find panel name, it cannot turn on display, + * and this is critical error case... + */ + WARN(1, "fail to find panel name, panel=%s, cmdline=%s\n", + panel->name, panel_name); + return; + } + + /* TODO: after using component_bind in samsung_panel_init, + * it doesn't have to use vdd_data.. + * Remove vdd_data, and allocate vdd memory here. + * vdds will be managed by vdds_list... + */ + vdd = ss_get_vdd(ndx); + + ss_set_display_ndx(vdd, ndx); + + panel->panel_private = vdd; + vdd->msm_private = panel; + list_add(&vdd->vdd_list, &vdds_list); + + if (ss_panel_debug_init(vdd)) + LCD_ERR("Fail to create debugfs\n"); + + if (ss_smmu_debug_init(vdd)) + LCD_ERR("Fail to create smmu debug\n"); + + mutex_init(&vdd->vdd_lock); + mutex_init(&vdd->cmd_lock); + mutex_init(&vdd->bl_lock); + mutex_init(&vdd->ss_spi_lock); + + /* To guarantee ALPM ON or OFF mode change operation*/ + mutex_init(&vdd->panel_lpm.lpm_lock); + + /* To guarantee dynamic MIPI clock change*/ + mutex_init(&vdd->rf_info.vdd_dyn_mipi_lock); + + if (ss_is_cmd_mode(vdd)) { + vdd->panel_func.ss_event_osc_te_fitting = + ss_event_osc_te_fitting; + } + + vdd->panel_func.ss_event_frame_update = + ss_event_frame_update; + vdd->panel_func.ss_event_fb_event_callback = + ss_event_fb_event_callback; + vdd->panel_func.ss_event_esd_recovery_init = + ss_event_esd_recovery_init; + + vdd->manufacture_id_dsi = PBA_ID; + + vdd->panel_dead = false; + + if (IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_init)) + LCD_ERR("no samsung_panel_init fucn"); + else + vdd->panel_func.samsung_panel_init(vdd); + + if (vdd->mdnie.support_mdnie || vdd->support_cabc) { + if (ss_panel_attached(ndx)) + vdd->mdnie.mdnie_tune_state_dsi = init_dsi_tcon_mdnie_class(vdd); + } + + spin_lock_init(&vdd->esd_recovery.irq_lock); + + vdd->hmt_stat.hmt_enable = hmt_enable; + vdd->hmt_stat.hmt_reverse_update = hmt_reverse_update; + vdd->hmt_stat.hmt_bright_update = hmt_bright_update; + + ss_panel_attach_set(vdd, true); + + INIT_DELAYED_WORK(&vdd->delay_disp_on_work, samsung_display_delay_disp_on_work); + + /* Init Other line panel support */ + if (!IS_ERR_OR_NULL(vdd->panel_func.parsing_otherline_pdata) && ss_panel_attached(vdd->ndx)) { + if (!IS_ERR_OR_NULL(vdd->panel_func.get_panel_fab_type)) { + if (vdd->panel_func.get_panel_fab_type() == NEW_FB_PANLE_TYPE) { + LCD_ERR("parsing_otherline_pdata (%d)\n", vdd->panel_func.get_panel_fab_type()); + + INIT_DELAYED_WORK(&vdd->other_line_panel_support_work, read_panel_data_work_fn); + vdd->other_line_panel_support_workq = + create_singlethread_workqueue("other_line_panel_support_wq"); + + if (vdd->other_line_panel_support_workq) { + vdd->other_line_panel_work_cnt = OTHERLINE_WORKQ_CNT; + queue_delayed_work(vdd->other_line_panel_support_workq, + &vdd->other_line_panel_support_work, + msecs_to_jiffies(OTHERLINE_WORKQ_DEALY)); + } + } + } + } + + mutex_init(&vdd->exclusive_tx.ex_tx_lock); + vdd->exclusive_tx.enable = 0; + init_waitqueue_head(&vdd->exclusive_tx.ex_tx_waitq); + + ss_create_sysfs(vdd); + +#if defined(CONFIG_SEC_FACTORY) + vdd->is_factory_mode = true; +#endif + + /* parse display dtsi node */ + ss_panel_parse_dt(vdd); + + ss_spi_init(vdd); + + ss_dsi_poc_init(vdd); + + if (vdd->self_disp.init) + vdd->self_disp.init(vdd); + + if (vdd->copr.panel_init) + vdd->copr.panel_init(vdd); + + /* work thread for brightness change */ + vdd->br.br_wq = create_singlethread_workqueue("brightness_wq"); + if (vdd->br.br_wq == NULL) + LCD_ERR("failed to create read brightness workqueue..\n"); + + INIT_WORK(&vdd->br.br_work, (work_func_t)ss_brightness_work); + + LCD_INFO("-- \n"); +} diff --git a/drivers/gpu/drm/msm/samsung/ss_dsi_panel_common.h b/drivers/gpu/drm/msm/samsung/ss_dsi_panel_common.h new file mode 100755 index 000000000000..5df376f22139 --- /dev/null +++ b/drivers/gpu/drm/msm/samsung/ss_dsi_panel_common.h @@ -0,0 +1,1925 @@ +/* + * ================================================================= + * + * + * Description: samsung display common file + * + * Author: jb09.kim + * Company: Samsung Electronics + * + * ================================================================ + */ +/* + +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 SS_DSI_PANEL_COMMON_H +#define SS_DSI_PANEL_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include