blob: d959a2555165ce7c96be22906118cdde6ddffdfc [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/clk.h>
#include <linux/of_device.h>
#include <linux/compat.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
#include <linux/sched/clock.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_address.h>
#endif
#include <linux/amlogic/pm.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
#endif
#include "lcd_reg.h"
#include "lcd_common.h"
#include "lcd_tcon.h"
#ifdef CONFIG_AMLOGIC_VPU
#include <linux/amlogic/media/vpu/vpu.h>
#endif
#include <linux/amlogic/gki_module.h>
#define LCD_CDEV_NAME "lcd"
unsigned int lcd_debug_print_flag;
unsigned int lcd_tcon_bin_path_index;
/* for driver global resource init:
* 0: none
* n: initialized cnt
*/
static unsigned char lcd_global_init_flag;
static unsigned int lcd_drv_init_state;
static struct aml_lcd_drv_s *lcd_driver[LCD_MAX_DRV];
static struct workqueue_struct *lcd_workqueue;
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
struct mutex lcd_vout_mutex;
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
struct mutex lcd_power_mutex;
EXPORT_SYMBOL(lcd_power_mutex);
int lcd_vout_serve_bypass;
struct lcd_cdev_s {
dev_t devno;
struct class *class;
};
static struct lcd_cdev_s *lcd_cdev;
static char lcd_propname[LCD_MAX_DRV][24] = {"mipi_0", "null", "null"};
static char lcd_panel_name[LCD_MAX_DRV][24] = {"null", "null", "null"};
#define LCD_VSYNC_NONE_INTERVAL msecs_to_jiffies(500)
/* *********************************************************
* lcd config define
* *********************************************************
*/
static struct lcd_boot_ctrl_s lcd_boot_ctrl_config[LCD_MAX_DRV] = {
{
.lcd_type = LCD_TYPE_MAX,
.lcd_bits = 0,
.advanced_flag = 0,
.init_level = 0,
},
{
.lcd_type = LCD_TYPE_MAX,
.lcd_bits = 0,
.advanced_flag = 0,
.init_level = 0,
},
{
.lcd_type = LCD_TYPE_MAX,
.lcd_bits = 0,
.advanced_flag = 0,
.init_level = 0,
}
};
static struct lcd_debug_ctrl_s lcd_debug_ctrl_config = {
.debug_print_flag = 0,
.debug_test_pattern = 0,
.debug_para_source = 0,
.debug_lcd_mode = 0,
};
static struct aml_lcd_drv_s *lcd_driver_add(int index)
{
struct aml_lcd_drv_s *pdrv = NULL;
if (index >= LCD_MAX_DRV)
return NULL;
pdrv = kzalloc(sizeof(*pdrv), GFP_KERNEL);
if (!pdrv)
return NULL;
pdrv->index = index;
/* default config */
strcpy(pdrv->config.propname, lcd_propname[index]);
strcpy(pdrv->config.basic.model_name, lcd_panel_name[index]);
pdrv->config.basic.lcd_type = LCD_TYPE_MAX;
pdrv->config.power.power_on_step[0].type = LCD_POWER_TYPE_MAX;
pdrv->config.power.power_off_step[0].type = LCD_POWER_TYPE_MAX;
pdrv->config.pinmux_flag = 0xff;
pdrv->config.backlight_index = 0xff;
/* default vinfo */
pdrv->vinfo.mode = VMODE_LCD;
pdrv->vinfo.frac = 0;
pdrv->vinfo.viu_color_fmt = COLOR_FMT_RGB444;
pdrv->vinfo.viu_mux = ((index << 4) | VIU_MUX_ENCL);
pdrv->vinfo.vout_device = NULL;
/* boot ctrl */
pdrv->boot_ctrl = &lcd_boot_ctrl_config[index];
pdrv->debug_ctrl = &lcd_debug_ctrl_config;
pdrv->config.custom_pinmux = pdrv->boot_ctrl->custom_pinmux;
return pdrv;
}
struct aml_lcd_drv_s *aml_lcd_get_driver(int index)
{
if (index >= LCD_MAX_DRV)
return NULL;
return lcd_driver[index];
}
EXPORT_SYMBOL(aml_lcd_get_driver);
inline void lcd_queue_work(struct work_struct *work)
{
if (lcd_workqueue)
queue_work(lcd_workqueue, work);
else
schedule_work(work);
}
inline void lcd_queue_delayed_work(struct delayed_work *dwork, int ms)
{
if (lcd_workqueue)
queue_delayed_work(lcd_workqueue, dwork, msecs_to_jiffies(ms));
else
schedule_delayed_work(dwork, msecs_to_jiffies(ms));
}
/* ********************************************************* */
static void lcd_power_ctrl(struct aml_lcd_drv_s *pdrv, int status)
{
struct lcd_power_step_s *power_step;
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
struct lcd_extern_driver_s *edrv;
struct lcd_extern_dev_s *edev;
#endif
unsigned int i, index, wait;
int value = -1;
if (pdrv->lcd_pxp) {
LCDPR("[%d]: %s: lcd_pxp bypass\n", pdrv->index, __func__);
return;
}
LCDPR("[%d]: %s: %d\n", pdrv->index, __func__, status);
i = 0;
while (i < LCD_PWR_STEP_MAX) {
if (status)
power_step = &pdrv->config.power.power_on_step[i];
else
power_step = &pdrv->config.power.power_off_step[i];
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: power_ctrl: %d, step %d\n",
pdrv->index, status, i);
LCDPR("[%d]: %s: type=%d, index=%d, value=%d, delay=%d\n",
pdrv->index, __func__,
power_step->type, power_step->index,
power_step->value, power_step->delay);
}
switch (power_step->type) {
case LCD_POWER_TYPE_CPU:
index = power_step->index;
lcd_cpu_gpio_set(pdrv, index, power_step->value);
break;
case LCD_POWER_TYPE_PMU:
LCDPR("to do\n");
break;
case LCD_POWER_TYPE_SIGNAL:
if (status)
pdrv->driver_init(pdrv);
else
pdrv->driver_disable(pdrv);
break;
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
case LCD_POWER_TYPE_EXTERN:
index = power_step->index;
edrv = lcd_extern_get_driver(pdrv->index);
edev = lcd_extern_get_dev(edrv, index);
if (!edrv || !edev)
break;
if (status) {
if (edev->power_on)
edev->power_on(edrv, edev);
else
LCDERR("[%d]: no ext_%d power on\n", pdrv->index, index);
} else {
if (edev->power_off)
edev->power_off(edrv, edev);
else
LCDERR("[%d]: no ext_%d power off\n", pdrv->index, index);
}
break;
#endif
case LCD_POWER_TYPE_WAIT_GPIO:
index = power_step->index;
lcd_cpu_gpio_set(pdrv, index, LCD_GPIO_INPUT);
LCDPR("[%d]: lcd_power_type_wait_gpio wait\n",
pdrv->index);
for (wait = 0; wait < power_step->delay; wait++) {
value = lcd_cpu_gpio_get(pdrv, index);
if (value == power_step->value) {
LCDPR("[%d]: wait_gpio %d ok\n",
pdrv->index, value);
break;
}
mdelay(1);
}
if (wait == power_step->delay) {
LCDERR("[%d]: wait_gpio %d timeout!\n",
pdrv->index, value);
}
break;
case LCD_POWER_TYPE_CLK_SS:
break;
default:
break;
}
if (power_step->type != LCD_POWER_TYPE_WAIT_GPIO &&
power_step->delay > 0)
lcd_delay_ms(power_step->delay);
i++;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: %d finished\n", pdrv->index, __func__, status);
}
static void lcd_dlg_switch_mode(struct aml_lcd_drv_s *pdrv)
{
struct lcd_power_step_s *power_step;
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
struct lcd_extern_driver_s *edrv;
struct lcd_extern_dev_s *edev;
#endif
unsigned int i = 0, index;
unsigned long long local_time[3];
LCDPR("[%d]: %s\n", pdrv->index, __func__);
while (i < LCD_PWR_STEP_MAX) {
power_step = &pdrv->config.power.power_on_step[i];
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
switch (power_step->type) {
case LCD_POWER_TYPE_SIGNAL:
if (pdrv->config.basic.lcd_type == LCD_P2P) {
local_time[0] = sched_clock();
lcd_tcon_reload(pdrv);
local_time[1] = sched_clock();
pdrv->config.cus_ctrl.tcon_reload_time =
local_time[1] - local_time[0];
}
break;
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
case LCD_POWER_TYPE_EXTERN:
local_time[0] = sched_clock();
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: power_ctrl step %d\n",
pdrv->index, i);
LCDPR("[%d]: %s: type=%d, index=%d, value=%d, delay=%d\n",
pdrv->index, __func__,
power_step->type, power_step->index,
power_step->value, power_step->delay);
}
index = power_step->index;
edrv = lcd_extern_get_driver(pdrv->index);
edev = lcd_extern_get_dev(edrv, index);
if (!edrv || !edev)
break;
if (edev->power_on)
edev->power_on(edrv, edev);
else
LCDERR("[%d]: no ext_%d power on\n", pdrv->index, index);
local_time[1] = sched_clock();
pdrv->config.cus_ctrl.level_shift_time = local_time[1] - local_time[0];
break;
#endif
default:
break;
}
i++;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s finished\n", pdrv->index, __func__);
}
static void lcd_dlg_power_ctrl(struct aml_lcd_drv_s *pdrv, int status)
{
struct lcd_power_step_s *power_step;
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
struct lcd_extern_driver_s *edrv;
struct lcd_extern_dev_s *edev;
#endif
unsigned int i, index;
unsigned long long local_time[3];
LCDPR("[%d]: %s: %d\n", pdrv->index, __func__, status);
i = 0;
while (i < LCD_PWR_STEP_MAX) {
if (status)
power_step = &pdrv->config.power.power_on_step[i];
else
power_step = &pdrv->config.power.power_off_step[i];
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
switch (power_step->type) {
case LCD_POWER_TYPE_SIGNAL:
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: power_ctrl: %d, step %d\n",
pdrv->index, status, i);
LCDPR("[%d]: %s: type=%d, index=%d, value=%d, delay=%d\n",
pdrv->index, __func__,
power_step->type, power_step->index,
power_step->value, power_step->delay);
}
if (status)
pdrv->driver_init(pdrv);
else
pdrv->driver_disable(pdrv);
break;
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
case LCD_POWER_TYPE_EXTERN:
local_time[0] = sched_clock();
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: power_ctrl: %d, step %d\n",
pdrv->index, status, i);
LCDPR("[%d]: %s: type=%d, index=%d, value=%d, delay=%d\n",
pdrv->index, __func__,
power_step->type, power_step->index,
power_step->value, power_step->delay);
}
index = power_step->index;
edrv = lcd_extern_get_driver(pdrv->index);
edev = lcd_extern_get_dev(edrv, index);
if (!edrv || !edev)
break;
if (status) {
if (edev->power_on)
edev->power_on(edrv, edev);
else
LCDERR("[%d]: no ext_%d power on\n", pdrv->index, index);
} else {
if (edev->power_off)
edev->power_off(edrv, edev);
else
LCDERR("[%d]: no ext_%d power off\n", pdrv->index, index);
}
local_time[1] = sched_clock();
pdrv->config.cus_ctrl.level_shift_time = local_time[1] - local_time[0];
break;
#endif
default:
break;
}
i++;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: %d finished\n", pdrv->index, __func__, status);
}
static void lcd_power_encl_on(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
if (pdrv->status & LCD_STATUS_ENCL_ON) {
LCDPR("[%d]: %s: on already\n", pdrv->index, __func__);
mutex_unlock(&lcd_vout_mutex);
return;
}
pdrv->driver_init_pre(pdrv);
pdrv->status |= LCD_STATUS_ENCL_ON;
/* vsync_none_timer conditional enabled to save cpu loading */
if (pdrv->viu_sel == LCD_VIU_SEL_NONE) {
if (pdrv->vsync_none_timer_flag == 0) {
pdrv->vs_none_timer.expires =
jiffies + LCD_VSYNC_NONE_INTERVAL;
add_timer(&pdrv->vs_none_timer);
pdrv->vsync_none_timer_flag = 1;
LCDPR("[%d]: add vs_none_timer handler\n", pdrv->index);
}
} else {
if (pdrv->vsync_none_timer_flag) {
del_timer_sync(&pdrv->vs_none_timer);
pdrv->vsync_none_timer_flag = 0;
}
}
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_power_encl_off(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
if (!(pdrv->status & LCD_STATUS_ENCL_ON)) {
LCDPR("[%d]: %s: off already\n", pdrv->index, __func__);
mutex_unlock(&lcd_vout_mutex);
return;
}
pdrv->status &= ~LCD_STATUS_ENCL_ON;
pdrv->driver_disable_post(pdrv);
if (pdrv->vsync_none_timer_flag) {
del_timer_sync(&pdrv->vs_none_timer);
pdrv->vsync_none_timer_flag = 0;
}
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_dlg_power_if_on(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
if (!(pdrv->status & LCD_STATUS_IF_ON)) {
if (pdrv->config.cus_ctrl.dlg_flag) {
if (pdrv->config.cus_ctrl.dlg_flag == 1)
lcd_power_ctrl(pdrv, 1);
else if (pdrv->config.cus_ctrl.dlg_flag == 2)
lcd_dlg_power_ctrl(pdrv, 1);
} else {
lcd_power_ctrl(pdrv, 1);
}
pdrv->status |= LCD_STATUS_IF_ON;
}
pdrv->config.change_flag = 0;
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_dlg_power_if_off(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
if (pdrv->status & LCD_STATUS_IF_ON) {
pdrv->status &= ~LCD_STATUS_IF_ON;
if (pdrv->config.cus_ctrl.dlg_flag) {
if (pdrv->config.cus_ctrl.dlg_flag == 1)
lcd_power_ctrl(pdrv, 0);
else if (pdrv->config.cus_ctrl.dlg_flag == 2)
lcd_dlg_power_ctrl(pdrv, 0);
} else {
lcd_power_ctrl(pdrv, 0);
}
}
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_power_if_on(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
if (!(pdrv->status & LCD_STATUS_IF_ON)) {
lcd_power_ctrl(pdrv, 1);
pdrv->status |= LCD_STATUS_IF_ON;
}
pdrv->config.change_flag = 0;
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_power_if_off(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
if (pdrv->status & LCD_STATUS_IF_ON) {
pdrv->status &= ~LCD_STATUS_IF_ON;
lcd_power_ctrl(pdrv, 0);
}
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_power_screen_black(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
lcd_screen_black(pdrv);
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_power_screen_restore(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
lcd_screen_restore(pdrv);
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_module_reset(struct aml_lcd_drv_s *pdrv)
{
mutex_lock(&lcd_vout_mutex);
pdrv->status &= ~LCD_STATUS_ON;
lcd_power_ctrl(pdrv, 0);
msleep(500);
pdrv->driver_init_pre(pdrv);
lcd_power_ctrl(pdrv, 1);
pdrv->status |= LCD_STATUS_ON;
pdrv->config.change_flag = 0;
lcd_screen_restore(pdrv);
LCDPR("[%d]: clear mute\n", pdrv->index);
mutex_unlock(&lcd_vout_mutex);
}
static void lcd_screen_restore_work(struct work_struct *work)
{
unsigned long flags = 0;
int ret = 0;
struct aml_lcd_drv_s *pdrv;
unsigned long long local_time[3];
local_time[0] = sched_clock();
pdrv = container_of(work, struct aml_lcd_drv_s, screen_restore_work);
mutex_lock(&lcd_power_mutex);
reinit_completion(&pdrv->vsync_done);
spin_lock_irqsave(&pdrv->isr_lock, flags);
if (pdrv->unmute_count_test)
pdrv->mute_count = pdrv->unmute_count_test;
else
pdrv->mute_count = 4;
pdrv->mute_flag = 1;
spin_unlock_irqrestore(&pdrv->isr_lock, flags);
ret = wait_for_completion_timeout(&pdrv->vsync_done,
msecs_to_jiffies(500));
if (!ret)
LCDPR("vmode switch: wait_for_completion_timeout\n");
lcd_screen_restore(pdrv);
mutex_unlock(&lcd_power_mutex);
local_time[1] = sched_clock();
pdrv->config.cus_ctrl.unmute_time = local_time[1] - local_time[0];
}
static void lcd_lata_resume_work(struct work_struct *work)
{
struct aml_lcd_drv_s *pdrv;
pdrv = container_of(work, struct aml_lcd_drv_s, late_resume_work);
mutex_lock(&lcd_power_mutex);
aml_lcd_notifier_call_chain(LCD_EVENT_ENABLE, (void *)pdrv);
lcd_if_enable_retry(pdrv);
LCDPR("[%d]: %s finished\n", pdrv->index, __func__);
mutex_unlock(&lcd_power_mutex);
}
static void lcd_auto_test_delayed(struct work_struct *p_work)
{
struct delayed_work *d_work;
struct aml_lcd_drv_s *pdrv;
d_work = container_of(p_work, struct delayed_work, work);
pdrv = container_of(d_work, struct aml_lcd_drv_s,
test_delayed_work);
LCDPR("[%d]: %s\n", pdrv->index, __func__);
mutex_lock(&lcd_power_mutex);
aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, (void *)pdrv);
mutex_unlock(&lcd_power_mutex);
}
static void lcd_auto_test_func(struct aml_lcd_drv_s *pdrv)
{
pdrv->test_state = pdrv->auto_test;
lcd_queue_delayed_work(&pdrv->test_delayed_work, 20000);
}
static int lcd_vsync_print_cnt;
static inline void lcd_vsync_handler(struct aml_lcd_drv_s *pdrv)
{
unsigned long flags = 0;
if (!pdrv)
return;
switch (pdrv->config.basic.lcd_type) {
case LCD_MIPI:
#ifdef CONFIG_AMLOGIC_LCD_TABLET
if (pdrv->config.control.mipi_cfg.dread) {
if (pdrv->config.control.mipi_cfg.dread->flag) {
lcd_mipi_test_read(pdrv, pdrv->config.control.mipi_cfg.dread);
pdrv->config.control.mipi_cfg.dread->flag = 0;
}
}
#endif
break;
case LCD_VBYONE:
if (pdrv->vbyone_vsync_handler)
pdrv->vbyone_vsync_handler(pdrv);
break;
case LCD_MLVDS:
case LCD_P2P:
lcd_tcon_vsync_isr(pdrv);
break;
default:
break;
}
spin_lock_irqsave(&pdrv->isr_lock, flags);
if (pdrv->mute_flag) {
if (pdrv->mute_count > 0) {
pdrv->mute_count--;
} else if (pdrv->mute_count == 0) {
complete(&pdrv->vsync_done);
pdrv->mute_flag = 0;
}
}
if (pdrv->test_flag != pdrv->test_state) {
pdrv->test_state = pdrv->test_flag;
lcd_debug_test(pdrv, pdrv->test_state);
}
spin_unlock_irqrestore(&pdrv->isr_lock, flags);
if (lcd_vsync_print_cnt++ >= LCD_DEBUG_VSYNC_INTERVAL) {
lcd_vsync_print_cnt = 0;
if (lcd_debug_print_flag & LCD_DBG_PR_ISR) {
LCDPR("[%d]: %s: viu_sel: %d, mute_count: %d\n",
pdrv->index, __func__, pdrv->viu_sel, pdrv->mute_count);
}
}
}
static irqreturn_t lcd_vsync_isr(int irq, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if (!pdrv)
return IRQ_HANDLED;
if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0)
return IRQ_HANDLED;
if (pdrv->viu_sel == 1) {
lcd_vsync_handler(pdrv);
if (pdrv->vsync_cnt++ >= 65536)
pdrv->vsync_cnt = 0;
}
return IRQ_HANDLED;
}
static irqreturn_t lcd_vsync2_isr(int irq, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if (!pdrv)
return IRQ_HANDLED;
if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0)
return IRQ_HANDLED;
if (pdrv->viu_sel == 2) {
lcd_vsync_handler(pdrv);
if (pdrv->vsync_cnt++ >= 65536)
pdrv->vsync_cnt = 0;
}
return IRQ_HANDLED;
}
static irqreturn_t lcd_vsync3_isr(int irq, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if (!pdrv)
return IRQ_HANDLED;
if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0)
return IRQ_HANDLED;
if (pdrv->viu_sel == 3) {
lcd_vsync_handler(pdrv);
if (pdrv->vsync_cnt++ >= 65536)
pdrv->vsync_cnt = 0;
}
return IRQ_HANDLED;
}
static void lcd_vsync_none_timer_handler(struct timer_list *timer)
{
struct aml_lcd_drv_s *pdrv = from_timer(pdrv, timer, vs_none_timer);
if (pdrv->data->chip_type == LCD_CHIP_C3)
return;
if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0)
goto lcd_vsync_none_timer_handler_end;
if (pdrv->vsync_cnt == pdrv->vsync_cnt_previous) {
lcd_wait_vsync(pdrv);
lcd_vsync_handler(pdrv);
}
pdrv->vsync_cnt_previous = pdrv->vsync_cnt;
lcd_vsync_none_timer_handler_end:
if (pdrv->vsync_none_timer_flag) {
pdrv->vs_none_timer.expires =
jiffies + LCD_VSYNC_NONE_INTERVAL;
add_timer(&pdrv->vs_none_timer);
}
}
/* ****************************************
* lcd notify
* ****************************************
*/
static int lcd_power_encl_on_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_ENCL_ON) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
if (pdrv->status & LCD_STATUS_ENCL_ON) {
LCDPR("[%d]: lcd is already enabled\n", pdrv->index);
return NOTIFY_OK;
}
lcd_power_encl_on(pdrv);
return NOTIFY_OK;
}
static struct notifier_block lcd_power_encl_on_nb = {
.notifier_call = lcd_power_encl_on_notifier,
.priority = LCD_PRIORITY_POWER_ENCL_ON,
};
static int lcd_power_encl_off_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_ENCL_OFF) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
if (pdrv->status & LCD_STATUS_IF_ON) {
LCDPR("[%d]: %s: force power off interface ahead\n", pdrv->index, __func__);
lcd_power_if_off(pdrv);
}
lcd_power_encl_off(pdrv);
return NOTIFY_OK;
}
static struct notifier_block lcd_power_encl_off_nb = {
.notifier_call = lcd_power_encl_off_notifier,
.priority = LCD_PRIORITY_POWER_ENCL_OFF,
};
static int lcd_dlg_switch_mode_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_DLG_SWITCH_MODE) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
if (pdrv->status & LCD_STATUS_ENCL_ON) {
mutex_lock(&lcd_vout_mutex);
lcd_dlg_switch_mode(pdrv);
mutex_unlock(&lcd_vout_mutex);
} else {
LCDERR("[%d]: %s: can't power on when controller is off\n",
pdrv->index, __func__);
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block lcd_dlg_switch_mode_nb = {
.notifier_call = lcd_dlg_switch_mode_notifier,
.priority = LCD_PRIORITY_DLG_SWITCH_MODE,
};
static int lcd_dlg_power_if_on_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_DLG_POWER_ON) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
if (pdrv->status & LCD_STATUS_IF_ON) {
LCDPR("[%d]: lcd interface is already enabled\n", pdrv->index);
return NOTIFY_OK;
}
if (pdrv->status & LCD_STATUS_ENCL_ON) {
lcd_dlg_power_if_on(pdrv);
} else {
LCDERR("[%d]: %s: can't power on when controller is off\n",
pdrv->index, __func__);
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block lcd_dlg_power_if_on_nb = {
.notifier_call = lcd_dlg_power_if_on_notifier,
.priority = LCD_PRIORITY_DLG_POWER_IF_ON,
};
static int lcd_dlg_power_if_off_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_DLG_POWER_OFF) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
lcd_dlg_power_if_off(pdrv);
return NOTIFY_OK;
}
static struct notifier_block lcd_dlg_power_if_off_nb = {
.notifier_call = lcd_dlg_power_if_off_notifier,
.priority = LCD_PRIORITY_DLG_POWER_IF_OFF,
};
static int lcd_power_if_on_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_IF_ON) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
if (pdrv->status & LCD_STATUS_IF_ON) {
LCDPR("[%d]: lcd interface is already enabled\n", pdrv->index);
return NOTIFY_OK;
}
if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) {
LCDPR("[%d]: %s: force power on controller ahead\n", pdrv->index, __func__);
lcd_power_encl_on(pdrv);
}
lcd_power_if_on(pdrv);
return NOTIFY_OK;
}
static struct notifier_block lcd_power_if_on_nb = {
.notifier_call = lcd_power_if_on_notifier,
.priority = LCD_PRIORITY_POWER_IF_ON,
};
static int lcd_power_if_off_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
if ((event & LCD_EVENT_IF_OFF) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
lcd_power_if_off(pdrv);
return NOTIFY_OK;
}
static struct notifier_block lcd_power_if_off_nb = {
.notifier_call = lcd_power_if_off_notifier,
.priority = LCD_PRIORITY_POWER_IF_OFF,
};
static int lcd_power_screen_black_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
unsigned long long local_time[3];
local_time[0] = sched_clock();
if ((event & LCD_EVENT_SCREEN_BLACK) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
lcd_power_screen_black(pdrv);
local_time[1] = sched_clock();
pdrv->config.cus_ctrl.mute_time = local_time[1] - local_time[0];
return NOTIFY_OK;
}
static struct notifier_block lcd_power_screen_black_nb = {
.notifier_call = lcd_power_screen_black_notifier,
.priority = LCD_PRIORITY_SCREEN_BLACK,
};
static int lcd_power_screen_restore_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data;
unsigned long long local_time[3];
local_time[0] = sched_clock();
if ((event & LCD_EVENT_SCREEN_RESTORE) == 0)
return NOTIFY_DONE;
if (!pdrv) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", pdrv->index, __func__, event);
lcd_power_screen_restore(pdrv);
local_time[1] = sched_clock();
pdrv->config.cus_ctrl.unmute_time = local_time[1] - local_time[0];
return NOTIFY_OK;
}
static struct notifier_block lcd_power_screen_restore_nb = {
.notifier_call = lcd_power_screen_restore_notifier,
.priority = LCD_PRIORITY_SCREEN_RESTORE,
};
static int lcd_vlock_param_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aml_lcd_drv_s *pdrv;
int index;
unsigned int *param;
if ((event & LCD_EVENT_VLOCK_PARAM) == 0)
return NOTIFY_DONE;
if (!data) {
LCDERR("%s: data is null\n", __func__);
return NOTIFY_DONE;
}
param = (unsigned int *)data;
index = param[LCD_VLOCK_PARAM_NUM];
pdrv = aml_lcd_get_driver(index);
if (!pdrv) {
LCDERR("[%d]: %s: drv is null\n", index, __func__);
return NOTIFY_DONE;
}
if (pdrv->probe_done == 0)
return NOTIFY_DONE;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: 0x%lx\n", index, __func__, event);
memcpy(param, pdrv->config.vlock_param,
(LCD_VLOCK_PARAM_NUM * sizeof(unsigned int)));
return NOTIFY_OK;
}
static struct notifier_block lcd_vlock_param_nb = {
.notifier_call = lcd_vlock_param_notifier,
};
static int lcd_notifier_init(void)
{
int ret = 0;
ret = aml_lcd_notifier_register(&lcd_power_encl_on_nb);
if (ret)
LCDERR("register lcd_power_encl_on_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_power_encl_off_nb);
if (ret)
LCDERR("register lcd_power_encl_off_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_dlg_switch_mode_nb);
if (ret)
LCDERR("register lcd_dlg_switch_mode_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_dlg_power_if_on_nb);
if (ret)
LCDERR("register lcd_dlg_power_if_on_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_dlg_power_if_off_nb);
if (ret)
LCDERR("register lcd_dlg_power_if_off_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_power_if_on_nb);
if (ret)
LCDERR("register lcd_power_if_on_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_power_if_off_nb);
if (ret)
LCDERR("register lcd_power_if_off_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_power_screen_black_nb);
if (ret)
LCDERR("register lcd_power_screen_black_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_power_screen_restore_nb);
if (ret)
LCDERR("register lcd_power_screen_restore_nb failed\n");
ret = aml_lcd_notifier_register(&lcd_vlock_param_nb);
if (ret)
LCDERR("register lcd_vlock_param_nb failed\n");
return 0;
}
static void lcd_notifier_remove(void)
{
aml_lcd_notifier_unregister(&lcd_power_screen_restore_nb);
aml_lcd_notifier_unregister(&lcd_power_screen_black_nb);
aml_lcd_notifier_unregister(&lcd_dlg_switch_mode_nb);
aml_lcd_notifier_unregister(&lcd_dlg_power_if_off_nb);
aml_lcd_notifier_unregister(&lcd_dlg_power_if_on_nb);
aml_lcd_notifier_unregister(&lcd_power_if_off_nb);
aml_lcd_notifier_unregister(&lcd_power_if_on_nb);
aml_lcd_notifier_unregister(&lcd_power_encl_off_nb);
aml_lcd_notifier_unregister(&lcd_power_encl_on_nb);
aml_lcd_notifier_unregister(&lcd_vlock_param_nb);
}
/* **************************************** */
/* ************************************************************* */
/* lcd ioctl */
/* ************************************************************* */
static int lcd_io_open(struct inode *inode, struct file *file)
{
struct aml_lcd_drv_s *pdrv;
pdrv = container_of(inode->i_cdev, struct aml_lcd_drv_s, cdev);
file->private_data = pdrv;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
return 0;
}
static int lcd_io_release(struct inode *inode, struct file *file)
{
struct aml_lcd_drv_s *pdrv;
if (!file->private_data)
return 0;
pdrv = (struct aml_lcd_drv_s *)file->private_data;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
file->private_data = NULL;
return 0;
}
static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
void __user *argp;
int mcd_nr = -1;
struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)file->private_data;
struct lcd_optical_info_s *opt_info;
struct aml_lcd_tcon_bin_s lcd_tcon_buff;
struct tcon_rmem_s *tcon_rmem = get_lcd_tcon_rmem();
struct tcon_mem_map_table_s *mm_table = get_lcd_tcon_mm_table();
struct lcd_tcon_data_block_header_s block_header;
union lcd_ctrl_config_u *pctrl;
unsigned int size = 0, temp, m = 0;
unsigned char *mem_vaddr = NULL;
char *str = NULL;
int index = 0, i = 0;
struct lcd_config_s *pconf;
struct phy_config_s *phy_cfg, phy_usr;
unsigned int ss_level = 0xffffffff, ss_freq = 0xffffffff, ss_mode = 0xffffffff;
struct lcd_ss_ctl_s ss_ctl = {0xffffffff, 0xffffffff, 0xffffffff};
if (!pdrv)
return -EFAULT;
pconf = &pdrv->config;
pctrl = &pdrv->config.control;
opt_info = &pdrv->config.optical;
mcd_nr = _IOC_NR(cmd);
LCDPR("[%d]: %s: cmd_dir = 0x%x, cmd_nr = 0x%x\n",
pdrv->index, __func__, _IOC_DIR(cmd), mcd_nr);
argp = (void __user *)arg;
switch (mcd_nr) {
case LCD_IOC_NR_GET_HDR_INFO:
if (copy_to_user(argp, opt_info, sizeof(struct lcd_optical_info_s)))
ret = -EFAULT;
break;
case LCD_IOC_NR_SET_HDR_INFO:
if (copy_from_user(opt_info, argp, sizeof(struct lcd_optical_info_s))) {
ret = -EFAULT;
} else {
lcd_optical_vinfo_update(pdrv);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: set optical info:\n"
"hdr_support %d\n"
"features %d\n"
"primaries_r_x %d\n"
"primaries_r_y %d\n"
"primaries_g_x %d\n"
"primaries_g_y %d\n"
"primaries_b_x %d\n"
"primaries_b_y %d\n"
"white_point_x %d\n"
"white_point_y %d\n"
"luma_max %d\n"
"luma_min %d\n"
"luma_avg %d\n\n",
pdrv->index,
opt_info->hdr_support,
opt_info->features,
opt_info->primaries_r_x,
opt_info->primaries_r_y,
opt_info->primaries_g_x,
opt_info->primaries_g_y,
opt_info->primaries_b_x,
opt_info->primaries_b_y,
opt_info->white_point_x,
opt_info->white_point_y,
opt_info->luma_max,
opt_info->luma_min,
opt_info->luma_avg);
}
}
break;
case LCD_IOC_GET_TCON_BIN_MAX_CNT_INFO:
if (!mm_table) {
ret = -EFAULT;
break;
}
if (copy_to_user(argp, &mm_table->block_cnt, sizeof(unsigned int)))
ret = -EFAULT;
break;
case LCD_IOC_SET_TCON_DATA_INDEX_INFO:
if (copy_from_user(&lcd_tcon_bin_path_index, argp,
sizeof(unsigned int)))
ret = -EFAULT;
break;
case LCD_IOC_GET_TCON_BIN_PATH_INFO:
if (!mm_table || !tcon_rmem) {
ret = -EFAULT;
break;
}
mem_vaddr = tcon_rmem->bin_path_rmem.mem_vaddr;
if (!mem_vaddr) {
LCDERR("[%d]: %s: no tcon bin path rmem\n", pdrv->index, __func__);
ret = -EFAULT;
break;
}
if (lcd_tcon_bin_path_index >= mm_table->block_cnt) {
ret = -EFAULT;
break;
}
m = 32 + 256 * lcd_tcon_bin_path_index;
str = (char *)&mem_vaddr[m + 4];
LCDPR("[%d]: get tcon bin_path[%d]: %s\n",
pdrv->index, lcd_tcon_bin_path_index, str);
if (copy_to_user(argp, str, 256))
ret = -EFAULT;
break;
case LCD_IOC_SET_TCON_BIN_DATA_INFO:
if (!mm_table || !tcon_rmem) {
ret = -EFAULT;
break;
}
mem_vaddr = tcon_rmem->bin_path_rmem.mem_vaddr;
if (!mem_vaddr) {
LCDERR("[%d]: %s: no tcon bin path rmem\n", pdrv->index, __func__);
ret = -EFAULT;
break;
}
memset(&lcd_tcon_buff, 0, sizeof(struct aml_lcd_tcon_bin_s));
if (copy_from_user(&lcd_tcon_buff, argp, sizeof(struct aml_lcd_tcon_bin_s))) {
ret = -EFAULT;
break;
}
if (lcd_tcon_buff.size == 0) {
LCDERR("[%d]: %s: invalid data size %d\n",
pdrv->index, __func__, size);
ret = -EFAULT;
break;
}
index = lcd_tcon_buff.index;
if (index >= mm_table->block_cnt) {
LCDERR("[%d]: %s: invalid index %d\n", pdrv->index, __func__, index);
ret = -EFAULT;
break;
}
m = 32 + 256 * index;
str = (char *)&mem_vaddr[m + 4];
temp = *(unsigned int *)&mem_vaddr[m];
memset(&block_header, 0, sizeof(struct lcd_tcon_data_block_header_s));
argp = (void __user *)lcd_tcon_buff.ptr;
if (copy_from_user(&block_header, argp,
sizeof(struct lcd_tcon_data_block_header_s))) {
ret = -EFAULT;
break;
}
size = block_header.block_size;
if (size > lcd_tcon_buff.size ||
size < sizeof(struct lcd_tcon_data_block_header_s)) {
LCDERR("[%d]: %s: block[%d] size 0x%x error\n",
pdrv->index, __func__, index, size);
ret = -EFAULT;
break;
}
kfree(mm_table->data_mem_vaddr[index]);
mm_table->data_mem_vaddr[index] =
kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
if (!mm_table->data_mem_vaddr[index]) {
ret = -EFAULT;
break;
}
if (copy_from_user(mm_table->data_mem_vaddr[index], argp, size)) {
kfree(mm_table->data_mem_vaddr[index]);
mm_table->data_mem_vaddr[index] = NULL;
ret = -EFAULT;
break;
}
LCDPR("[%d]: load tcon bin_path[%d]: %s, size: 0x%x -> 0x%x\n",
pdrv->index, index, str, temp, size);
ret = lcd_tcon_data_load(pdrv, mm_table->data_mem_vaddr[index], index);
if (ret) {
kfree(mm_table->data_mem_vaddr[index]);
mm_table->data_mem_vaddr[index] = NULL;
ret = -EFAULT;
}
break;
case LCD_IOC_POWER_CTRL:
if (copy_from_user((void *)&temp, argp, sizeof(unsigned int))) {
ret = -EFAULT;
break;
}
if (temp) {
mutex_lock(&lcd_power_mutex);
aml_lcd_notifier_call_chain(LCD_EVENT_IF_POWER_ON, (void *)pdrv);
lcd_if_enable_retry(pdrv);
mutex_unlock(&lcd_power_mutex);
} else {
mutex_lock(&lcd_power_mutex);
aml_lcd_notifier_call_chain(LCD_EVENT_IF_POWER_OFF, (void *)pdrv);
mutex_unlock(&lcd_power_mutex);
}
break;
case LCD_IOC_MUTE_CTRL:
if (copy_from_user((void *)&temp, argp, sizeof(unsigned int))) {
ret = -EFAULT;
break;
}
if (temp)
lcd_screen_black(pdrv);
else
lcd_screen_restore(pdrv);
break;
case LCD_IOC_GET_PHY_PARAM:
phy_cfg = &pconf->phy_cfg;
if (copy_to_user(argp, (const void *)phy_cfg, sizeof(struct phy_config_s)))
ret = -EFAULT;
break;
case LCD_IOC_SET_PHY_PARAM:
memset(&phy_usr, 0, sizeof(struct phy_config_s));
if (copy_from_user((void *)&phy_usr, argp, sizeof(struct phy_config_s))) {
ret = -EFAULT;
break;
}
memcpy(&pdrv->config.phy_cfg, &phy_usr, sizeof(struct phy_config_s));
phy_cfg = &pconf->phy_cfg;
if (phy_cfg->ioctl_mode == 0) {
switch (pdrv->config.basic.lcd_type) {
case LCD_LVDS:
pctrl->lvds_cfg.phy_vswing = phy_cfg->vswing_level;
pctrl->lvds_cfg.phy_preem = phy_cfg->preem_level;
break;
case LCD_VBYONE:
pctrl->vbyone_cfg.phy_vswing = phy_cfg->vswing_level;
pctrl->vbyone_cfg.phy_preem = phy_cfg->preem_level;
break;
case LCD_MLVDS:
pctrl->mlvds_cfg.phy_vswing = phy_cfg->vswing_level;
pctrl->mlvds_cfg.phy_preem = phy_cfg->preem_level;
break;
case LCD_P2P:
pctrl->p2p_cfg.phy_vswing = phy_cfg->vswing_level;
pctrl->p2p_cfg.phy_preem = phy_cfg->preem_level;
break;
case LCD_EDP:
pctrl->edp_cfg.phy_vswing = phy_cfg->vswing_level;
pctrl->edp_cfg.phy_preem = phy_cfg->preem_level;
break;
default:
LCDERR("%s: not support lcd_type: %s\n",
__func__,
lcd_type_type_to_str(pdrv->config.basic.lcd_type));
return -EINVAL;
}
phy_cfg->vswing =
lcd_phy_vswing_level_to_value(pdrv, phy_cfg->vswing_level);
temp = lcd_phy_preem_level_to_value(pdrv, phy_cfg->preem_level);
for (i = 0; i < phy_cfg->lane_num; i++)
phy_cfg->lane[i].preem = temp;
}
if (pdrv->status & LCD_STATUS_IF_ON)
lcd_phy_set(pdrv, 1);
break;
case LCD_IOC_GET_SS:
lcd_get_ss_num(pdrv, &ss_ctl.level, &ss_ctl.freq, &ss_ctl.mode);
if (copy_to_user(argp, (const void *)&ss_ctl, sizeof(struct lcd_ss_ctl_s)))
ret = -EFAULT;
break;
case LCD_IOC_SET_SS:
if (copy_from_user((void *)&ss_ctl, argp, sizeof(struct lcd_ss_ctl_s))) {
ret = -EFAULT;
break;
}
if (ss_ctl.level != 0xffffffff)
ss_level = ss_ctl.level & 0xff;
if (ss_ctl.freq != 0xffffffff)
ss_freq = ss_ctl.freq & 0xff;
if (ss_ctl.mode != 0xffffffff)
ss_mode = ss_ctl.mode & 0xff;
ret = lcd_set_ss(pdrv, ss_level, ss_freq, ss_mode);
if (ret == 0) {
if (ss_level <= 0xff)
pdrv->config.timing.ss_level = ss_level;
if (ss_freq <= 0xff)
pdrv->config.timing.ss_freq = ss_freq;
if (ss_mode <= 0xff)
pdrv->config.timing.ss_mode = ss_mode;
}
break;
default:
LCDERR("[%d]: not support ioctl cmd_nr: 0x%x\n",
pdrv->index, mcd_nr);
ret = -EINVAL;
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long lcd_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = lcd_ioctl(file, cmd, arg);
return ret;
}
#endif
static const struct file_operations lcd_fops = {
.owner = THIS_MODULE,
.open = lcd_io_open,
.release = lcd_io_release,
.unlocked_ioctl = lcd_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = lcd_compat_ioctl,
#endif
};
static int lcd_cdev_add(struct aml_lcd_drv_s *pdrv, struct device *parent)
{
dev_t devno;
int ret = 0;
if (!pdrv) {
LCDERR("%s: pdrv is null\n", __func__);
return -1;
}
if (!lcd_cdev) {
ret = 1;
goto lcd_cdev_add_failed;
}
devno = MKDEV(MAJOR(lcd_cdev->devno), pdrv->index);
cdev_init(&pdrv->cdev, &lcd_fops);
pdrv->cdev.owner = THIS_MODULE;
ret = cdev_add(&pdrv->cdev, devno, 1);
if (ret) {
ret = 2;
goto lcd_cdev_add_failed;
}
pdrv->dev = device_create(lcd_cdev->class, parent,
devno, NULL, "lcd%d", pdrv->index);
if (IS_ERR_OR_NULL(pdrv->dev)) {
ret = 3;
goto lcd_cdev_add_failed1;
}
dev_set_drvdata(pdrv->dev, pdrv);
pdrv->dev->of_node = parent->of_node;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s OK\n", pdrv->index, __func__);
return 0;
lcd_cdev_add_failed1:
cdev_del(&pdrv->cdev);
lcd_cdev_add_failed:
LCDERR("[%d]: %s: failed: %d\n", pdrv->index, __func__, ret);
return -1;
}
static void lcd_cdev_remove(struct aml_lcd_drv_s *pdrv)
{
dev_t devno;
if (!lcd_cdev || !pdrv)
return;
devno = MKDEV(MAJOR(lcd_cdev->devno), pdrv->index);
device_destroy(lcd_cdev->class, devno);
cdev_del(&pdrv->cdev);
}
static int lcd_global_init_once(struct platform_device *pdev)
{
int ret;
if (lcd_global_init_flag) {
lcd_global_init_flag++;
return 0;
}
lcd_global_init_flag++;
lcd_debug_print_flag = lcd_debug_ctrl_config.debug_print_flag;
mutex_init(&lcd_vout_mutex);
mutex_init(&lcd_power_mutex);
lcd_clk_config_init();
lcd_notifier_init();
#ifdef CONFIG_AMLOGIC_LCD_EXTERN
lcd_extern_init();
#endif
/* init workqueue */
lcd_workqueue = create_workqueue("lcd_work_queue");
if (!lcd_workqueue) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDERR("can't create lcd workqueue\n");
}
lcd_cdev = kzalloc(sizeof(*lcd_cdev), GFP_KERNEL);
if (!lcd_cdev)
return -1;
ret = alloc_chrdev_region(&lcd_cdev->devno, 0,
LCD_MAX_DRV, LCD_CDEV_NAME);
if (ret) {
ret = 1;
goto lcd_cdev_init_once_err;
}
lcd_cdev->class = class_create(THIS_MODULE, "aml_lcd");
if (IS_ERR_OR_NULL(lcd_cdev->class)) {
ret = 2;
goto lcd_cdev_init_once_err_1;
}
return 0;
lcd_cdev_init_once_err_1:
unregister_chrdev_region(lcd_cdev->devno, LCD_MAX_DRV);
lcd_cdev_init_once_err:
kfree(lcd_cdev);
lcd_cdev = NULL;
LCDERR("%s: failed: %d\n", __func__, ret);
return -1;
}
static void lcd_global_remove_once(void)
{
if (lcd_global_init_flag > 1) {
lcd_global_init_flag--;
return;
}
lcd_global_init_flag--;
lcd_notifier_remove();
if (!lcd_cdev)
return;
class_destroy(lcd_cdev->class);
unregister_chrdev_region(lcd_cdev->devno, LCD_MAX_DRV);
kfree(lcd_cdev);
lcd_cdev = NULL;
}
/* ************************************************************* */
static int lcd_vsync_irq_init(struct aml_lcd_drv_s *pdrv)
{
init_completion(&pdrv->vsync_done);
if (pdrv->res_vsync_irq[0]) {
snprintf(pdrv->vsync_isr_name[0], 15, "lcd%d_vsync", pdrv->index);
if (request_irq(pdrv->res_vsync_irq[0]->start,
lcd_vsync_isr, IRQF_SHARED,
pdrv->vsync_isr_name[0], (void *)pdrv)) {
LCDERR("[%d]: can't request %s\n",
pdrv->index, pdrv->vsync_isr_name[0]);
} else {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: request %s successful\n",
pdrv->index, pdrv->vsync_isr_name[0]);
}
}
}
if (pdrv->res_vsync_irq[1]) {
snprintf(pdrv->vsync_isr_name[1], 15, "lcd%d_vsync2", pdrv->index);
if (request_irq(pdrv->res_vsync_irq[1]->start,
lcd_vsync2_isr, IRQF_SHARED,
pdrv->vsync_isr_name[1], (void *)pdrv)) {
LCDERR("[%d]: can't request %s\n",
pdrv->index, pdrv->vsync_isr_name[1]);
} else {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: request %s successful\n",
pdrv->index, pdrv->vsync_isr_name[1]);
}
}
}
if (pdrv->res_vsync_irq[2]) {
snprintf(pdrv->vsync_isr_name[2], 15, "lcd%d_vsync3", pdrv->index);
if (request_irq(pdrv->res_vsync_irq[2]->start,
lcd_vsync3_isr, IRQF_SHARED,
pdrv->vsync_isr_name[2], (void *)pdrv)) {
LCDERR("[%d]: can't request %s\n",
pdrv->index, pdrv->vsync_isr_name[2]);
} else {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: request %s successful\n",
pdrv->index, pdrv->vsync_isr_name[2]);
}
}
}
/* add timer to monitor hpll frequency */
timer_setup(&pdrv->vs_none_timer, &lcd_vsync_none_timer_handler, 0);
/* pdrv->vs_none_timer.data = NULL; */
pdrv->vs_none_timer.expires = jiffies + LCD_VSYNC_NONE_INTERVAL;
/*add_timer(&pdrv->vs_none_timer);*/
/*LCDPR("add vs_none_timer handler\n"); */
return 0;
}
static void lcd_vsync_irq_remove(struct aml_lcd_drv_s *pdrv)
{
if (pdrv->res_vsync_irq[0])
free_irq(pdrv->res_vsync_irq[0]->start, (void *)pdrv);
if (pdrv->res_vsync_irq[1])
free_irq(pdrv->res_vsync_irq[1]->start, (void *)pdrv);
if (pdrv->res_vsync_irq[2])
free_irq(pdrv->res_vsync_irq[2]->start, (void *)pdrv);
if (pdrv->vsync_none_timer_flag) {
del_timer_sync(&pdrv->vs_none_timer);
pdrv->vsync_none_timer_flag = 0;
}
}
static void lcd_init_vout(struct aml_lcd_drv_s *pdrv)
{
switch (pdrv->mode) {
#ifdef CONFIG_AMLOGIC_LCD_TV
case LCD_MODE_TV:
lcd_tv_vout_server_init(pdrv);
break;
#endif
#ifdef CONFIG_AMLOGIC_LCD_TABLET
case LCD_MODE_TABLET:
lcd_tablet_vout_server_init(pdrv);
break;
#endif
default:
LCDERR("[%d]: invalid lcd mode: %d\n",
pdrv->index, pdrv->mode);
break;
}
}
static int lcd_mode_init(struct aml_lcd_drv_s *pdrv)
{
if (!pdrv)
return -1;
switch (pdrv->mode) {
#ifdef CONFIG_AMLOGIC_LCD_TV
case LCD_MODE_TV:
lcd_mode_tv_init(pdrv);
break;
#endif
#ifdef CONFIG_AMLOGIC_LCD_TABLET
case LCD_MODE_TABLET:
lcd_mode_tablet_init(pdrv);
break;
#endif
default:
LCDERR("[%d]: invalid lcd mode: %d\n",
pdrv->index, pdrv->mode);
break;
}
return 0;
}
static int lcd_mode_probe(struct aml_lcd_drv_s *pdrv)
{
int ret;
if (!pdrv)
return -1;
ret = lcd_get_config(pdrv);
if (ret)
return -1;
/* must behind lcd_get_config, for phy will probe by interface type */
lcd_phy_probe(pdrv);
lcd_debug_probe(pdrv);
lcd_mode_init(pdrv);
pdrv->probe_done = 1;
lcd_vsync_irq_init(pdrv);
if (pdrv->init_flag) {
LCDPR("[%d]: power on for init_flag\n", pdrv->index);
mutex_lock(&lcd_power_mutex);
aml_lcd_notifier_call_chain(LCD_EVENT_IF_POWER_ON, (void *)pdrv);
lcd_if_enable_retry(pdrv);
mutex_unlock(&lcd_power_mutex);
}
/* add notifier for video sync_duration info refresh */
lcd_vout_notify_mode_change(pdrv);
if (pdrv->auto_test)
lcd_auto_test_func(pdrv);
lcd_drm_add(pdrv->dev);
return 0;
}
static int lcd_config_remove(struct aml_lcd_drv_s *pdrv)
{
switch (pdrv->mode) {
#ifdef CONFIG_AMLOGIC_LCD_TV
case LCD_MODE_TV:
lcd_tv_vout_server_remove(pdrv);
lcd_mode_tv_remove(pdrv);
break;
#endif
#ifdef CONFIG_AMLOGIC_LCD_TABLET
case LCD_MODE_TABLET:
lcd_tablet_vout_server_remove(pdrv);
lcd_mode_tablet_remove(pdrv);
break;
#endif
default:
LCDPR("[%d]: invalid lcd mode\n", pdrv->index);
break;
}
lcd_clk_config_remove(pdrv);
return 0;
}
static void lcd_vout_server_remove(struct aml_lcd_drv_s *pdrv)
{
switch (pdrv->mode) {
#ifdef CONFIG_AMLOGIC_LCD_TV
case LCD_MODE_TV:
lcd_tv_vout_server_remove(pdrv);
break;
#endif
#ifdef CONFIG_AMLOGIC_LCD_TABLET
case LCD_MODE_TABLET:
lcd_tablet_vout_server_remove(pdrv);
break;
#endif
default:
LCDPR("[%d]: %s: invalid lcd mode\n", pdrv->index, __func__);
break;
}
}
static void lcd_config_probe_work(struct work_struct *p_work)
{
struct delayed_work *d_work;
struct aml_lcd_drv_s *pdrv;
bool is_init;
int ret;
d_work = container_of(p_work, struct delayed_work, work);
pdrv = container_of(d_work, struct aml_lcd_drv_s, config_probe_dly_work);
is_init = lcd_unifykey_init_get();
if (!is_init) {
if (pdrv->retry_cnt++ < LCD_UNIFYKEY_WAIT_TIMEOUT) {
lcd_queue_delayed_work(&pdrv->config_probe_dly_work,
LCD_UNIFYKEY_RETRY_INTERVAL);
return;
}
LCDERR("[%d]: %s: key_init_flag=%d, exit\n", pdrv->index, __func__, is_init);
goto lcd_config_probe_work_failed;
}
LCDPR("[%d]: key_init_flag=%d, retry_cnt=%d\n", pdrv->index, is_init, pdrv->retry_cnt);
ret = lcd_mode_probe(pdrv);
if (ret) {
LCDERR("[%d]: %s: mode_probe failed, exit\n", pdrv->index, __func__);
goto lcd_config_probe_work_failed;
}
if ((pdrv->status & LCD_STATUS_VMODE_ACTIVE) &&
!(pdrv->status & LCD_STATUS_ENCL_ON)) {
LCDPR("[%d]: %s: lcd_enable in kernel\n", pdrv->index, __func__);
aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, (void *)pdrv);
lcd_if_enable_retry(pdrv);
}
return;
lcd_config_probe_work_failed:
lcd_vout_server_remove(pdrv);
lcd_driver[pdrv->index] = NULL;
kfree(pdrv);
}
static void lcd_config_default(struct aml_lcd_drv_s *pdrv)
{
unsigned int init_state;
pdrv->init_flag = 0;
init_state = lcd_get_venc_init_config(pdrv);
if (init_state) {
switch (pdrv->boot_ctrl->init_level) {
case LCD_INIT_LEVEL_NORMAL:
pdrv->status = LCD_STATUS_ON;
pdrv->resume_flag = (LCD_RESUME_PREPARE | LCD_RESUME_ENABLE);
break;
case LCD_INIT_LEVEL_PWR_OFF:
pdrv->status = LCD_STATUS_ENCL_ON;
pdrv->resume_flag = LCD_RESUME_PREPARE;
break;
case LCD_INIT_LEVEL_KERNEL_ON:
pdrv->init_flag = 1;
pdrv->status = LCD_STATUS_ENCL_ON;
pdrv->resume_flag = LCD_RESUME_PREPARE;
break;
default:
pdrv->status = LCD_STATUS_ON;
pdrv->resume_flag = (LCD_RESUME_PREPARE | LCD_RESUME_ENABLE);
break;
}
} else {
pdrv->status = 0;
pdrv->resume_flag = 0;
}
LCDPR("[%d]: status: %d, init_flag: %d\n",
pdrv->index, pdrv->status, pdrv->init_flag);
}
static int lcd_config_probe(struct aml_lcd_drv_s *pdrv, struct platform_device *pdev)
{
unsigned int val;
int ret = 0;
ret = lcd_base_config_load_from_dts(pdrv);
if (ret)
return -1;
pdrv->res_vsync_irq[0] = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
pdrv->res_vsync_irq[1] = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync2");
pdrv->res_vsync_irq[2] = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync3");
pdrv->res_vx1_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vbyone");
pdrv->res_tcon_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tcon");
pdrv->test_state = pdrv->debug_ctrl->debug_test_pattern;
pdrv->test_flag = 0;
pdrv->mute_state = 0;
pdrv->mute_flag = 0;
pdrv->mute_count = 0;
pdrv->mute_count_test = 0;
pdrv->unmute_count_test = 0;
pdrv->tcon_isr_bypass = 0;
pdrv->fr_mode = 0;
pdrv->viu_sel = LCD_VIU_SEL_NONE;
pdrv->vsync_none_timer_flag = 0;
pdrv->module_reset = lcd_module_reset;
lcd_clk_config_probe(pdrv);
lcd_phy_config_init(pdrv);
lcd_venc_probe(pdrv);
lcd_config_default(pdrv);
lcd_init_vout(pdrv);
if (pdrv->key_valid) {
lcd_queue_delayed_work(&pdrv->config_probe_dly_work, 0);
} else {
ret = lcd_mode_probe(pdrv);
if (ret) {
lcd_vout_server_remove(pdrv);
LCDERR("[%d]: probe exit\n", pdrv->index);
return -1;
}
}
if ((pdrv->status & LCD_STATUS_IF_ON) == 0)
return 0;
if (pdrv->config.basic.lcd_type == LCD_TYPE_MAX) {
val = pdrv->boot_ctrl->advanced_flag;
switch (pdrv->boot_ctrl->lcd_type) {
case LCD_RGB:
pdrv->config.control.rgb_cfg.de_valid = val & 0x1;
pdrv->config.control.rgb_cfg.sync_valid = (val >> 1) & 0x1;
lcd_rgb_pinmux_set(pdrv, 1);
break;
case LCD_VBYONE:
lcd_vbyone_pinmux_set(pdrv, 1);
break;
case LCD_MLVDS:
lcd_mlvds_pinmux_set(pdrv, 1);
break;
case LCD_P2P:
pdrv->config.control.p2p_cfg.p2p_type = val;
lcd_p2p_pinmux_set(pdrv, 1);
break;
case LCD_EDP:
lcd_edp_pinmux_set(pdrv, 1);
break;
case LCD_MIPI:
lcd_mipi_pinmux_set(pdrv, 1);
break;
default:
break;
}
}
return 0;
}
#ifdef CONFIG_OF
static struct lcd_data_s lcd_data_axg = {
.chip_type = LCD_CHIP_AXG,
.chip_name = "axg",
.reg_map_table = &lcd_reg_g12a[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_g12a = {
.chip_type = LCD_CHIP_G12A,
.chip_name = "g12a",
.reg_map_table = &lcd_reg_g12a[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_g12b = {
.chip_type = LCD_CHIP_G12B,
.chip_name = "g12b",
.reg_map_table = &lcd_reg_g12a[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_tl1 = {
.chip_type = LCD_CHIP_TL1,
.chip_name = "tl1",
.reg_map_table = &lcd_reg_tl1[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_sm1 = {
.chip_type = LCD_CHIP_SM1,
.chip_name = "sm1",
.reg_map_table = &lcd_reg_g12a[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_tm2 = {
.chip_type = LCD_CHIP_TM2,
.chip_name = "tm2",
.reg_map_table = &lcd_reg_tl1[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_t5 = {
.chip_type = LCD_CHIP_T5,
.chip_name = "t5",
.reg_map_table = &lcd_reg_t5[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_t5d = {
.chip_type = LCD_CHIP_T5D,
.chip_name = "t5d",
.reg_map_table = &lcd_reg_t5[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_t7 = {
.chip_type = LCD_CHIP_T7,
.chip_name = "t7",
.reg_map_table = &lcd_reg_t7[0],
.drv_max = 3,
.offset_venc = {0x0, 0x600, 0x800},
.offset_venc_if = {0x0, 0x500, 0x600},
.offset_venc_data = {0x0, 0x100, 0x200},
};
static struct lcd_data_s lcd_data_t3 = {
.chip_type = LCD_CHIP_T3,
.chip_name = "t3",
.reg_map_table = &lcd_reg_t5[0],
.drv_max = 2,
.offset_venc = {0x0, 0x600, 0x0},
.offset_venc_if = {0x0, 0x500, 0x0},
.offset_venc_data = {0x0, 0x100, 0x0},
};
static struct lcd_data_s lcd_data_t5w = {
.chip_type = LCD_CHIP_T5W,
.chip_name = "t5w",
.reg_map_table = &lcd_reg_t5[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_c3 = {
.chip_type = LCD_CHIP_C3,
.chip_name = "c3",
.reg_map_table = &lcd_reg_c3[0],
.drv_max = 1,
.offset_venc = {0},
.offset_venc_if = {0},
.offset_venc_data = {0},
};
static struct lcd_data_s lcd_data_t5m = {
.chip_type = LCD_CHIP_T5M,
.chip_name = "t5m",
.reg_map_table = &lcd_reg_t5[0],
.drv_max = 1,
.offset_venc = {0x0},
.offset_venc_if = {0x0},
.offset_venc_data = {0x0},
};
static const struct of_device_id lcd_dt_match_table[] = {
{
.compatible = "amlogic, lcd-axg",
.data = &lcd_data_axg,
},
{
.compatible = "amlogic, lcd-g12a",
.data = &lcd_data_g12a,
},
{
.compatible = "amlogic, lcd-g12b",
.data = &lcd_data_g12b,
},
{
.compatible = "amlogic, lcd-tl1",
.data = &lcd_data_tl1,
},
{
.compatible = "amlogic, lcd-sm1",
.data = &lcd_data_sm1,
},
{
.compatible = "amlogic, lcd-tm2",
.data = &lcd_data_tm2,
},
{
.compatible = "amlogic, lcd-t5",
.data = &lcd_data_t5,
},
{
.compatible = "amlogic, lcd-t5d",
.data = &lcd_data_t5d,
},
{
.compatible = "amlogic, lcd-t7",
.data = &lcd_data_t7,
},
{
.compatible = "amlogic, lcd-t3",
.data = &lcd_data_t3,
},
{
.compatible = "amlogic, lcd-t5w",
.data = &lcd_data_t5w,
},
{
.compatible = "amlogic, lcd-c3",
.data = &lcd_data_c3,
},
{
.compatible = "amlogic, lcd-t5m",
.data = &lcd_data_t5m,
},
{}
};
#endif
static int lcd_probe(struct platform_device *pdev)
{
struct aml_lcd_drv_s *pdrv;
const struct of_device_id *match;
struct lcd_data_s *pdata;
unsigned int index = 0;
int ret = 0;
lcd_global_init_once(pdev);
if (!pdev->dev.of_node)
return -1;
ret = of_property_read_u32(pdev->dev.of_node, "index", &index);
if (ret) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("%s: no index exist, default to 0\n", __func__);
index = 0;
}
if (index >= LCD_MAX_DRV) {
LCDERR("%s: invalid index %d\n", __func__, index);
return -1;
}
if (lcd_drv_init_state & (1 << index)) {
LCDERR("%s: index %d driver already registered\n",
__func__, index);
return -1;
}
lcd_drv_init_state |= (1 << index);
match = of_match_device(lcd_dt_match_table, &pdev->dev);
if (!match) {
LCDERR("%s: no match table\n", __func__);
return -1;
}
pdata = (struct lcd_data_s *)match->data;
LCDPR("[%d]: driver version: %s(%d-%s)\n",
index,
LCD_DRV_VERSION,
pdata->chip_type,
pdata->chip_name);
if (index >= pdata->drv_max) {
LCDERR("[%d]: %s: invalid index\n", index, __func__);
return -1;
}
pdrv = lcd_driver_add(index);
if (!pdrv)
goto lcd_probe_err_0;
/* set drvdata */
lcd_driver[index] = pdrv;
pdrv->data = pdata;
//pdrv->of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, pdrv);
#ifdef CONFIG_AMLOGIC_VPU
/*vpu dev register for lcd*/
pdrv->lcd_vpu_dev = vpu_dev_register(VPU_VENCL, LCD_CDEV_NAME);
#endif
ret = lcd_ioremap(pdrv, pdev);
if (ret)
goto lcd_probe_err_1;
spin_lock_init(&pdrv->isr_lock);
INIT_DELAYED_WORK(&pdrv->config_probe_dly_work, lcd_config_probe_work);
INIT_WORK(&pdrv->late_resume_work, lcd_lata_resume_work);
INIT_WORK(&pdrv->screen_restore_work, lcd_screen_restore_work);
INIT_DELAYED_WORK(&pdrv->test_delayed_work, lcd_auto_test_delayed);
ret = lcd_cdev_add(pdrv, &pdev->dev);
if (ret)
goto lcd_probe_err_2;
ret = lcd_config_probe(pdrv, pdev);
if (ret)
goto lcd_probe_err_2;
LCDPR("[%d]: %s ok, init_state:0x%x\n", index, __func__, lcd_drv_init_state);
return 0;
lcd_probe_err_2:
lcd_cdev_remove(pdrv);
lcd_probe_err_1:
/* free drvdata */
platform_set_drvdata(pdev, NULL);
lcd_driver[index] = NULL;
/* free drv */
kfree(pdrv);
lcd_probe_err_0:
lcd_drv_init_state &= ~(1 << index);
LCDPR("[%d]: %s failed\n", index, __func__);
return ret;
}
static int lcd_remove(struct platform_device *pdev)
{
struct aml_lcd_drv_s *pdrv = platform_get_drvdata(pdev);
int index;
if (!pdrv)
return 0;
lcd_drm_remove(pdrv->dev);
index = pdrv->index;
cancel_work_sync(&pdrv->late_resume_work);
cancel_work_sync(&pdrv->screen_restore_work);
cancel_delayed_work(&pdrv->config_probe_dly_work);
if (lcd_workqueue)
destroy_workqueue(lcd_workqueue);
lcd_vsync_irq_remove(pdrv);
lcd_cdev_remove(pdrv);
lcd_debug_remove(pdrv);
lcd_config_remove(pdrv);
/* free drvdata */
platform_set_drvdata(pdev, NULL);
kfree(pdrv->reg_map);
kfree(pdrv);
lcd_driver[index] = NULL;
lcd_drv_init_state &= ~(1 << index);
lcd_global_remove_once();
LCDPR("[%d]: %s, init_state:0x%x\n", index, __func__, lcd_drv_init_state);
return 0;
}
static int lcd_resume(struct platform_device *pdev)
{
struct aml_lcd_drv_s *pdrv = platform_get_drvdata(pdev);
if (!pdrv)
return 0;
if ((pdrv->status & LCD_STATUS_VMODE_ACTIVE) == 0)
return 0;
mutex_lock(&lcd_power_mutex);
LCDPR("[%d]: %s\n", pdrv->index, __func__);
pdrv->resume_flag |= LCD_RESUME_PREPARE;
aml_lcd_notifier_call_chain(LCD_EVENT_PREPARE, (void *)pdrv);
LCDPR("[%d]: %s finished\n", pdrv->index, __func__);
mutex_unlock(&lcd_power_mutex);
return 0;
}
static int lcd_suspend(struct platform_device *pdev, pm_message_t state)
{
struct aml_lcd_drv_s *pdrv = platform_get_drvdata(pdev);
if (!pdrv)
return 0;
mutex_lock(&lcd_power_mutex);
if (pdrv->status & LCD_STATUS_IF_ON)
LCDERR("[%d]: %s: lcd interface is still enabled!\n", pdrv->index, __func__);
pdrv->resume_flag &= ~LCD_RESUME_PREPARE;
if (pdrv->status & LCD_STATUS_ENCL_ON) {
aml_lcd_notifier_call_chain(LCD_EVENT_UNPREPARE, (void *)pdrv);
LCDPR("[%d]: %s finished\n", pdrv->index, __func__);
}
mutex_unlock(&lcd_power_mutex);
return 0;
}
static void lcd_shutdown(struct platform_device *pdev)
{
struct aml_lcd_drv_s *pdrv = platform_get_drvdata(pdev);
if (!pdrv)
return;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("%s\n", __func__);
if (pdrv->status & LCD_STATUS_ENCL_ON)
aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, (void *)pdrv);
}
static struct platform_driver lcd_platform_driver = {
.probe = lcd_probe,
.remove = lcd_remove,
.suspend = lcd_suspend,
.resume = lcd_resume,
.shutdown = lcd_shutdown,
.driver = {
.name = "mesonlcd",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(lcd_dt_match_table),
#endif
},
};
int __init lcd_init(void)
{
if (platform_driver_register(&lcd_platform_driver)) {
LCDERR("failed to register lcd driver module\n");
return -ENODEV;
}
return 0;
}
void __exit lcd_exit(void)
{
platform_driver_unregister(&lcd_platform_driver);
}
static int lcd_panel_name_para_setup(char *str)
{
if (str)
sprintf(lcd_panel_name[0], "%s", str);
LCDPR("panel_name: %s\n", lcd_panel_name[0]);
return 0;
}
static int lcd1_panel_name_para_setup(char *str)
{
if (str)
sprintf(lcd_panel_name[1], "%s", str);
LCDPR("panel_name: %s\n", lcd_panel_name[1]);
return 0;
}
static int lcd2_panel_name_para_setup(char *str)
{
if (str)
sprintf(lcd_panel_name[2], "%s", str);
LCDPR("panel_name: %s\n", lcd_panel_name[2]);
return 0;
}
static int lcd_panel_type_para_setup(char *str)
{
if (str)
sprintf(lcd_propname[0], "%s", str);
LCDPR("panel_type: %s\n", lcd_propname[0]);
return 0;
}
static int lcd1_panel_type_para_setup(char *str)
{
if (str)
sprintf(lcd_propname[1], "%s", str);
LCDPR("panel1_type: %s\n", lcd_propname[1]);
return 0;
}
static int lcd2_panel_type_para_setup(char *str)
{
if (str)
sprintf(lcd_propname[2], "%s", str);
LCDPR("panel2_type: %s\n", lcd_propname[2]);
return 0;
}
static int lcd_boot_ctrl_setup(char *str)
{
int ret = 0;
unsigned int data32 = 0;
struct lcd_boot_ctrl_s *boot_ctrl = &lcd_boot_ctrl_config[0];
if (!str)
return -EINVAL;
ret = kstrtouint(str, 16, &data32);
if (ret) {
LCDERR("%s:invalid data\n", __func__);
return -EINVAL;
}
LCDPR("lcd_ctrl: 0x%08x\n", data32);
boot_ctrl->lcd_type = data32 & 0xf;
boot_ctrl->lcd_bits = (data32 >> 4) & 0xf;
boot_ctrl->advanced_flag = (data32 >> 8) & 0xff;
boot_ctrl->custom_pinmux = (data32 >> 16) & 0x1;
boot_ctrl->init_level = (data32 >> 18) & 0x3;
return 0;
}
static int lcd1_boot_ctrl_setup(char *str)
{
int ret = 0;
unsigned int data32 = 0;
struct lcd_boot_ctrl_s *boot_ctrl = &lcd_boot_ctrl_config[1];
if (!str)
return -EINVAL;
ret = kstrtouint(str, 16, &data32);
if (ret) {
LCDERR("%s:invalid data\n", __func__);
return -EINVAL;
}
LCDPR("lcd1_ctrl: 0x%08x\n", data32);
boot_ctrl->lcd_type = data32 & 0xf;
boot_ctrl->lcd_bits = (data32 >> 4) & 0xf;
boot_ctrl->advanced_flag = (data32 >> 8) & 0xff;
boot_ctrl->custom_pinmux = (data32 >> 16) & 0x1;
boot_ctrl->init_level = (data32 >> 18) & 0x3;
return 0;
}
static int lcd2_boot_ctrl_setup(char *str)
{
int ret = 0;
unsigned int data32 = 0;
struct lcd_boot_ctrl_s *boot_ctrl = &lcd_boot_ctrl_config[2];
if (!str)
return -EINVAL;
ret = kstrtouint(str, 16, &data32);
if (ret) {
LCDERR("%s:invalid data\n", __func__);
return -EINVAL;
}
LCDPR("lcd2_ctrl: 0x%08x\n", data32);
boot_ctrl->lcd_type = data32 & 0xf;
boot_ctrl->lcd_bits = (data32 >> 4) & 0xf;
boot_ctrl->advanced_flag = (data32 >> 8) & 0xff;
boot_ctrl->custom_pinmux = (data32 >> 16) & 0x1;
boot_ctrl->init_level = (data32 >> 18) & 0x3;
return 0;
}
static int lcd_debug_ctrl_setup(char *str)
{
int ret = 0;
unsigned int data32 = 0;
struct lcd_debug_ctrl_s *debug_ctrl = &lcd_debug_ctrl_config;
if (!str)
return -EINVAL;
ret = kstrtouint(str, 16, &data32);
if (ret) {
LCDERR("%s:invalid data\n", __func__);
return -EINVAL;
}
LCDPR("debug_ctrl: 0x%08x\n", data32);
debug_ctrl->debug_print_flag = data32 & 0xff;
debug_ctrl->debug_test_pattern = (data32 >> 8) & 0xff;
debug_ctrl->debug_para_source = (data32 >> 28) & 0x3;
debug_ctrl->debug_lcd_mode = (data32 >> 30) & 0x3;
return 0;
}
__setup("panel_name=", lcd_panel_name_para_setup);
__setup("panel1_name=", lcd1_panel_name_para_setup);
__setup("panel2_name=", lcd2_panel_name_para_setup);
__setup("panel_type=", lcd_panel_type_para_setup);
__setup("panel1_type=", lcd1_panel_type_para_setup);
__setup("panel2_type=", lcd2_panel_type_para_setup);
__setup("lcd_ctrl=", lcd_boot_ctrl_setup);
__setup("lcd1_ctrl=", lcd1_boot_ctrl_setup);
__setup("lcd2_ctrl=", lcd2_boot_ctrl_setup);
__setup("lcd_debug=", lcd_debug_ctrl_setup);
//MODULE_DESCRIPTION("Meson LCD Panel Driver");
//MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Amlogic, Inc.");