1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-09-26 19:04:54 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

1802 lines
42 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk-provider.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/fb.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/lcd.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reboot.h>
#include <linux/rtc.h>
#include "../../../../../kernel/irq/internals.h"
#define USDM_ABD_MTK_UEVENT 1
#if defined(CONFIG_DRM_MEDIATEK)
#include <drm/drm_panel.h>
#if defined(USDM_ABD_MTK_UEVENT)
#include "../../mediatek/mtk_notify.h"
#endif
#endif
#if defined(CONFIG_MTK_FB)
#include "disp_lcm.h"
#include "disp_helper.h"
#if defined(USDM_ABD_MTK_UEVENT)
#include "mtk_notify.h"
#endif
#endif
#include "abd.h"
#if defined(CONFIG_SMCDSD_PANEL)
#include "usdm_board.h"
#include "usdm_notify.h"
#include "usdm_panel.h"
#endif
#undef pr_fmt
#define pr_fmt(fmt) "usdm: %s: " fmt, __func__
#define dbg_none(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)
#define dbg_info(fmt, ...) pr_info(fmt, ##__VA_ARGS__)
#define STREQ(a, b) (a && b && (*(a) == *(b)) && (strcmp((a), (b)) == 0))
#define STRNEQ(a, b) ((strncmp((a), (b), (strlen(a))) == 0))
#ifdef CONFIG_UML
#define rtc_time_to_tm(a, b) (memset(b, 0, sizeof(struct rtc_time)))
#else
#define rtc_time_to_tm(a, b) rtc_time64_to_tm(a, b)
#endif
#define abd_printf(m, ...) \
{ if (m) seq_printf(m, __VA_ARGS__); else dbg_info(__VA_ARGS__); } \
static LIST_HEAD(pending_list);
#if !defined(CONFIG_SMCDSD_PANEL)
struct mipi_dsi_lcd_common {
struct platform_device *pdev;
struct mipi_dsi_lcd_driver *drv;
unsigned int lcdconnected;
struct abd_protect abd;
};
struct mipi_dsi_lcd_common g_lcd_common;
unsigned int islcmconnected = 1;
/* usdm_notify */
static struct notifier_block usdm_nb_priority_max = { .priority = INT_MAX, };
static struct notifier_block usdm_nb_priority_min = { .priority = INT_MIN, };
static BLOCKING_NOTIFIER_HEAD(usdm_notifier_list);
static int usdm_register_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&usdm_notifier_list, nb);
}
/* usdm_board */
static struct device_node *of_find_usdm_board(struct device *dev)
{
return NULL;
}
static struct platform_device *of_find_device_by_path(const char *name)
{
struct device_node *np = NULL;
struct platform_device *pdev = NULL;
if (!name) {
dbg_info("name is null\n");
return NULL;
}
np = of_find_node_by_path(name);
if (!np) {
dbg_info("of_find_node_by_path fail for %s\n", name);
return NULL;
}
pdev = of_find_device_by_node(np);
if (!pdev) {
dbg_info("of_find_device_by_node fail\n");
return NULL;
}
return pdev;
}
#endif
struct platform_device *of_find_abd_dt_parent_platform_device(void)
{
return of_find_device_by_path("/panel");
}
struct platform_device *of_find_abd_container_platform_device(void)
{
return of_find_device_by_path("/panel");
}
static struct mipi_dsi_lcd_common *find_container(void)
{
struct mipi_dsi_lcd_common *container = NULL;
container = &g_lcd_common;
return container;
}
static struct abd_protect *find_abd(void)
{
struct mipi_dsi_lcd_common *container = find_container();
struct abd_protect *abd = NULL;
if (!container) {
dbg_info("find_container fail\n");
return NULL;
}
abd = &container->abd;
return abd;
}
static inline struct mipi_dsi_lcd_common *get_abd_container_of(struct abd_protect *abd)
{
struct mipi_dsi_lcd_common *container = container_of(abd, struct mipi_dsi_lcd_common, abd);
return container;
}
static int get_boot_frame_bypass(struct abd_protect *abd)
{
return !abd->islcmconnected;
}
static int get_frame_bypass(struct abd_protect *abd)
{
return !islcmconnected;
}
static void set_frame_bypass(struct abd_protect *abd, unsigned int bypass)
{
#if defined(THIS_IS_REDUNDANT_CODE)
#if defined(CONFIG_MTK_HIGH_FRAME_RATE)
struct mipi_dsi_lcd_common *container = get_abd_container_of(abd);
#endif
if (get_frame_bypass(abd) != bypass)
dbg_info("%d->%d\n", get_frame_bypass(abd), bypass);
islcmconnected = bypass ? 0 : abd->islcmconnected;
#if defined(CONFIG_MTK_HIGH_FRAME_RATE)
disp_helper_set_option(DISP_OPT_DYNAMIC_FPS, bypass ? 0 : !!container->lcm_params->dsi.dfps_num);
#endif
#endif
}
static int get_mipi_rw_bypass(struct abd_protect *abd)
{
struct mipi_dsi_lcd_common *container = get_abd_container_of(abd);
return !container->lcdconnected;
}
static void set_mipi_rw_bypass(struct abd_protect *abd, unsigned int bypass)
{
struct mipi_dsi_lcd_common *container = get_abd_container_of(abd);
if (get_mipi_rw_bypass(abd) != bypass)
dbg_info("%d->%d\n", get_mipi_rw_bypass(abd), bypass);
container->lcdconnected = bypass ? 0 : !!lcdtype;
}
static inline int get_boot_lcdtype(void)
{
return get_lk_boot_panel_id();
}
static inline unsigned int get_boot_lcdconnected(void)
{
return get_boot_lcdtype() ? 1 : 0;
}
static void save_boot_lcd_information(struct abd_protect *abd)
{
abd->islcmconnected = islcmconnected;
if (get_boot_lcdconnected() && get_frame_bypass(abd))
usdm_abd_save_str(abd, "islcmconnected abnormal");
}
#if defined(CONFIG_SMCDSD_PANEL)
static int usdm_abd_con_set_dummy(struct abd_protect *abd, unsigned int dummy)
{
struct mipi_dsi_lcd_common *container = get_abd_container_of(abd);
int ret = NOTIFY_DONE;
dbg_info("%d\n", dummy);
abd->lcd_driver = abd->lcd_driver ? abd->lcd_driver : container->drv;
if (dummy) {
container->drv = NULL;
ret = NOTIFY_STOP;
} else {
container->drv = abd->lcd_driver;
set_frame_bypass(abd, 0);
set_mipi_rw_bypass(abd, 0);
}
return ret;
}
#endif
static int usdm_abd_simple_write_to_buffer(char *ibuf, size_t sizeof_ibuf,
loff_t *ppos, const char *user_buf, size_t count)
{
int ret = 0;
loff_t pos = 0;
if (ppos && *ppos != 0)
return -EINVAL;
if (count == 0)
return -EINVAL;
if (count >= sizeof_ibuf)
return -ENOMEM;
memset(ibuf, 0, sizeof_ibuf);
ret = simple_write_to_buffer(ibuf, sizeof_ibuf, ppos ? ppos : &pos, user_buf, count);
if (ret < 0)
return ret;
ibuf[ret] = '\0';
ibuf = strim(ibuf);
if (!ibuf[0])
return -EFAULT;
if (!isascii(ibuf[0]))
return -EFAULT;
if (!isalnum(ibuf[0]))
return -EFAULT;
return 0;
}
static void usdm_abd_save_str_array(struct abd_protect *abd,
unsigned int bit_size, unsigned int value, const char **print)
{
struct abd_str *event = NULL;
struct str_log *event_log = NULL;
if (!abd)
abd = find_abd();
if (!abd || !abd->init_done)
return;
event = &abd->s_event;
event_log = &event->log[(event->count % ABD_LOG_MAX)];
memset(event_log, 0, sizeof(struct str_log));
event_log->stamp = local_clock();
event_log->ktime = ktime_get_real_seconds();
//event_log->value = 0;
//event_log->size = bit_size;
//memcpy(&event_log->print, print, sizeof(char *) * event_log->size);
memcpy(&event_log->print, print, sizeof(char *) * 1);
event->count++;
abd_printf(NULL, "usdm_abd: %s\n", print[0]);
}
void usdm_abd_save_str(struct abd_protect *abd, const char *print)
{
const char *print_array[BITS_PER_BYTE] = { print, };
usdm_abd_save_str_array(abd, 1, 0, print_array);
}
EXPORT_SYMBOL(usdm_abd_save_str);
static void __usdm_abd_print_bit(struct seq_file *m, struct bit_log *log)
{
struct timespec64 ts;
struct rtc_time tm;
unsigned int bit = 0;
char print_buf[ABD_STR_TMP] = {0, };
struct seq_file p = {
.buf = print_buf,
.size = sizeof(print_buf) - 1,
};
if (!m)
seq_puts(&p, "usdm_abd: ");
ts = ns_to_timespec64(log->stamp);
rtc_time_to_tm(log->ktime, &tm);
seq_printf(&p, "%d-%02d-%02d %02d:%02d:%02d / %lu.%06lu / 0x%0*X, ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
(unsigned long)ts.tv_sec, ts.tv_nsec / 1000, log->size >> 2, log->value);
for (bit = 0; bit < log->size; bit++) {
if (log->print[bit]) {
if (!bit || !log->print[bit - 1] || strcmp(log->print[bit - 1], log->print[bit]))
seq_printf(&p, "%s, ", log->print[bit]);
}
}
abd_printf(m, "%s\n", p.count ? p.buf : "");
}
void usdm_abd_save_bit(struct abd_protect *abd, unsigned int bit_size, unsigned int value, char **print)
{
struct abd_bit *first = NULL;
struct abd_bit *event = NULL;
struct bit_log *first_log = NULL;
struct bit_log *event_log = NULL;
if (!abd)
abd = find_abd();
if (!abd || !abd->init_done)
return;
first = &abd->b_first;
event = &abd->b_event;
first_log = &first->log[(first->count % ABD_LOG_MAX)];
event_log = &event->log[(event->count % ABD_LOG_MAX)];
memset(event_log, 0, sizeof(struct bit_log));
event_log->stamp = local_clock();
event_log->ktime = ktime_get_real_seconds();
event_log->value = value;
event_log->size = bit_size;
memcpy(&event_log->print, print, sizeof(char *) * event_log->size);
if (!first->count) {
memset(first_log, 0, sizeof(struct bit_log));
memcpy(first_log, event_log, sizeof(struct bit_log));
first->count++;
}
__usdm_abd_print_bit(NULL, event_log);
event->count++;
}
EXPORT_SYMBOL(usdm_abd_save_bit);
void usdm_abd_mask_bit(struct abd_protect *abd, unsigned int bit_size, unsigned int value, char **print, u32 invert)
{
unsigned int mask = 0, bit;
unsigned long print_tag;
char *print_new[BITS_PER_BYTE * sizeof(u32)] = {0, };
if (!abd)
abd = find_abd();
if (!abd || !abd->init_done)
return;
if (bit_size > (u16)ARRAY_SIZE(print_new)) {
dbg_info("bit_size(%d) > print_new(%d)\n", bit_size, (u16)ARRAY_SIZE(print_new));
bit_size = (u16)ARRAY_SIZE(print_new);
}
for (bit = 0; bit < bit_size; bit++) {
if (print[bit])
mask |= BIT(bit);
}
print_tag = value & mask;
print_tag = print_tag ^ invert;
if (!print_tag)
return;
for_each_set_bit(bit, &print_tag, bit_size) {
if (print[bit])
print_new[bit] = print[bit];
}
usdm_abd_save_bit(abd, bit_size, value, print_new);
}
EXPORT_SYMBOL(usdm_abd_mask_bit);
static void usdm_abd_save_pin(struct abd_protect *abd, struct abd_pin_info *pin, struct abd_pin *trace, bool on)
{
struct abd_pin *first = NULL;
struct pin_log *first_log = NULL;
struct pin_log *trace_log = NULL;
if (!abd || !abd->init_done)
return;
if (pin->index == ABD_PIN_LOG)
return;
first = &pin->p_first;
first_log = &first->log[(first->count) % ABD_LOG_MAX];
trace_log = &trace->log[(trace->count) % ABD_LOG_MAX];
trace_log->stamp = local_clock();
trace_log->ktime = ktime_get_real_seconds();
trace_log->level = pin->level;
trace_log->onoff = on;
if (!first->count) {
memset(first_log, 0, sizeof(struct pin_log));
memcpy(first_log, trace_log, sizeof(struct pin_log));
first->count++;
}
trace->count++;
}
static void usdm_abd_pin_clear_pending_bit(int irq)
{
struct irq_desc *desc;
desc = irq_to_desc(irq);
if (desc->irq_data.chip->irq_ack) {
desc->irq_data.chip->irq_ack(&desc->irq_data);
desc->istate &= ~IRQS_PENDING;
}
}
static void usdm_abd_pin_enable_irq(int irq, unsigned int on)
{
if (on) {
usdm_abd_pin_clear_pending_bit(irq);
enable_irq(irq);
} else {
usdm_abd_pin_clear_pending_bit(irq);
disable_irq_nosync(irq);
}
}
static struct abd_pin_info *usdm_abd_find_pin_info(struct abd_protect *abd, int id, int *is_gpio)
{
struct abd_pin_info *pin = NULL;
int i = 0, flag = -EINVAL;
for (i = 0; i < ABD_PIN_MAX; i++) {
if (abd->pin[i].gpio && abd->pin[i].gpio == id) {
pin = &abd->pin[i];
flag = 1;
break;
} else if (abd->pin[i].irq && abd->pin[i].irq == id) {
pin = &abd->pin[i];
flag = 0;
break;
}
}
if (is_gpio)
*is_gpio = flag;
return pin;
}
static void __usdm_abd_pin_enable(struct abd_protect *abd, struct abd_pin_info *pin, bool on)
{
struct abd_pin *trace = &pin->p_lcdon;
struct abd_pin *event = &pin->p_event;
struct abd_sub_info *sub_info = NULL;
struct list_head *chain_list = NULL;
if (!abd || !abd->init_done || !pin)
return;
if (!pin->gpio && !pin->irq)
return;
if (pin->enable == on)
return;
pin->enable = on;
pin->level = gpio_get_value(pin->gpio);
dbg_info("on: %d, %s(%3d,%3d,%d) level: %d, count: %d(event: %d) %s\n",
on, pin->name, pin->gpio, pin->irq, pin->desc->depth, pin->level, trace->count, event->count,
(pin->level == pin->active_level) ? (pin->index == ABD_PIN_LOG ? "undefined" : "abnormal") : "normal");
if (pin->name && !strcmp(pin->name, "pcd"))
set_frame_bypass(abd, (pin->level == pin->active_level) ? 1 : 0);
chain_list = on ? &pin->enter_chain : &pin->leave_chain;
if (pin->level == pin->active_level) {
usdm_abd_save_pin(abd, pin, trace, on);
if (pin->bug_flag == 1 || pin->bug_flag & IRQ_TYPE_LEVEL_MASK) {
dbg_info("%s has bug_flag(%d)\n", pin->name, pin->bug_flag);
BUG();
}
list_for_each_entry(sub_info, chain_list, node) {
if (sub_info->handler)
sub_info->handler(pin->gpio, sub_info->chain_data);
}
}
if (pin->level != pin->active_level && on)
pin->active_depth = 0;
if (pin->irq)
usdm_abd_pin_enable_irq(pin->irq, on);
}
void usdm_abd_enable(struct abd_protect *abd, unsigned int enable)
{
unsigned int i = 0;
if (!abd)
return;
if (abd->enable == enable)
dbg_none("already %s\n", enable ? "enabled" : "disabled");
if (abd->enable != enable)
dbg_info("bypass: rw(%d)frame(%d)\n", get_mipi_rw_bypass(abd), get_frame_bypass(abd));
if (!abd->enable && enable) { /* off -> on */
//abd->f_lcdon.lcdon_flag = 0;
//abd->u_lcdon.lcdon_flag = 0;
}
abd->enable = enable;
for (i = 0; i < ABD_PIN_MAX; i++)
__usdm_abd_pin_enable(abd, &abd->pin[i], enable);
}
static irqreturn_t usdm_abd_handler(int irq, void *dev_id)
{
struct abd_protect *abd = (struct abd_protect *)dev_id;
struct abd_pin_info *pin = NULL;
struct abd_pin *trace = NULL;
struct abd_pin *lcdon = NULL;
struct abd_sub_info *sub_info = NULL;
unsigned int i = 0;
spin_lock(&abd->slock);
for (i = 0; i < ABD_PIN_MAX; i++) {
pin = &abd->pin[i];
trace = &pin->p_event;
lcdon = &pin->p_lcdon;
if (pin && irq == pin->irq)
break;
}
if (i == ABD_PIN_MAX) {
dbg_info("irq(%d) is not in abd\n", irq);
goto exit;
}
pin->level = gpio_get_value(pin->gpio);
usdm_abd_save_pin(abd, pin, trace, 1);
dbg_info("%s(%3d,%3d) level: %d, count: %d(lcdon: %d) %s\n",
pin->name, pin->gpio, pin->irq, pin->level, trace->count, lcdon->count,
(pin->level == pin->active_level) ? (pin->index == ABD_PIN_LOG ? "undefined" : "abnormal") : "normal");
if (pin->bug_flag == 1 || pin->bug_flag & IRQ_TYPE_EDGE_BOTH) {
dbg_info("%s has bug_flag(%d)\n", pin->name, pin->bug_flag);
BUG();
}
if (pin->active_level != pin->level)
goto exit;
if (i == ABD_PIN_PCD)
set_frame_bypass(abd, 1);
list_for_each_entry(sub_info, &pin->event_chain, node) {
if (sub_info->handler)
sub_info->handler(irq, sub_info->chain_data);
}
exit:
spin_unlock(&abd->slock);
return IRQ_HANDLED;
}
#if defined(USDM_ABD_MTK_UEVENT) && defined(CONFIG_MTK_FB)
static void __usdm_abd_blank(struct abd_protect *abd)
{
struct fb_info *fbinfo = registered_fb[0];
dbg_info("+ noti_uevent\n");
if (fbinfo) {
if (!lock_fb_info(fbinfo)) {
dbg_info("fblock is failed\n");
return;
}
unlock_fb_info(fbinfo);
}
noti_uevent_user(&uevent_data, 1);
dbg_info("- noti_uevent\n");
}
#elif defined(USDM_ABD_MTK_UEVENT) && defined(CONFIG_DRM_MEDIATEK)
static void __usdm_abd_blank(struct abd_protect *abd)
{
struct platform_device *pdev = of_find_device_by_path("dsi0");
struct device *dev = &pdev->dev;
struct device_node *remote_node, *endpoint;
struct drm_panel *panel;
struct drm_device *drm;
dbg_info("+ noti_uevent\n");
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (!endpoint) {
dbg_info("of_graph_get_next_endpoint fail");
return;
}
remote_node = of_graph_get_remote_port_parent(endpoint);
if (!remote_node) {
dbg_info("of_graph_get_remote_port_parent fail");
return;
}
panel = of_drm_find_panel(remote_node);
of_node_put(remote_node);
if (!panel) {
dbg_info("of_drm_find_panel fail");
return;
}
drm = panel->drm;
if (!drm) {
dbg_info("drm is null\n");
return;
}
noti_uevent_user_by_drm(drm, 1);
dbg_info("- noti_uevent\n");
}
#elif defined(CONFIG_MTK_FB)
static int __usdm_abd_fb_blank(struct fb_info *info, int blank)
{
struct fb_event evdata = {0, };
int fbblank = 0;
int ret = 0;
if (!lock_fb_info(info)) {
dbg_info("fblock is failed\n");
return ret;
}
dbg_info("+\n");
fbblank = blank;
evdata.info = info;
evdata.data = &fbblank;
info->flags |= FBINFO_MISC_USEREVENT;
usdm_notifier_call_chain(USDM_EARLY_EVENT_BLANK, &evdata);
ret = info->fbops->fb_blank(blank, info);
usdm_notifier_call_chain(USDM_EVENT_BLANK, &evdata);
info->flags &= ~FBINFO_MISC_USEREVENT;
unlock_fb_info(info);
dbg_info("-\n");
return 0;
}
static void __usdm_abd_blank(struct abd_protect *abd)
{
struct fb_info *fbinfo = registered_fb[0];
dbg_info("+\n");
if (fbinfo) {
if (!lock_fb_info(fbinfo)) {
dbg_info("fblock is failed\n");
return;
}
unlock_fb_info(fbinfo);
}
__usdm_abd_fb_blank(fbinfo, FB_BLANK_POWERDOWN);
__usdm_abd_fb_blank(fbinfo, FB_BLANK_UNBLANK);
dbg_info("-\n");
}
#elif defined(CONFIG_DRM_MEDIATEK)
static void __usdm_abd_blank(struct abd_protect *abd)
{
struct mipi_dsi_lcd_common *container = get_abd_container_of(abd);
dbg_info("+\n");
if (container->drv) {
call_drv_ops(container, panel_reset, 0);
call_drv_ops(container, panel_power, 0);
}
usdm_abd_con_set_dummy(abd, 1);
dbg_info("-\n");
}
#else
static void __usdm_abd_blank(struct abd_protect *abd) {}
#endif
static void usdm_abd_blank_work(struct work_struct *work)
{
struct abd_protect *abd = container_of(work, struct abd_protect, blank_work);
dbg_info("+\n");
__usdm_abd_blank(abd);
dbg_info("-\n");
}
void usdm_abd_blank(struct abd_protect *abd)
{
if (!abd->init_done || !abd->boot_done)
return;
dbg_info("blank_flag: %u\n", abd->blank_flag);
if (!abd->blank_flag)
queue_work(abd->blank_workqueue, &abd->blank_work);
abd->blank_flag = 1;
}
static irqreturn_t usdm_abd_refresh_handler(int id, void *dev_id)
{
struct abd_protect *abd = (struct abd_protect *)dev_id;
struct abd_pin_info *pin = NULL;
int is_gpio = 0;
pin = usdm_abd_find_pin_info(abd, id, &is_gpio);
if (!pin) {
dbg_info("invalid id(%d)\n", id);
return IRQ_HANDLED;
}
dbg_info("%s(%3d) %s(%3d,%3d,%d) active_depth: %d\n",
is_gpio ? "gpio" : "irq", id, pin->name, pin->gpio, pin->irq, pin->desc->depth, pin->active_depth);
if (is_gpio)
pin->active_depth++;
if (pin->active_depth < 3) /* todo */
usdm_abd_blank(abd);
return IRQ_HANDLED;
}
static irqreturn_t usdm_abd_detatch_handler(int id, void *dev_id)
{
struct abd_protect *abd = (struct abd_protect *)dev_id;
dbg_info("bypass: rw(%d)frame(%d)\n", get_mipi_rw_bypass(abd), get_frame_bypass(abd));
set_frame_bypass(abd, 1);
set_mipi_rw_bypass(abd, 1);
return IRQ_HANDLED;
}
int usdm_abd_pin_register_refresh_handler(struct abd_protect *abd, int irq)
{
BUG_ON(!abd);
BUG_ON(!abd->init_done);
return usdm_abd_pin_register_handler(abd, irq, usdm_abd_refresh_handler, abd);
}
enum {
CHAIN_TYPE_NONE = 0x00000000,
CHAIN_TYPE_LEVEL_ENTER = 0x00000001,
CHAIN_TYPE_LEVEL_LEAVE = 0x00000002,
CHAIN_TYPE_LEVEL_MASK = (CHAIN_TYPE_LEVEL_ENTER | CHAIN_TYPE_LEVEL_LEAVE),
CHAIN_TYPE_EVENT = 0x00000004,
CHAIN_TYPE_MAX,
};
static const char *CHAINE_TYPE_NAME[CHAIN_TYPE_MAX] = {
"CHAIN_TYPE_NONE",
"CHAIN_TYPE_LEVEL_ENTER",
"CHAIN_TYPE_LEVEL_LEAVE",
"CHAIN_TYPE_LEVEL_MASK",
"CHAIN_TYPE_EVENT",
};
static int chain_name_to_type(const char *name)
{
int i;
if (!name)
return -EINVAL;
for (i = 0; i < CHAIN_TYPE_MAX; i++) {
if (STRNEQ(CHAINE_TYPE_NAME[i], name))
return i;
}
return -EFAULT;
}
static struct list_head *chain_type_to_list(struct abd_pin_info *pin, int type)
{
struct list_head *chain_list = NULL;
switch (type) {
case CHAIN_TYPE_LEVEL_ENTER:
chain_list = &pin->enter_chain;
break;
case CHAIN_TYPE_LEVEL_LEAVE:
chain_list = &pin->leave_chain;
break;
case CHAIN_TYPE_EVENT:
chain_list = &pin->event_chain;
break;
}
return chain_list;
}
static irq_handler_t handler_name_to_handler(const char *string)
{
irq_handler_t handler = NULL;
if (STRNEQ("abd_func_refresh", string))
handler = usdm_abd_refresh_handler;
else if (STRNEQ("abd_func_detatch", string))
handler = usdm_abd_detatch_handler;
return handler;
}
static int usdm_abd_pin_register_handler_chain(struct abd_protect *abd,
int id, irq_handler_t func, void *dev_id, int chain_type)
{
struct abd_sub_info *sub_info = NULL, *tmp = NULL;
struct list_head *chain_list = NULL;
struct abd_pin_info *pin = usdm_abd_find_pin_info(abd, id, NULL);
if (!pin) {
dbg_info("usdm_abd_find_pin_info fail %d\n", id);
return -EINVAL;
}
if (gpio_get_value(pin->gpio) == pin->active_level) {
dbg_info("%s(%d) is already %s(%d)\n", pin->name, gpio_get_value(pin->gpio),
(pin->active_level) ? "high" : "low", pin->level);
return -EINVAL;
}
chain_list = chain_type_to_list(pin, chain_type);
list_for_each_entry_safe(sub_info, tmp, chain_list, node) {
WARN(sub_info->handler == func && sub_info->chain_data == dev_id,
"already registered handler\n");
}
sub_info = kzalloc(sizeof(struct abd_sub_info), GFP_KERNEL);
if (!sub_info)
return -EINVAL;
sub_info->handler = func;
sub_info->chain_data = dev_id;
list_add_tail(&sub_info->node, chain_list);
dbg_info("%s(%3d,%3d) %s done\n", pin->name, pin->gpio, pin->irq, CHAINE_TYPE_NAME[chain_type]);
return 0;
}
static int __of_usdm_abd_pin_register_handler_chain(struct abd_protect *abd,
irq_handler_t func, int gpio, const char *chain_name)
{
int ret = 0, chain_type;
struct abd_pin_info *pin = usdm_abd_find_pin_info(abd, gpio, NULL);
char ibuf[80];
char *pbuf, *token;
if (!pin) {
dbg_info("usdm_abd_find_pin_info fail gpio: %d\n", gpio);
return -EINVAL;
}
ret = usdm_abd_simple_write_to_buffer(ibuf, sizeof(ibuf), NULL, chain_name, strlen(chain_name));
if (ret < 0) {
dbg_info("simple_write_to_buffer fail: %d\n", ret);
return ret;
}
pbuf = ibuf;
while ((token = strsep(&pbuf, " ,"))) {
if (*token == '\0')
continue;
chain_type = chain_name_to_type(token);
if (chain_type < 0)
continue;
switch (chain_type) {
case CHAIN_TYPE_LEVEL_ENTER:
case CHAIN_TYPE_LEVEL_LEAVE:
case CHAIN_TYPE_EVENT:
ret = usdm_abd_pin_register_handler_chain(abd, gpio, func, abd, chain_type);
if (ret < 0)
dbg_info("usdm_abd_pin_register_handler_chain fail %d\n", ret);
break;
}
}
return ret;
}
static void of_usdm_abd_pin_register_handler_chain(struct abd_protect *abd)
{
struct device_node *np = NULL;
struct platform_device *pdev = NULL;
struct property *pp;
irq_handler_t func = NULL;
const char *info = NULL;
const char *subinfo = NULL;
int gpio = 0, i, count;
pdev = of_find_abd_dt_parent_platform_device();
np = of_find_usdm_board(pdev ? &pdev->dev : NULL);
if (!np) {
dbg_none("of_find_usdm_board fail\n");
return;
}
for_each_property_of_node(np, pp) {
if (!pp->name)
continue;
if (!strlen(pp->name))
continue;
dbg_none("%s\n", pp->name);
if (!STRNEQ("abd_func", pp->name))
continue;
count = of_property_count_strings(np, pp->name);
if (count < 0 || !count || count % 2) {
dbg_info("%s count %d invalid\n", pp->name, count);
continue;
}
count /= 2;
for (i = 0; i < count; i++) {
of_property_read_string_index(np, pp->name, i * 2, &info);
of_property_read_string_index(np, pp->name, i * 2 + 1, &subinfo);
if (!info || !subinfo)
continue;
if (!strlen(info) || !strlen(subinfo))
continue;
dbg_none("info(%s) subinfo(%s)\n", info, subinfo);
gpio = of_get_named_gpio_flags(np, info, 0, NULL);
if (!gpio_is_valid(gpio)) {
dbg_info("gpio_is_valid fail, gpio: %s, %d\n", info, gpio);
continue;
}
func = handler_name_to_handler(pp->name);
if (!func)
continue;
__of_usdm_abd_pin_register_handler_chain(abd, func, gpio, subinfo);
}
}
}
int usdm_abd_pin_register_handler(struct abd_protect *abd, int id, irq_handler_t func, void *dev_id)
{
struct abd_pin_info *pin = NULL;
int chain_type, is_gpio = 0;
if (!id) {
dbg_info("id(%d) invalid\n", id);
return -EINVAL;
}
if (!func) {
dbg_info("func invalid\n");
return -EINVAL;
}
abd = abd ? abd : find_abd();
if (!abd) {
dbg_info("abd invalid\n");
return -EINVAL;
}
pin = usdm_abd_find_pin_info(abd, id, &is_gpio);
if (!pin) {
dbg_info("id(%d) is not in abd\n", id);
return -EINVAL;
}
chain_type = is_gpio ? CHAIN_TYPE_LEVEL_ENTER : CHAIN_TYPE_EVENT;
usdm_abd_pin_register_handler_chain(abd, id, func, dev_id, chain_type);
return 0;
}
#if defined(CONFIG_SMCDSD_PANEL)
static int usdm_abd_con_fb_notifier_callback(struct notifier_block *this,
unsigned long event, void *data)
{
struct abd_protect *abd = container_of(this, struct abd_protect, con_fb_notifier);
struct fb_event *evdata = data;
struct abd_pin_info *pin = NULL;
int fb_blank, gpio_active;
switch (event) {
case USDM_EVENT_BLANK:
case USDM_EARLY_EVENT_BLANK:
break;
default:
return NOTIFY_DONE;
}
fb_blank = *(int *)evdata->data;
if (evdata->info->node)
return NOTIFY_DONE;
if (!abd->boot_done) {
dbg_info("boot_done\n");
abd->boot_done = 1;
return NOTIFY_DONE;
}
if (fb_blank != FB_BLANK_UNBLANK)
return NOTIFY_DONE;
pin = &abd->pin[ABD_PIN_CON];
gpio_active = (gpio_get_value(pin->gpio) == pin->active_level) ? 1 : 0;
dbg_info("%s\n", gpio_active ? "disconnected" : "connected");
if (IS_EARLY(event))
return usdm_abd_con_set_dummy(abd, gpio_active);
else if (IS_AFTER(event) && gpio_active)
return usdm_abd_con_set_dummy(abd, gpio_active);
return NOTIFY_DONE;
}
#else
static int usdm_abd_con_fb_notifier_callback(struct notifier_block *this,
unsigned long event, void *data)
{
return NOTIFY_DONE;
}
#endif
void usdm_abd_con_register(struct abd_protect *abd)
{
struct platform_device *pdev = NULL;
struct device_node *np = NULL;
pdev = of_find_abd_dt_parent_platform_device();
np = of_find_usdm_board(pdev ? &pdev->dev : NULL);
if (!of_find_property(np, "gpio_con", NULL))
return;
abd->con_fb_notifier.priority = usdm_nb_priority_max.priority;
abd->con_fb_notifier.notifier_call = usdm_abd_con_fb_notifier_callback;
usdm_register_notifier(&abd->con_fb_notifier);
if (abd->pin[ABD_PIN_CON].irq) {
usdm_abd_pin_register_handler(abd, abd->pin[ABD_PIN_CON].irq, usdm_abd_detatch_handler, abd);
usdm_abd_pin_register_handler(abd, abd->pin[ABD_PIN_CON].irq, usdm_abd_refresh_handler, abd);
}
}
int usdm_abd_register_printer(struct abd_protect *abd, int (*show)(struct seq_file *, void *), void *data)
{
struct abd_sub_info *sub_info = NULL, *tmp = NULL;
struct abd_pending *pending;
abd = abd ? abd : find_abd();
if (!abd) {
pending = kzalloc(sizeof(struct abd_pending), GFP_KERNEL);
pending->printer.show = show;
list_add_tail(&pending->node, &pending_list);
dbg_info("abd invalid\n");
return -EINVAL;
}
if (!abd->init_done) {
pending = kzalloc(sizeof(struct abd_pending), GFP_KERNEL);
pending->printer.show = show;
list_add_tail(&pending->node, &pending_list);
dbg_info("abd init_done invalid\n");
return -EINVAL;
}
list_for_each_entry_safe(sub_info, tmp, &abd->printer_list, node) {
WARN(sub_info->show == show && sub_info->chain_data == data,
"already registered printer\n");
}
sub_info = kzalloc(sizeof(struct abd_sub_info), GFP_KERNEL);
if (!sub_info)
return -EINVAL;
sub_info->show = show;
sub_info->chain_data = data;
list_add_tail(&sub_info->node, &abd->printer_list);
dbg_info("printer is registered\n");
return 0;
}
static void __usdm_abd_print_pin(struct seq_file *m, struct abd_pin *trace)
{
struct timespec64 ts;
struct rtc_time tm;
struct pin_log *log;
unsigned int i = 0;
if (!trace->count)
return;
abd_printf(m, "%s total count: %d\n", trace->name, trace->count);
for (i = 0; i < ABD_LOG_MAX; i++) {
log = &trace->log[i];
if (!log->stamp)
continue;
ts = ns_to_timespec64(log->stamp);
rtc_time_to_tm(log->ktime, &tm);
abd_printf(m, "%d-%02d-%02d %02d:%02d:%02d / %lu.%06lu / level: %d onoff: %d\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
(unsigned long)ts.tv_sec, ts.tv_nsec / 1000, log->level, log->onoff);
}
}
static void usdm_abd_print_pin(struct seq_file *m, struct abd_pin_info *pin)
{
if (!pin->irq && !pin->gpio)
return;
if (!pin->p_first.count)
return;
abd_printf(m, "[%s]\n", pin->name);
__usdm_abd_print_pin(m, &pin->p_first);
__usdm_abd_print_pin(m, &pin->p_lcdon);
__usdm_abd_print_pin(m, &pin->p_event);
}
static void usdm_abd_print_str(struct seq_file *m, struct abd_str *trace)
{
unsigned int log_max = ABD_LOG_MAX, i, idx;
struct timespec64 ts;
struct rtc_time tm;
int start = trace->count - 1;
struct str_log *log;
char print_buf[ABD_STR_TMP] = {0, };
struct seq_file p = {
.buf = print_buf,
.size = sizeof(print_buf) - 1,
};
if (start < 0)
return;
abd_printf(m, "==========_STR_DEBUG_==========\n");
start = (start > log_max) ? start - log_max + 1 : 0;
for (i = 0; i < log_max; i++) {
idx = (start + i) % ABD_LOG_MAX;
log = &trace->log[idx];
if (!log->stamp)
continue;
ts = ns_to_timespec64(log->stamp);
rtc_time_to_tm(log->ktime, &tm);
if (i && !(i % 2)) {
abd_printf(m, "%s\n", p.buf);
p.count = 0;
memset(print_buf, 0, sizeof(print_buf));
}
seq_printf(&p, "%d-%02d-%02d %02d:%02d:%02d / %lu.%06lu / %-20s ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
(unsigned long)ts.tv_sec, ts.tv_nsec / 1000, log->print[0]);
}
abd_printf(m, "%s\n", p.count ? p.buf : "");
}
static void usdm_abd_print_bit(struct seq_file *m, struct abd_bit *trace)
{
struct bit_log *log;
unsigned int i = 0;
if (!trace->count)
return;
abd_printf(m, "%s total count: %d\n", trace->name, trace->count);
for (i = 0; i < ABD_LOG_MAX; i++) {
log = &trace->log[i];
if (!log->stamp)
continue;
__usdm_abd_print_bit(m, log);
}
}
void usdm_abd_simple_print(struct abd_protect *abd, struct seq_file *m, void *unused)
{
unsigned int i = 0;
abd = abd ? abd : find_abd();
if (!abd) {
dbg_info("find_abd fail\n");
return;
}
abd_printf(m, "==========_USDM_ABD_==========\n");
abd_printf(m, "bypass rw(%d)frame(%d,%d) lcdtype(%6X)\n",
get_mipi_rw_bypass(abd), get_boot_frame_bypass(abd), get_frame_bypass(abd), get_boot_lcdtype());
for (i = 0; i < ABD_PIN_MAX; i++) {
if (abd->pin[i].p_first.count) {
abd_printf(m, "==========_PIN_DEBUG_==========\n");
break;
}
}
for (i = 0; i < ABD_PIN_MAX; i++)
usdm_abd_print_pin(m, &abd->pin[i]);
if (abd->b_first.count) {
abd_printf(m, "==========_BIT_DEBUG_==========\n");
usdm_abd_print_bit(m, &abd->b_first);
usdm_abd_print_bit(m, &abd->b_event);
}
usdm_abd_print_str(m, &abd->s_event);
}
EXPORT_SYMBOL(usdm_abd_simple_print);
static int usdm_abd_reboot_notifier(struct notifier_block *this,
unsigned long code, void *unused)
{
struct abd_protect *abd = container_of(this, struct abd_protect, reboot_notifier);
dbg_info("++ %lu\n", code);
usdm_abd_enable(abd, 0);
usdm_abd_simple_print(abd, NULL, NULL);
dbg_info("-- %lu\n", code);
return NOTIFY_DONE;
}
static int of_usdm_abd_pin_append_extra_information(struct abd_protect *abd,
struct abd_pin_info *pin, struct device_node *np, const char *dts_name)
{
struct of_phandle_args gpiospec;
struct of_phandle_args out_args;
int ret, count, extra;
unsigned int type;
ret = of_parse_phandle_with_args(np, dts_name, "#gpio-cells", 0, &gpiospec);
if (ret < 0) {
dbg_info("of_parse_phandle_with_args fail %d\n", ret);
return 0;
}
count = of_property_count_u32_elems(np, dts_name);
if (count < 0 || count > MAX_PHANDLE_ARGS) {
dbg_info("of_parse_phandle_with_args count(%d)\n", count);
return 0;
}
if (count <= gpiospec.args_count + 1)
return 0;
ret = of_property_read_u32_array(np, dts_name, &out_args.args[0], count);
if (ret < 0) {
dbg_info("of_property_read_u32_array fail %d\n", ret);
return 0;
}
extra = gpiospec.args_count + 1;
type = out_args.args[extra];
if (type == IRQ_TYPE_NONE) {
dbg_info("%s IRQ_TYPE_NONE\n", dts_name);
pin->irq = 0;
}
return 0;
}
static int of_usdm_abd_pin_register_handler(struct abd_protect *abd, struct abd_pin_info *pin,
char *keyword, irq_handler_t func)
{
int ret = 0, gpio = 0, to_irq = 0;
enum of_gpio_flags flags;
struct device_node *np = NULL;
struct platform_device *pdev = NULL;
unsigned int irqf_type = IRQF_TRIGGER_RISING;
struct abd_pin *trace = &pin->p_lcdon;
char *prefix_gpio = "gpio_";
char dts_name[10] = {0, };
if (strlen(keyword) + strlen(prefix_gpio) >= sizeof(dts_name)) {
dbg_info("%s is too log(%zu)\n", keyword, strlen(keyword));
goto exit;
}
scnprintf(dts_name, sizeof(dts_name), "%s%s", prefix_gpio, keyword);
pdev = of_find_abd_dt_parent_platform_device();
np = of_find_usdm_board(pdev ? &pdev->dev : NULL);
if (!of_find_property(np, dts_name, NULL))
goto exit;
gpio = of_get_named_gpio_flags(np, dts_name, 0, &flags);
if (!gpio_is_valid(gpio)) {
dbg_info("gpio_is_valid fail, gpio: %s, %d\n", dts_name, gpio);
goto exit;
}
dbg_info("found %s(%d) success\n", dts_name, gpio);
to_irq = gpio_to_irq(gpio);
dbg_info("gpio_to_irq, gpio: %d, irq: %d\n", gpio, to_irq);
pin->gpio = gpio;
pin->irq = (to_irq > 0) ? to_irq : 0;
if (pin->irq)
of_usdm_abd_pin_append_extra_information(abd, pin, np, dts_name);
pin->desc = (pin->irq) ? irq_to_desc(pin->irq) : kzalloc(sizeof(struct irq_desc), GFP_KERNEL);
pin->active_level = !(flags & OF_GPIO_ACTIVE_LOW);
irqf_type = (flags & OF_GPIO_ACTIVE_LOW) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
dbg_info("%s is active %s%s\n", keyword, pin->active_level ? "high" : "low",
(pin->irq) ? ((irqf_type == IRQF_TRIGGER_RISING) ? ", rising" : ", falling") : "");
pin->name = keyword;
pin->p_first.name = "first";
pin->p_lcdon.name = "lcdon";
pin->p_event.name = "event";
pin->level = gpio_get_value(pin->gpio);
if (pin->level == pin->active_level) {
dbg_info("%s(%d) is already %s(%d)\n", keyword, pin->gpio,
(pin->active_level) ? "high" : "low", pin->level);
usdm_abd_save_pin(abd, pin, trace, 1);
if (pin->name && !strcmp(pin->name, "pcd"))
set_frame_bypass(abd, 1);
}
if (pin->gpio) {
INIT_LIST_HEAD(&pin->enter_chain);
INIT_LIST_HEAD(&pin->leave_chain);
}
if (pin->irq) {
irq_set_irq_type(pin->irq, irqf_type);
irq_set_status_flags(pin->irq, _IRQ_NOAUTOEN);
usdm_abd_pin_clear_pending_bit(pin->irq);
if (devm_request_irq(&pdev->dev, pin->irq, func, irqf_type, keyword, abd)) {
dbg_info("failed to request irq for %s\n", keyword);
/* pin->gpio = 0; */
pin->irq = 0;
goto exit;
}
INIT_LIST_HEAD(&pin->event_chain);
}
exit:
return ret;
}
#if defined(CONFIG_SMCDSD_PANEL)
static int usdm_abd_pin_early_notifier_callback(struct notifier_block *this,
unsigned long event, void *data)
{
struct abd_protect *abd = container_of(this, struct abd_protect, pin_early_notifier);
struct fb_event *evdata = data;
int fb_blank;
switch (event) {
case USDM_EARLY_EVENT_BLANK:
case USDM_EARLY_EVENT_DOZE:
break;
default:
return NOTIFY_DONE;
}
fb_blank = *(int *)evdata->data;
if (evdata->info->node)
return NOTIFY_DONE;
if (!abd->boot_done) {
dbg_info("boot_done\n");
abd->boot_done = 1;
}
flush_workqueue(abd->blank_workqueue);
usdm_abd_enable(abd, 0);
return NOTIFY_DONE;
}
static int usdm_abd_pin_after_notifier_callback(struct notifier_block *this,
unsigned long event, void *data)
{
struct abd_protect *abd = container_of(this, struct abd_protect, pin_after_notifier);
struct fb_event *evdata = data;
struct abd_pin_info *pin = NULL;
unsigned int i = 0;
int fb_blank;
switch (event) {
case USDM_EVENT_BLANK:
case USDM_EVENT_DOZE:
break;
default:
return NOTIFY_DONE;
}
fb_blank = *(int *)evdata->data;
if (evdata->info->node)
return NOTIFY_DONE;
if (fb_blank == FB_BLANK_UNBLANK) {
usdm_abd_enable(abd, 1);
if (abd->blank_flag) {
dbg_info("blank_flag: %u\n", abd->blank_flag);
abd->blank_flag = 0;
}
} else if (fb_blank == FB_BLANK_POWERDOWN) {
for (i = 0; i < ABD_PIN_MAX; i++) {
pin = &abd->pin[i];
if (pin && pin->irq)
usdm_abd_pin_clear_pending_bit(pin->irq);
}
}
return NOTIFY_DONE;
}
#else
static int usdm_abd_pin_early_notifier_callback(struct notifier_block *this,
unsigned long event, void *data)
{
return NOTIFY_DONE;
}
static int usdm_abd_pin_after_notifier_callback(struct notifier_block *this,
unsigned long event, void *data)
{
return NOTIFY_DONE;
}
#endif
static void usdm_abd_pin_register(struct abd_protect *abd)
{
spin_lock_init(&abd->slock);
abd->pin[ABD_PIN_PCD].index = ABD_PIN_PCD;
abd->pin[ABD_PIN_DET].index = ABD_PIN_DET;
abd->pin[ABD_PIN_ERR].index = ABD_PIN_ERR;
abd->pin[ABD_PIN_CON].index = ABD_PIN_CON;
abd->pin[ABD_PIN_LOG].index = ABD_PIN_LOG;
of_usdm_abd_pin_register_handler(abd, &abd->pin[ABD_PIN_PCD], "pcd", usdm_abd_handler);
of_usdm_abd_pin_register_handler(abd, &abd->pin[ABD_PIN_DET], "det", usdm_abd_handler);
of_usdm_abd_pin_register_handler(abd, &abd->pin[ABD_PIN_ERR], "err", usdm_abd_handler);
of_usdm_abd_pin_register_handler(abd, &abd->pin[ABD_PIN_CON], "con", usdm_abd_handler);
of_usdm_abd_pin_register_handler(abd, &abd->pin[ABD_PIN_LOG], "log", usdm_abd_handler);
abd->pin_early_notifier.notifier_call = usdm_abd_pin_early_notifier_callback;
abd->pin_early_notifier.priority = usdm_nb_priority_max.priority - 1;
usdm_register_notifier(&abd->pin_early_notifier);
abd->pin_after_notifier.notifier_call = usdm_abd_pin_after_notifier_callback;
abd->pin_after_notifier.priority = usdm_nb_priority_min.priority + 1;
usdm_register_notifier(&abd->pin_after_notifier);
of_usdm_abd_pin_register_handler_chain(abd);
}
static int usdm_abd_misc_show(struct seq_file *m, void *unused)
{
struct abd_protect *abd = m->private;
struct abd_sub_info *sub_info = NULL;
mutex_lock(&abd->misc_lock);
usdm_abd_simple_print(abd, m, unused);
list_for_each_entry(sub_info, &abd->printer_list, node) {
if (!sub_info->show)
continue;
m->private = sub_info->chain_data;
sub_info->show(m, sub_info->chain_data);
}
m->private = abd;
mutex_unlock(&abd->misc_lock);
return 0;
}
static int usdm_abd_misc_open(struct inode *inode, struct file *file)
{
struct miscdevice *dev = file->private_data;
struct abd_protect *abd = container_of(dev, struct abd_protect, misc_entry);
file->private_data = NULL;
return single_open(file, usdm_abd_misc_show, abd);
}
static const struct file_operations usdm_abd_misc_fops = {
.read = seq_read,
.release = single_release,
.open = usdm_abd_misc_open,
};
static void usdm_abd_register_fops(struct abd_protect *abd)
{
int ret = 0;
abd->misc_entry.minor = MISC_DYNAMIC_MINOR;
abd->misc_entry.name = "sec_display_debug";
abd->misc_entry.fops = &usdm_abd_misc_fops;
abd->misc_entry.parent = NULL;
mutex_init(&abd->misc_lock);
ret = misc_register(&abd->misc_entry);
if (ret < 0)
dbg_info("misc_register fail(%d)\n", ret);
}
static ssize_t usdm_abd_debugfs_write(struct file *f, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct abd_protect *abd = ((struct seq_file *)f->private_data)->private;
char ibuf[80] = {0, };
int ret = 0;
if (IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP))
return count;
//if (!IS_ENABLED(CONFIG_USDM_LCD_DEBUG))
//return count;
ret = usdm_abd_simple_write_to_buffer(ibuf, sizeof(ibuf), ppos, user_buf, count);
if (ret < 0)
return ret;
dbg_info("%s\n", ibuf);
if (STRNEQ("blank", ibuf)) {
usdm_abd_blank(abd);
} else if (STRNEQ("con_blank", ibuf)) {
if (!abd->con_fb_notifier.priority)
usdm_abd_con_register(abd);
usdm_abd_blank(abd);
}
return count;
}
static int usdm_abd_debugfs_show(struct seq_file *m, void *unused)
{
struct abd_protect *abd = m->private;
usdm_abd_simple_print(abd, m, unused);
return 0;
}
static int usdm_abd_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, usdm_abd_debugfs_show, inode->i_private);
}
static const struct file_operations usdm_abd_debugfs_fops = {
.read = seq_read,
.release = single_release,
.write = usdm_abd_debugfs_write,
.open = usdm_abd_debugfs_open,
};
static void usdm_abd_register_debugfs(struct abd_protect *abd)
{
struct dentry *abd_debugfs_root = NULL;
struct dentry *dd_debugfs_root;
if (IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP))
return;
//if (!IS_ENABLED(CONFIG_USDM_LCD_DEBUG))
//return;
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return;
dbg_info("++\n");
dd_debugfs_root = debugfs_lookup("dd", NULL);
dd_debugfs_root = dd_debugfs_root ? dd_debugfs_root : debugfs_create_dir("dd", NULL);
abd_debugfs_root = dd_debugfs_root ? debugfs_create_dir("abd", dd_debugfs_root) : NULL;
abd->debugfs_root = abd_debugfs_root;
if (!abd->debugfs_root) {
dbg_info("debugfs_create_dir fail\n");
return;
}
debugfs_create_file("_debug", 0644, abd_debugfs_root, abd, &usdm_abd_debugfs_fops);
debugfs_create_u32("pcd_bug", 0644, abd_debugfs_root, (u32 *)&abd->pin[ABD_PIN_PCD].bug_flag);
debugfs_create_u32("det_bug", 0644, abd_debugfs_root, (u32 *)&abd->pin[ABD_PIN_DET].bug_flag);
debugfs_create_u32("err_bug", 0644, abd_debugfs_root, (u32 *)&abd->pin[ABD_PIN_ERR].bug_flag);
debugfs_create_u32("con_bug", 0644, abd_debugfs_root, (u32 *)&abd->pin[ABD_PIN_CON].bug_flag);
debugfs_create_u32("log_bug", 0644, abd_debugfs_root, (u32 *)&abd->pin[ABD_PIN_LOG].bug_flag);
dbg_info("-- entity was registered\n");
}
static void usdm_abd_register(struct abd_protect *abd)
{
dbg_info("++\n");
abd->b_first.name = "first";
abd->b_event.name = abd->s_event.name = "event";
abd->reboot_notifier.notifier_call = usdm_abd_reboot_notifier;
register_reboot_notifier(&abd->reboot_notifier);
INIT_LIST_HEAD(&abd->printer_list);
INIT_WORK(&abd->blank_work, usdm_abd_blank_work);
abd->blank_workqueue = create_singlethread_workqueue("abd_blank_workqueue");
if (!abd->blank_workqueue)
dbg_info("create_singlethread_workqueue fail\n");
usdm_abd_register_fops(abd);
usdm_abd_register_debugfs(abd);
dbg_info("-- entity was registered\n");
}
int usdm_abd_init(void)
{
struct abd_protect *abd = find_abd();
struct abd_pending *pending = NULL;
if (!abd) {
dbg_info("find_abd fail\n");
return 0;
}
usdm_abd_register(abd);
abd->init_done = 1;
save_boot_lcd_information(abd);
if (get_boot_lcdconnected())
usdm_abd_pin_register(abd);
else
set_frame_bypass(abd, 1);
dbg_info("bypass rw(%d)frame(%d,%d) lcdtype(%6X)\n",
get_mipi_rw_bypass(abd), get_boot_frame_bypass(abd), get_frame_bypass(abd), get_boot_lcdtype());
usdm_abd_enable(abd, 1);
#if defined(CONFIG_MEDIATEK_SOLUTION) || defined(CONFIG_ARCH_MEDIATEK)
usdm_abd_register_printer(abd, mtkfb_debug_show, NULL);
#endif
list_for_each_entry(pending, &pending_list, node) {
usdm_abd_register_printer(abd, pending->printer.show, NULL);
}
BUILD_BUG_ON_MSG(sizeof(struct abd_protect) >> 20, "sizeof(struct abd_protect) is too big");
return 0;
}
EXPORT_SYMBOL(usdm_abd_init);