blob: 52b08eb4552a97860956099d4fd6133f2b5a9fb5 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <linux/version.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/of_reserved_mem.h>
#include <linux/cma.h>
#include <linux/dma-contiguous.h>
#include <linux/dma-mapping.h>
#include <linux/compat.h>
#include <linux/sched/clock.h>
#include <linux/amlogic/media/vpu/vpu.h>
#include <linux/amlogic/media/vout/lcd/aml_ldim.h>
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
#include <linux/amlogic/media/vout/lcd/ldim_fw.h>
#include "../../lcd_common.h"
#include "ldim_drv.h"
#include "ldim_reg.h"
#define AML_LDIM_DEV_NAME "aml_ldim"
unsigned char ldim_debug_print;
struct ldim_dev_s {
struct ldim_drv_data_s *data;
struct vpu_dev_s *vpu_dev;
struct cdev cdev;
struct device *dev;
dev_t aml_ldim_devno;
struct class *aml_ldim_clsp;
struct cdev *aml_ldim_cdevp;
};
static struct ldim_dev_s ldim_dev;
static struct aml_ldim_driver_s ldim_driver;
static spinlock_t ldim_isr_lock;
static spinlock_t rdma_ldim_isr_lock;
static struct workqueue_struct *ldim_queue;
static struct work_struct ldim_on_vs_work;
static struct work_struct ldim_off_vs_work;
static struct work_struct ldim_err_handle_work;
static int ldim_on_init(void);
static int ldim_power_on(void);
static int ldim_power_off(void);
static int ldim_set_level(unsigned int level);
static void ldim_test_ctrl(int flag);
static void ldim_ld_sel_ctrl(int flag);
static void ldim_pwm_vs_update(void);
static void ldim_config_print(void);
static struct aml_ldim_info_s ldim_info;
static struct aml_ldim_pq_s ldim_pq;
static struct ldim_config_s ldim_config = {
.hsize = 3840,
.vsize = 2160,
.seg_row = 1,
.seg_col = 1,
.bl_mode = 1,
.func_en = 0,
.remap_en = 1,
.hvcnt_bypass = 0,
.dev_index = 0xff,
};
static struct ldim_rmem_s ldim_rmem = {
.flag = 0,
.wr_mem_vaddr1 = NULL,
.wr_mem_paddr1 = 0,
.wr_mem_vaddr2 = NULL,
.wr_mem_paddr2 = 0,
.rd_mem_vaddr1 = NULL,
.rd_mem_paddr1 = 0,
.wr_mem_vaddr2 = NULL,
.wr_mem_paddr2 = 0,
.wr_mem_size = 0,
.rd_mem_size = 0,
/* for new ldc */
.rsv_mem_paddr = 0,
.rsv_mem_size = 0x100000,
.global_hist_vaddr = NULL,
.global_hist_paddr = 0,
.global_hist_mem_size = 0,
.global_hist_highmem_flag = 0,
.seg_hist_vaddr = NULL,
.seg_hist_paddr = 0,
.seg_hist_mem_size = 0,
.seg_hist_highmem_flag = 0,
.duty_vaddr = NULL,
.duty_paddr = 0,
.duty_mem_size = 0,
.duty_highmem_flag = 0,
};
static struct ldim_stts_s ldim_stts = {
.max_rgb = NULL,
.hist_matrix = NULL,
.global_hist = NULL,
.seg_hist = NULL,
};
static struct ldim_comp_s ldim_comp = {
.ldc_comp_en = 1,
.ldc_bl_buf_diff = 0,
.ldc_glb_gain = 0x380,
.ldc_dth_en = 1,
.ldc_dth_bw = 1,
};
static struct aml_ldim_driver_s ldim_driver = {
.valid_flag = 0, /* default invalid, active when bl_ctrl_method=ldim */
.static_pic_flag = 0,
.vsync_change_flag = 0,
.init_on_flag = 0,
.func_en = 0,
.remap_en = 0,
.demo_en = 0,
.black_frm_en = 0,
.ld_sel = 1,
.func_bypass = 0,
.brightness_bypass = 0,
.test_bl_en = 0,
.test_remap_en = 0,
.avg_update_en = 0,
.matrix_update_en = 0,
.alg_en = 0,
.top_en = 0,
.hist_en = 0,
.load_db_en = 1,
.db_print_flag = 0,
.state = 0,
.data_min = LD_DATA_MIN,
.data_max = LD_DATA_MAX,
.brightness_level = 0,
.litgain = LD_DATA_MAX,
.irq_cnt = 0,
.arithmetic_time = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
.xfer_time = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
.conf = &ldim_config,
.dev_drv = NULL,
.rmem = &ldim_rmem,
.stts = &ldim_stts,
.fw = NULL,
.comp = &ldim_comp,
.fw_cus_pre = NULL,
.fw_cus_post = NULL,
.test_matrix = NULL,
.local_bl_matrix = NULL,
.bl_matrix_cur = NULL,
.bl_matrix_pre = NULL,
.init = ldim_on_init,
.power_on = ldim_power_on,
.power_off = ldim_power_off,
.set_level = ldim_set_level,
.test_ctrl = ldim_test_ctrl,
.ld_sel_ctrl = ldim_ld_sel_ctrl,
.pwm_vs_update = ldim_pwm_vs_update,
.config_print = ldim_config_print,
};
struct aml_ldim_driver_s *aml_ldim_get_driver(void)
{
return &ldim_driver;
}
void ldim_delay(int ms)
{
if (ms > 0 && ms < 20)
usleep_range(ms * 1000, ms * 1000 + 1);
else if (ms > 20)
msleep(ms);
}
void ldim_remap_ctrl(unsigned char status)
{
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
unsigned int temp;
temp = ldim_driver.matrix_update_en;
if (status) {
ldim_driver.matrix_update_en = 0;
if (bdrv->data->chip_type == LCD_CHIP_T3 ||
bdrv->data->chip_type == LCD_CHIP_T7) {
ldim_hw_remap_en_t7(&ldim_driver, 1);
} else {
ldim_hw_remap_en(&ldim_driver, 1);
}
msleep(20);
if (bdrv->data->chip_type == LCD_CHIP_TM2)
ldim_hw_vpu_dma_mif_en(LDIM_VPU_DMA_RD, 1);
else if (bdrv->data->chip_type == LCD_CHIP_TM2B)
ldim_hw_vpu_dma_mif_en_tm2b(LDIM_VPU_DMA_RD, 1);
ldim_driver.matrix_update_en = temp;
ldim_driver.remap_en = 1;
} else {
ldim_driver.remap_en = 0;
ldim_driver.matrix_update_en = 0;
if (bdrv->data->chip_type == LCD_CHIP_T3 ||
bdrv->data->chip_type == LCD_CHIP_T7) {
ldim_hw_remap_en_t7(&ldim_driver, 0);
} else {
ldim_hw_remap_en(&ldim_driver, 0);
}
msleep(20);
if (bdrv->data->chip_type == LCD_CHIP_TM2)
ldim_hw_vpu_dma_mif_en(LDIM_VPU_DMA_RD, 0);
else if (bdrv->data->chip_type == LCD_CHIP_TM2B)
ldim_hw_vpu_dma_mif_en_tm2b(LDIM_VPU_DMA_RD, 0);
ldim_driver.matrix_update_en = temp;
}
LDIMPR("%s: %d\n", __func__, status);
}
static void ldim_func_ctrl(struct aml_ldim_driver_s *ldim_drv, int flag)
{
if (!ldim_drv)
return;
if (flag) {
if (ldim_driver.ld_sel == 0) {
if (ldim_debug_print)
LDIMPR("%s: exit for ld_sel=0\n", __func__);
return;
}
/* enable other flag */
ldim_driver.top_en = 1;
ldim_driver.hist_en = 1;
ldim_driver.alg_en = 1;
/* enable update */
ldim_driver.avg_update_en = 1;
/*ldim_driver.matrix_update_en = 1;*/
ldim_driver.func_en = 1;
ldim_remap_ctrl(ldim_config.remap_en);
ldim_driver.state |= LDIM_STATE_FUNC_EN;
} else {
ldim_driver.state &= ~LDIM_STATE_FUNC_EN;
/* disable remap */
ldim_remap_ctrl(0);
ldim_driver.func_en = 0;
/* disable update */
ldim_driver.avg_update_en = 0;
/*ldim_driver.matrix_update_en = 0;*/
/* disable other flag */
ldim_driver.top_en = 0;
ldim_driver.hist_en = 0;
ldim_driver.alg_en = 0;
/* refresh system brightness */
ldim_driver.level_update = 1;
}
LDIMPR("%s: %d\n", __func__, flag);
}
static void ldim_stts_initial(unsigned int pic_h, unsigned int pic_v,
unsigned int hist_vnum, unsigned int hist_hnum)
{
LDIMPR("%s: %d %d %d %d\n",
__func__, pic_h, pic_v, hist_vnum, hist_hnum);
ldim_driver.fw->seg_col = hist_hnum;
ldim_driver.fw->seg_row = hist_vnum;
if (!ldim_dev.data) {
LDIMERR("%s: invalid data\n", __func__);
return;
}
if (ldim_dev.vpu_dev)
vpu_dev_mem_power_on(ldim_dev.vpu_dev);
if (ldim_dev.data->stts_init)
ldim_dev.data->stts_init(pic_h, pic_v, hist_vnum, hist_hnum);
}
static void ldim_initial(unsigned int pic_h, unsigned int pic_v,
unsigned int hist_vnum, unsigned int hist_hnum,
unsigned int blk_mode, unsigned int ldim_bl_en,
unsigned int hvcnt_bypass)
{
if (!ldim_driver.fw->ctrl->nprm) {
LDIMERR("%s: nprm is null\n", __func__);
return;
}
LDIMPR("%s: %d %d %d %d %d %d %d\n",
__func__, pic_h, pic_v, hist_vnum, hist_hnum,
blk_mode, ldim_bl_en, hvcnt_bypass);
ldim_driver.matrix_update_en = ldim_bl_en;
ld_func_cfg_ldreg(ldim_driver.fw->ctrl->nprm);
/* config params begin */
/* configuration of the panel parameters */
ldim_driver.fw->ctrl->nprm->reg_ld_pic_row_max = pic_v;
ldim_driver.fw->ctrl->nprm->reg_ld_pic_col_max = pic_h;
/* Maximum to BLKVMAX , Maximum to BLKHMAX */
ldim_driver.fw->ctrl->nprm->reg_ld_blk_vnum = hist_vnum;
ldim_driver.fw->ctrl->nprm->reg_ld_blk_hnum = hist_hnum;
ldim_driver.fw->ctrl->nprm->reg_ld_blk_mode = blk_mode;
/*config params end */
ld_func_fw_cfg_once(ldim_driver.fw->ctrl->nprm);
if (!ldim_dev.data) {
LDIMERR("%s: invalid data\n", __func__);
return;
}
if (ldim_dev.data->remap_init) {
ldim_dev.data->remap_init(ldim_driver.fw->ctrl->nprm,
ldim_bl_en, hvcnt_bypass);
}
}
static void ldim_drv_init(struct aml_ldim_driver_s *ldim_drv)
{
if (!ldim_drv)
return;
if (ldim_drv->fw && ldim_drv->dev_drv) {
ldim_func_profile_update(ldim_drv->fw,
ldim_drv->dev_drv->bl_profile);
}
ldim_stts_initial(ldim_drv->conf->hsize, ldim_drv->conf->vsize,
ldim_drv->conf->seg_row, ldim_drv->conf->seg_col);
ldim_initial(ldim_config.hsize, ldim_config.vsize,
ldim_config.seg_row, ldim_config.seg_col,
ldim_config.bl_mode, ldim_config.func_en,
ldim_config.hvcnt_bypass);
}
static int ldim_on_init(void)
{
int ret = 0;
if (ldim_debug_print)
LDIMPR("%s\n", __func__);
if (ldim_driver.data->drv_init)
ldim_driver.data->drv_init(&ldim_driver);
/* default disable ldim function */
if (ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver, ldim_config.func_en);
ldim_driver.init_on_flag = 1;
ldim_driver.level_update = 1;
ldim_driver.state |= LDIM_STATE_POWER_ON;
return ret;
}
static int ldim_power_on(void)
{
int ret = 0;
LDIMPR("%s\n", __func__);
if (ldim_driver.data->drv_init)
ldim_driver.data->drv_init(&ldim_driver);
if (ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver, ldim_config.func_en);
if (ldim_driver.dev_drv && ldim_driver.dev_drv->power_on)
ldim_driver.dev_drv->power_on(&ldim_driver);
ldim_driver.init_on_flag = 1;
ldim_driver.level_update = 1;
ldim_driver.state |= LDIM_STATE_POWER_ON;
return ret;
}
static int ldim_power_off(void)
{
int ret = 0;
LDIMPR("%s\n", __func__);
ldim_driver.state &= ~LDIM_STATE_POWER_ON;
ldim_driver.init_on_flag = 0;
if (ldim_driver.dev_drv && ldim_driver.dev_drv->power_off)
ldim_driver.dev_drv->power_off(&ldim_driver);
if (ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver, 0);
return ret;
}
static int ldim_set_level(unsigned int level)
{
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
unsigned int level_max, level_min;
ldim_driver.brightness_level = level;
if (ldim_driver.brightness_bypass)
return 0;
level_max = bdrv->bconf.level_max;
level_min = bdrv->bconf.level_min;
level = ((level - level_min) * (ldim_driver.data_max - ldim_driver.data_min)) /
(level_max - level_min) + ldim_driver.data_min;
level &= 0xfff;
ldim_driver.litgain = (unsigned long)level;
ldim_driver.level_update = 1;
return 0;
}
static void ldim_test_ctrl(int flag)
{
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
if (bdrv->data->chip_type >= LCD_CHIP_T7)
return;
if (flag) /* when enable lcd bist pattern, bypass ldim function */
ldim_driver.func_bypass = 1;
else
ldim_driver.func_bypass = 0;
LDIMPR("%s: ldim_func_bypass = %d\n",
__func__, ldim_driver.func_bypass);
}
static void ldim_ld_sel_ctrl(int flag)
{
LDIMPR("%s: ld_sel: %d\n", __func__, flag);
if (flag) {
ldim_driver.ld_sel = 1;
ldim_driver.state |= LDIM_STATE_LD_EN;
if (ldim_driver.data->func_ctrl) {
ldim_driver.data->func_ctrl(&ldim_driver,
ldim_config.func_en);
}
} else {
ldim_driver.ld_sel = 0;
ldim_driver.state &= ~LDIM_STATE_LD_EN;
if (ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver, 0);
}
}
static void ldim_pwm_vs_update(void)
{
if (ldim_driver.dev_drv && ldim_driver.dev_drv->pwm_vs_update)
ldim_driver.dev_drv->pwm_vs_update(&ldim_driver);
}
static void ldim_config_print(void)
{
if (ldim_driver.dev_drv && ldim_driver.dev_drv->config_print)
ldim_driver.dev_drv->config_print(&ldim_driver);
}
/* ******************************************************
* local dimming function
* ******************************************************
*/
static void ldim_vs_arithmetic(struct aml_ldim_driver_s *ldim_drv)
{
struct ld_reg_s *nprm = ldim_driver.fw->ctrl->nprm;
unsigned int size;
if (ldim_driver.top_en == 0)
return;
ldim_hw_stts_read_zone(ldim_driver.conf->seg_row,
ldim_driver.conf->seg_col);
if (ldim_driver.alg_en == 0)
return;
if (!ldim_driver.fw->fw_alg_frm) {
if (ldim_drv->dbg_vs_cnt == 0)
LDIMERR("%s: ldim_fw ko is not installed\n", __func__);
return;
}
ldim_driver.fw->fw_alg_frm(ldim_driver.fw, ldim_driver.stts);
size = ldim_driver.conf->seg_row * ldim_driver.conf->seg_col;
memcpy(ldim_driver.local_bl_matrix, nprm->bl_matrix,
size * (sizeof(unsigned int)));
}
static void ldim_vs_arithmetic_tm2(struct aml_ldim_driver_s *ldim_drv)
{
struct ld_reg_s *nprm = ldim_driver.fw->ctrl->nprm;
unsigned int *local_ldim_buf;
unsigned int size, i;
if (!ldim_driver.stts->hist_matrix)
return;
if (!ldim_driver.stts->max_rgb)
return;
if (ldim_driver.hist_en == 0)
return;
size = ldim_driver.conf->seg_row * ldim_driver.conf->seg_col;
local_ldim_buf = kcalloc(size * 20, sizeof(unsigned int), GFP_KERNEL);
if (!local_ldim_buf)
return;
/*stts_read*/
memcpy(local_ldim_buf, ldim_rmem.wr_mem_vaddr1,
size * 20 * sizeof(unsigned int));
for (i = 0; i < size; i++) {
memcpy(&ldim_driver.stts->hist_matrix[i * 16],
&local_ldim_buf[i * 20],
16 * sizeof(unsigned int));
memcpy(&ldim_driver.stts->max_rgb[i * 3],
&local_ldim_buf[(i * 20) + 16],
3 * sizeof(unsigned int));
}
if (ldim_driver.alg_en == 0) {
kfree(local_ldim_buf);
return;
}
if (!ldim_driver.fw->fw_alg_frm) {
if (ldim_driver.dbg_vs_cnt == 0)
LDIMERR("%s: ldim_fw ko is not installed\n", __func__);
return;
}
ldim_driver.fw->fw_alg_frm(ldim_driver.fw, ldim_driver.stts);
memcpy(ldim_driver.local_bl_matrix, nprm->bl_matrix,
size * (sizeof(unsigned int)));
kfree(local_ldim_buf);
}
static void ldim_remap_update(void)
{
if (!ldim_dev.data) {
if (ldim_driver.dbg_vs_cnt == 0)
LDIMERR("%s: invalid data\n", __func__);
return;
}
if (!ldim_dev.data->remap_update)
return;
ldim_dev.data->remap_update(ldim_driver.fw->ctrl->nprm,
ldim_driver.avg_update_en,
ldim_driver.matrix_update_en);
}
static void ldim_time_sort_save(unsigned long long *table,
unsigned long long data)
{
int i, j;
for (i = 9; i >= 0; i--) {
if (data > table[i]) {
for (j = 0; j < i; j++)
table[j] = table[j + 1];
table[i] = data;
break;
}
}
}
static void ldim_on_vs_brightness(void);
static void ldim_off_vs_brightness(void);
static irqreturn_t ldim_vsync_isr(int irq, void *dev_id)
{
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
unsigned long long local_time[3];
unsigned long flags;
if (ldim_driver.init_on_flag == 0)
return IRQ_HANDLED;
spin_lock_irqsave(&ldim_isr_lock, flags);
if (ldim_driver.dbg_vs_cnt++ >= 300) /* for debug print */
ldim_driver.dbg_vs_cnt = 0;
if (bdrv->data->chip_type == LCD_CHIP_T3 ||
bdrv->data->chip_type == LCD_CHIP_T7) {
ldim_remap_update();
}
if (ldim_driver.func_en) {
local_time[0] = sched_clock();
if (ldim_dev.data->vs_arithmetic)
ldim_dev.data->vs_arithmetic(&ldim_driver);
local_time[1] = sched_clock();
local_time[2] = local_time[1] - local_time[0];
ldim_time_sort_save(ldim_driver.arithmetic_time, local_time[2]);
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
ldim_on_vs_brightness();
#else
/*schedule_work(&ldim_on_vs_work);*/
queue_work(ldim_queue, &ldim_on_vs_work);
#endif
if (bdrv->data->chip_type != LCD_CHIP_T3 &&
bdrv->data->chip_type != LCD_CHIP_T7) {
ldim_remap_update();
}
} else {
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
ldim_off_vs_brightness();
#else
/*schedule_work(&ldim_off_vs_work);*/
queue_work(ldim_queue, &ldim_off_vs_work);
#endif
}
ldim_driver.irq_cnt++;
if (ldim_driver.irq_cnt > 0xfffffff)
ldim_driver.irq_cnt = 0;
spin_unlock_irqrestore(&ldim_isr_lock, flags);
return IRQ_HANDLED;
}
static void ldim_dev_err_handler(void)
{
struct ldim_dev_driver_s *dev_drv = ldim_driver.dev_drv;
int ret;
if (ldim_driver.dev_smr_bypass)
return;
if (!dev_drv || !dev_drv->dev_err_handler)
return;
ret = dev_drv->dev_err_handler(&ldim_driver);
if (ret) {
/*force update for next vsync*/
ldim_driver.level_update = 1;
}
}
static void ldim_dev_smr(int update_flag, unsigned int size)
{
struct ldim_dev_driver_s *dev_drv = ldim_driver.dev_drv;
if (ldim_driver.dev_smr_bypass)
return;
if (!dev_drv)
return;
if (!dev_drv->dev_smr) {
if (ldim_driver.dbg_vs_cnt == 0)
LDIMERR("%s: dev_smr is null\n", __func__);
return;
}
if (update_flag) {
dev_drv->dev_smr(&ldim_driver, ldim_driver.bl_matrix_cur, size);
memcpy(ldim_driver.bl_matrix_pre, ldim_driver.bl_matrix_cur,
(size * sizeof(unsigned int)));
} else {
if (dev_drv->dev_smr_dummy)
dev_drv->dev_smr_dummy(&ldim_driver);
}
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
/*schedule_work(&ldim_err_handle_work);*/
queue_work(ldim_queue, &ldim_err_handle_work);
#else
ldim_dev_err_handler();
#endif
}
static void ldim_on_vs_brightness(void)
{
unsigned long long local_time[3];
unsigned int size, i;
int update_flag = 0;
if (ldim_driver.init_on_flag == 0)
return;
if (ldim_driver.func_bypass)
return;
local_time[0] = sched_clock();
size = ldim_driver.conf->seg_row * ldim_driver.conf->seg_col;
if (ldim_driver.test_bl_en) {
memcpy(ldim_driver.bl_matrix_cur, ldim_driver.test_matrix,
(size * sizeof(unsigned int)));
} else {
if (ldim_driver.black_frm_en &&
ldim_driver.fw->ctrl->black_frm) {
memset(ldim_driver.bl_matrix_cur, 0,
(size * sizeof(unsigned int)));
} else {
for (i = 0; i < size; i++) {
ldim_driver.bl_matrix_cur[i] =
(((ldim_driver.local_bl_matrix[i] * ldim_driver.litgain)
+ (LD_DATA_MAX / 2)) >> LD_DATA_DEPTH);
}
}
}
if (ldim_driver.duty_update_flag & 0x01)
update_flag = memcmp(ldim_driver.bl_matrix_cur, ldim_driver.bl_matrix_pre,
(size * sizeof(unsigned int)));
else
update_flag = 1;
ldim_dev_smr(update_flag, size);
local_time[1] = sched_clock();
local_time[2] = local_time[1] - local_time[0];
ldim_time_sort_save(ldim_driver.xfer_time, local_time[2]);
}
static void ldim_off_vs_brightness(void)
{
unsigned long long local_time[3];
unsigned int size, i;
int update_flag = 0;
if (ldim_driver.init_on_flag == 0)
return;
local_time[0] = sched_clock();
size = ldim_driver.conf->seg_row * ldim_driver.conf->seg_col;
if (ldim_driver.test_bl_en) {
memcpy(ldim_driver.bl_matrix_cur, ldim_driver.test_matrix,
(size * sizeof(unsigned int)));
} else {
for (i = 0; i < size; i++)
ldim_driver.bl_matrix_cur[i] = ldim_driver.litgain;
}
if (ldim_driver.level_update) {
ldim_driver.level_update = 0;
update_flag = 1;
} else {
if (ldim_driver.duty_update_flag & 0x02)
update_flag = memcmp(ldim_driver.bl_matrix_cur,
ldim_driver.bl_matrix_pre,
(size * sizeof(unsigned int)));
else
update_flag = 1;
}
if (ldim_debug_print && update_flag) {
if (ldim_driver.test_bl_en)
LDIMPR("%s: test_matrix update\n", __func__);
else
LDIMPR("%s: level update: 0x%x\n", __func__, ldim_driver.litgain);
}
ldim_dev_smr(update_flag, size);
local_time[1] = sched_clock();
local_time[2] = local_time[1] - local_time[0];
ldim_time_sort_save(ldim_driver.xfer_time, local_time[2]);
}
static void ldim_on_update_brightness(struct work_struct *work)
{
ldim_on_vs_brightness();
}
static void ldim_off_update_brightness(struct work_struct *work)
{
ldim_off_vs_brightness();
}
static void ldim_err_handler(struct work_struct *work)
{
ldim_dev_err_handler();
}
/* ******************************************************
* local dimming dummy function for virtual ldim dev
* ******************************************************
*/
static int ldim_dev_add_virtual_driver(struct aml_ldim_driver_s *ldim_drv)
{
LDIMPR("%s\n", __func__);
ldim_drv->init();
return 0;
}
/* ******************************************************/
static int ldim_open(struct inode *inode, struct file *file)
{
struct ldim_dev_s *devp;
LDIMPR("%s\n", __func__);
/* Get the per-device structure that contains this cdev */
devp = container_of(inode->i_cdev, struct ldim_dev_s, cdev);
file->private_data = devp;
return 0;
}
static int ldim_release(struct inode *inode, struct file *file)
{
LDIMPR("%s\n", __func__);
file->private_data = NULL;
return 0;
}
static void aml_ldim_info_update(void)
{
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
struct fw_ctrl_s *fctrl;
int i = 0, j = 0;
if (!ldim_driver.fw || !ldim_driver.fw->ctrl) {
LDIMERR("%s: fw ctrl is null\n", __func__);
return;
}
fctrl = ldim_driver.fw->ctrl;
fctrl->tf_alpha = ldim_info.alpha;
fctrl->lpf_method = ldim_info.lpf_method;
fctrl->lpf_gain = ldim_info.lpf_gain;
fctrl->lpf_res = ldim_info.lpf_res;
fctrl->side_blk_diff_th = ldim_info.side_blk_diff_th;
fctrl->bbd_th = ldim_info.bbd_th;
fctrl->boost_gain = ldim_info.boost_gain;
fctrl->rgb_base = ldim_info.rgb_base;
fctrl->ld_remap_bypass = ldim_info.ld_remap_bypass;
fctrl->ld_tf_step_th = ldim_info.ld_tf_step_th;
fctrl->tf_blk_fresh_bl = ldim_info.tf_blk_fresh_bl;
fctrl->tf_fresh_bl = ldim_info.tf_fresh_bl;
fctrl->fw_ld_thtf_l = ldim_info.fw_ld_thtf_l;
fctrl->fw_rgb_diff_th = ldim_info.fw_rgb_diff_th;
fctrl->fw_ld_thist = ldim_info.fw_ld_thist;
for (i = 0; i < 17; i++)
fctrl->bl_remap_curve[i] = ldim_info.bl_remap_curve[i];
for (i = 0; i < 16; i++)
fctrl->fw_ld_whist[i] = ldim_info.fw_ld_whist[i];
for (i = 0; i < 16; i++) {
for (j = 0; j < 32; j++)
ld_remap_lut[i][j] = ldim_info.reg_ld_remap_lut[i][j];
}
ldim_config.remap_en = ldim_info.remapping_en;
ldim_config.func_en = ldim_info.func_en;
//LCD_CHIP_T3/T7 update in vsync isr
if (bdrv->data->chip_type != LCD_CHIP_T3 &&
bdrv->data->chip_type != LCD_CHIP_T7) {
if (ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver,
ldim_config.func_en);
}
}
static void ldim_remap_lut_print(char *buf, int len)
{
int i, j;
LDIMPR("ld_remap_lut:\n");
for (i = 0; i < 16; i++) {
LDIMPR(" %d:\n", i);
len = 0;
for (j = 0; j < 32; j++) {
if (j == 16)
len += sprintf(buf + len, "\n");
len += sprintf(buf + len, " %d",
ldim_info.reg_ld_remap_lut[i][j]);
}
LDIMPR("%s\n", buf);
}
}
static void aml_ldim_pq_update(void)
{
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
struct fw_ctrl_s *fctrl;
struct ldim_comp_s *comp;
int i = 0, j = 0;
if (!ldim_driver.fw || !ldim_driver.fw->ctrl) {
LDIMERR("%s: fw ctrl is null\n", __func__);
return;
}
ldim_driver.fw->fw_sel = ldim_pq.fw_sel;
fctrl = ldim_driver.fw->ctrl;
fctrl->prm_ldc->ldc_hist_mode = ldim_pq.ldc_hist_mode;
fctrl->prm_ldc->ldc_hist_blend_mode = ldim_pq.ldc_hist_blend_mode;
fctrl->prm_ldc->ldc_hist_blend_alpha = ldim_pq.ldc_hist_blend_alpha;
fctrl->prm_ldc->ldc_hist_adap_blend_max_gain =
ldim_pq.ldc_hist_adap_blend_max_gain;
fctrl->prm_ldc->ldc_hist_adap_blend_diff_th1 =
ldim_pq.ldc_hist_adap_blend_diff_th1;
fctrl->prm_ldc->ldc_hist_adap_blend_diff_th2 =
ldim_pq.ldc_hist_adap_blend_diff_th2;
fctrl->prm_ldc->ldc_hist_adap_blend_th0 =
ldim_pq.ldc_hist_adap_blend_th0;
fctrl->prm_ldc->ldc_hist_adap_blend_thn =
ldim_pq.ldc_hist_adap_blend_thn;
fctrl->prm_ldc->ldc_hist_adap_blend_gain_0 =
ldim_pq.ldc_hist_adap_blend_gain_0;
fctrl->prm_ldc->ldc_hist_adap_blend_gain_1 =
ldim_pq.ldc_hist_adap_blend_gain_1;
fctrl->prm_ldc->ldc_init_bl_min = ldim_pq.ldc_init_bl_min;
fctrl->prm_ldc->ldc_init_bl_max = ldim_pq.ldc_init_bl_max;
fctrl->prm_ldc->ldc_sf_mode = ldim_pq.ldc_sf_mode;
fctrl->prm_ldc->ldc_sf_gain_up = ldim_pq.ldc_sf_gain_up;
fctrl->prm_ldc->ldc_sf_gain_dn = ldim_pq.ldc_sf_gain_dn;
fctrl->prm_ldc->ldc_sf_tsf_3x3 = ldim_pq.ldc_sf_tsf_3x3;
fctrl->prm_ldc->ldc_sf_tsf_5x5 = ldim_pq.ldc_sf_tsf_5x5;
fctrl->prm_ldc->ldc_bs_bl_mode = ldim_pq.ldc_bs_bl_mode;
//fctrl->prm_ldc->ldc_glb_apl = ldim_pq.ldc_glb_apl;
fctrl->prm_ldc->ldc_bs_glb_apl_gain = ldim_pq.ldc_bs_glb_apl_gain;
fctrl->prm_ldc->ldc_bs_dark_scene_bl_th =
ldim_pq.ldc_bs_dark_scene_bl_th;
fctrl->prm_ldc->ldc_bs_gain = ldim_pq.ldc_bs_gain;
fctrl->prm_ldc->ldc_bs_limit_gain = ldim_pq.ldc_bs_limit_gain;
fctrl->prm_ldc->ldc_bs_loc_apl_gain = ldim_pq.ldc_bs_loc_apl_gain;
fctrl->prm_ldc->ldc_bs_loc_max_min_gain =
ldim_pq.ldc_bs_loc_max_min_gain;
fctrl->prm_ldc->ldc_bs_loc_dark_scene_bl_th =
ldim_pq.ldc_bs_loc_dark_scene_bl_th;
fctrl->prm_ldc->ldc_tf_en = ldim_pq.ldc_tf_en;
//fctrl->prm_ldc->ldc_tf_sc_flag = ldim_pq.ldc_tf_sc_flag;
fctrl->prm_ldc->ldc_tf_low_alpha = ldim_pq.ldc_tf_low_alpha;
fctrl->prm_ldc->ldc_tf_high_alpha = ldim_pq.ldc_tf_high_alpha;
fctrl->prm_ldc->ldc_tf_low_alpha_sc = ldim_pq.ldc_tf_low_alpha_sc;
fctrl->prm_ldc->ldc_tf_high_alpha_sc = ldim_pq.ldc_tf_high_alpha_sc;
fctrl->prm_ldc->ldc_dimming_curve_en = ldim_pq.ldc_dimming_curve_en;
fctrl->prm_ldc->ldc_sc_hist_diff_th = ldim_pq.ldc_sc_hist_diff_th;
fctrl->prm_ldc->ldc_sc_apl_diff_th = ldim_pq.ldc_sc_apl_diff_th;
for (i = 0; i < 17; i++)
fctrl->bl_remap_curve[i] = ldim_pq.bl_remap_curve[i];
comp = ldim_driver.comp;
comp->ldc_bl_buf_diff = ldim_pq.ldc_bl_buf_diff;
comp->ldc_glb_gain = ldim_pq.ldc_glb_gain;
comp->ldc_dth_en = ldim_pq.ldc_dth_en;
comp->ldc_dth_bw = ldim_pq.ldc_dth_bw;
for (i = 0; i < 16; i++) {
for (j = 0; j < 64; j++)
ldc_gain_lut_array[i][j] = ldim_pq.ldc_gain_lut[i][j];
}
for (i = 0; i < 64; i++)
ldc_min_gain_lut[i] = ldim_pq.ldc_min_gain_lut[i];
for (i = 0; i < 32; i++) {
for (j = 0; j < 16; j++)
ldc_dither_lut[i][j] = ldim_pq.ldc_dither_lut[i][j];
}
ldim_config.remap_en = ldim_pq.remapping_en;
ldim_config.func_en = ldim_pq.func_en;
//LCD_CHIP_T3/T7 update in vsync isr
if (bdrv->data->chip_type != LCD_CHIP_T3 &&
bdrv->data->chip_type != LCD_CHIP_T7) {
if (ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver,
ldim_config.func_en);
}
}
static long ldim_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int i = 0, len = 0;
int ret = 0;
char *curve_buf;
char *lut_buf;
void __user *argp;
int mcd_nr;
mcd_nr = _IOC_NR(cmd);
LDIMPR("%s: cmd_dir = 0x%x, cmd_nr = 0x%x\n",
__func__, _IOC_DIR(cmd), mcd_nr);
argp = (void __user *)arg;
switch (mcd_nr) {
case AML_LDIM_IOC_NR_GET_INFO:
if (copy_to_user(argp, &ldim_info,
sizeof(struct aml_ldim_info_s))) {
ret = -EFAULT;
}
break;
case AML_LDIM_IOC_NR_SET_INFO:
memset(&ldim_info, 0, sizeof(struct aml_ldim_info_s));
if (copy_from_user(&ldim_info, argp,
sizeof(struct aml_ldim_info_s))) {
ret = -EFAULT;
} else {
aml_ldim_info_update();
if (ldim_debug_print) {
LDIMPR("set ldim info:\n"
"func_en: %d\n"
"remapping_en: %d\n"
"alpha: %d\n"
"LPF_method: %d\n"
"lpf_gain: %d\n"
"lpf_res: %d\n"
"side_blk_diff_th: %d\n"
"bbd_th: %d\n"
"boost_gain: %d\n"
"rgb_base: %d\n"
"Ld_remap_bypass: %d\n"
"LD_TF_STEP_TH: %d\n"
"TF_BLK_FRESH_BL: %d\n"
"TF_FRESH_BL: %d\n"
"fw_LD_ThTF_l: %d\n"
"fw_rgb_diff_th: %d\n"
"fw_ld_thist: %d\n",
ldim_info.func_en,
ldim_info.remapping_en,
ldim_info.alpha,
ldim_info.lpf_method,
ldim_info.lpf_gain,
ldim_info.lpf_res,
ldim_info.side_blk_diff_th,
ldim_info.bbd_th,
ldim_info.boost_gain,
ldim_info.rgb_base,
ldim_info.ld_remap_bypass,
ldim_info.ld_tf_step_th,
ldim_info.tf_blk_fresh_bl,
ldim_info.tf_fresh_bl,
ldim_info.fw_ld_thtf_l,
ldim_info.fw_rgb_diff_th,
ldim_info.fw_ld_thist);
/* ldim_bl_remap_curve_print */
len = 17 * 8 + 20;
curve_buf = kcalloc(len, sizeof(char), GFP_KERNEL);
if (!curve_buf)
return 0;
LDIMPR("bl_remap_curve:\n");
len = 0;
for (i = 0; i < 17; i++)
len +=
sprintf(curve_buf + len, "\t%d",
ldim_info.bl_remap_curve[i]);
LDIMPR("%s\n", curve_buf);
LDIMPR("fw_ld_whist:\n");
len = 0;
for (i = 0; i < 16; i++) {
len += sprintf(curve_buf + len, "\t%d",
ldim_info.fw_ld_whist[i]);
}
LDIMPR("%s\n", curve_buf);
/*ldim_bl_remap_lut_print*/
len = 32 * 8 + 20;
lut_buf = kcalloc(len, sizeof(char), GFP_KERNEL);
if (!lut_buf) {
kfree(curve_buf);
return 0;
}
ldim_remap_lut_print(lut_buf, len);
kfree(curve_buf);
kfree(lut_buf);
}
}
break;
case AML_LDIM_IOC_NR_GET_INFO_NEW:
if (copy_to_user(argp, &ldim_pq,
sizeof(struct aml_ldim_pq_s))) {
ret = -EFAULT;
}
break;
case AML_LDIM_IOC_NR_SET_INFO_NEW:
memset(&ldim_pq, 0, sizeof(struct aml_ldim_pq_s));
if (copy_from_user(&ldim_pq, argp,
sizeof(struct aml_ldim_pq_s))) {
ret = -EFAULT;
}
aml_ldim_pq_update();
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long ldim_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = ldim_ioctl(file, cmd, arg);
return ret;
}
#endif
static const struct file_operations ldim_fops = {
.owner = THIS_MODULE,
.open = ldim_open,
.release = ldim_release,
.unlocked_ioctl = ldim_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ldim_compat_ioctl,
#endif
};
int aml_ldim_get_config_dts(struct device_node *child)
{
struct vinfo_s *vinfo = get_current_vinfo();
unsigned int para[5];
int ret;
if (!child) {
LDIMERR("child device_node is null\n");
return -1;
}
/* default setting */
ldim_config.hsize = vinfo->width;
ldim_config.vsize = vinfo->height;
/* get row & col from dts */
ret = of_property_read_u32_array(child, "bl_ldim_zone_row_col", para, 2);
if (ret) {
ret = of_property_read_u32_array(child, "bl_ldim_region_row_col", para, 2);
if (ret) {
LDIMERR("failed to get bl_ldim_zone_row_col\n");
goto aml_ldim_get_config_dts_next;
}
}
if ((para[0] * para[1]) > LD_BLKREGNUM) {
LDIMERR("zone row*col(%d*%d) is out of support\n", para[0], para[1]);
} else {
ldim_config.seg_row = para[0];
ldim_config.seg_col = para[1];
}
LDIMPR("get bl_zone row = %d, col = %d\n",
ldim_config.seg_row, ldim_config.seg_col);
aml_ldim_get_config_dts_next:
/* get bl_mode from dts */
ret = of_property_read_u32(child, "bl_ldim_mode", &para[0]);
if (ret)
LDIMERR("failed to get bl_ldim_mode\n");
else
ldim_config.bl_mode = (unsigned char)para[0];
LDIMPR("get bl_mode = %d\n", ldim_config.bl_mode);
/* get ldim_dev_index from dts */
ret = of_property_read_u32(child, "ldim_dev_index", &para[0]);
if (ret)
LDIMERR("failed to get ldim_dev_index\n");
else
ldim_config.dev_index = (unsigned char)para[0];
if (ldim_config.dev_index < 0xff)
LDIMPR("get ldim_dev_index = %d\n", ldim_config.dev_index);
return 0;
}
int aml_ldim_get_config_unifykey(unsigned char *buf)
{
unsigned char *p;
struct vinfo_s *vinfo = get_current_vinfo();
/* default setting */
ldim_config.hsize = vinfo->width;
ldim_config.vsize = vinfo->height;
p = buf;
/* ldim: 24byte */
/* get bl_ldim_region_row_col 4byte*/
ldim_config.seg_row = *(p + LCD_UKEY_BL_LDIM_ROW);
ldim_config.seg_col = *(p + LCD_UKEY_BL_LDIM_COL);
LDIMPR("get bl_zone row = %d, col = %d\n",
ldim_config.seg_row, ldim_config.seg_col);
/* get bl_ldim_mode 1byte*/
ldim_config.bl_mode = *(p + LCD_UKEY_BL_LDIM_MODE);
LDIMPR("get bl_mode = %d\n", ldim_config.bl_mode);
/* get ldim_dev_index 1byte*/
ldim_config.dev_index = *(p + LCD_UKEY_BL_LDIM_DEV_INDEX);
if (ldim_config.dev_index < 0xff)
LDIMPR("get dev_index = %d\n", ldim_config.dev_index);
return 0;
}
static int ldim_rmem_tm2(void)
{
/* init reserved memory */
ldim_rmem.wr_mem_vaddr1 = kmalloc(ldim_rmem.wr_mem_size, GFP_KERNEL);
if (!ldim_rmem.wr_mem_vaddr1)
goto ldim_rmem_err0;
ldim_rmem.wr_mem_paddr1 = virt_to_phys(ldim_rmem.wr_mem_vaddr1);
ldim_rmem.wr_mem_vaddr2 = kmalloc(ldim_rmem.wr_mem_size, GFP_KERNEL);
if (!ldim_rmem.wr_mem_vaddr2)
goto ldim_rmem_err1;
ldim_rmem.wr_mem_paddr2 = virt_to_phys(ldim_rmem.wr_mem_vaddr2);
ldim_rmem.rd_mem_vaddr1 = kmalloc(ldim_rmem.rd_mem_size, GFP_KERNEL);
if (!ldim_rmem.rd_mem_vaddr1)
goto ldim_rmem_err2;
ldim_rmem.rd_mem_paddr1 = virt_to_phys(ldim_rmem.rd_mem_vaddr1);
ldim_rmem.rd_mem_vaddr2 = kmalloc(ldim_rmem.rd_mem_size, GFP_KERNEL);
if (!ldim_rmem.rd_mem_vaddr2)
goto ldim_rmem_err3;
ldim_rmem.rd_mem_paddr2 = virt_to_phys(ldim_rmem.rd_mem_vaddr2);
memset(ldim_rmem.wr_mem_vaddr1, 0, ldim_rmem.wr_mem_size);
memset(ldim_rmem.wr_mem_vaddr2, 0, ldim_rmem.wr_mem_size);
memset(ldim_rmem.rd_mem_vaddr1, 0, ldim_rmem.rd_mem_size);
memset(ldim_rmem.rd_mem_vaddr2, 0, ldim_rmem.rd_mem_size);
return 0;
ldim_rmem_err3:
kfree(ldim_rmem.rd_mem_vaddr1);
ldim_rmem_err2:
kfree(ldim_rmem.wr_mem_vaddr2);
ldim_rmem_err1:
kfree(ldim_rmem.wr_mem_vaddr1);
ldim_rmem_err0:
return -1;
}
static int aml_ldim_malloc(struct platform_device *pdev, struct ldim_drv_data_s *data,
unsigned int row, unsigned int col)
{
struct ldim_stts_s *stts = ldim_driver.stts;
struct ldim_fw_s *fw = ldim_driver.fw;
unsigned int zone_num = row * col;
int i, ret = 0;
if (!fw) {
LDIMERR("%s: fw is null\n", __func__);
return -1;
}
if (ldim_dev.data->alloc_rmem) {
ret = ldim_dev.data->alloc_rmem();
if (ret)
goto ldim_malloc_err0;
}
if (!stts)
goto ldim_malloc_err0;
stts->hist_matrix = kcalloc((zone_num * 16), sizeof(unsigned int),
GFP_KERNEL);
if (!stts->hist_matrix)
goto ldim_malloc_err0;
stts->max_rgb = kcalloc((zone_num * 3), sizeof(unsigned int),
GFP_KERNEL);
if (!stts->max_rgb)
goto ldim_malloc_err1;
ldim_driver.local_bl_matrix = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.local_bl_matrix)
goto ldim_malloc_err2;
ldim_driver.bl_matrix_cur = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.bl_matrix_cur)
goto ldim_malloc_err3;
ldim_driver.bl_matrix_pre = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.bl_matrix_pre)
goto ldim_malloc_err4;
ldim_driver.test_matrix = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.test_matrix)
goto ldim_malloc_err5;
for (i = 0; i < zone_num; i++)
ldim_driver.test_matrix[i] = 2048;
return 0;
ldim_malloc_err5:
kfree(ldim_driver.bl_matrix_pre);
ldim_driver.bl_matrix_pre = NULL;
ldim_malloc_err4:
kfree(ldim_driver.bl_matrix_cur);
ldim_driver.bl_matrix_cur = NULL;
ldim_malloc_err3:
kfree(ldim_driver.local_bl_matrix);
ldim_driver.local_bl_matrix = NULL;
ldim_malloc_err2:
kfree(stts->max_rgb);
stts->max_rgb = NULL;
ldim_malloc_err1:
kfree(stts->hist_matrix);
stts->hist_matrix = NULL;
ldim_malloc_err0:
LDIMERR("%s failed\n", __func__);
return -1;
}
static unsigned char *aml_ldim_rmem_map(unsigned long paddr, unsigned int mem_size,
unsigned int *flag)
{
unsigned char *vaddr = NULL;
unsigned int highmem_flag = 0;
highmem_flag = PageHighMem(phys_to_page(paddr));
*flag = highmem_flag;
//LDIMPR("%s: highmem_flag: %d\n", __func__, highmem_flag);
if (highmem_flag == 0) {
vaddr = phys_to_virt(paddr);
if (!vaddr)
return NULL;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL)
LDIMPR("%s: phys_to_virt: paddr=0x%lx, vaddr=0x%px, size: 0x%x\n",
__func__, paddr, vaddr, mem_size);
} else {
vaddr = lcd_vmap(paddr, mem_size);
if (!vaddr)
return NULL;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL)
LDIMPR("%s: vmap: paddr=0x%lx, vaddr=0x%px, size: 0x%x\n",
__func__, paddr, vaddr, mem_size);
}
return vaddr;
}
static void aml_ldim_rmem_unmap(unsigned char *vaddr, unsigned int flag)
{
if (flag)
lcd_unmap_phyaddr(vaddr);
}
static int aml_ldim_malloc_t7(struct platform_device *pdev, struct ldim_drv_data_s *data,
unsigned int row, unsigned int col)
{
struct ldim_stts_s *stts = ldim_driver.stts;
struct ldim_fw_s *fw = ldim_driver.fw;
unsigned int zone_num = row * col;
unsigned int mem_size;
int i, ret = 0;
/* init reserved memory */
ret = of_reserved_mem_device_init(&pdev->dev);
if (ret) {
LDIMERR("init reserved memory failed\n");
return -1;
}
mem_size = ldim_rmem.rsv_mem_size;
ldim_rmem.rsv_mem_vaddr = dma_alloc_coherent(&pdev->dev, mem_size,
&ldim_rmem.rsv_mem_paddr, GFP_KERNEL);
if (!ldim_rmem.rsv_mem_vaddr) {
ldim_rmem.flag = 0;
LDIMERR("ldc resv mem failed\n");
return -1;
}
ldim_rmem.rsv_mem_size = mem_size;
ldim_rmem.flag = 1;
LDIMPR("ldc resv mem paddr: 0x%lx, size: 0x%x\n",
(unsigned long)ldim_rmem.rsv_mem_paddr, ldim_rmem.rsv_mem_size);
ldim_rmem.profile_mem_paddr = ldim_rmem.rsv_mem_paddr + LDC_PROFILE_OFFSET;
ldim_rmem.profile_mem_size = LDC_GLOBAL_HIST_OFFSET - LDC_PROFILE_OFFSET;
ldim_rmem.global_hist_paddr = ldim_rmem.rsv_mem_paddr + LDC_GLOBAL_HIST_OFFSET;
ldim_rmem.global_hist_mem_size = LDC_SEG_HIST0_OFFSET - LDC_GLOBAL_HIST_OFFSET;
ldim_rmem.global_hist_vaddr = aml_ldim_rmem_map(ldim_rmem.global_hist_paddr,
ldim_rmem.global_hist_mem_size,
&ldim_rmem.global_hist_highmem_flag);
if (!ldim_rmem.global_hist_vaddr)
goto ldim_malloc_t7_err0;
ldim_rmem.seg_hist_paddr = ldim_rmem.rsv_mem_paddr + LDC_SEG_HIST0_OFFSET;
ldim_rmem.seg_hist_mem_size = LDC_SEG_DUTY0_OFFSET - LDC_SEG_HIST0_OFFSET;
ldim_rmem.seg_hist_vaddr = aml_ldim_rmem_map(ldim_rmem.seg_hist_paddr,
ldim_rmem.seg_hist_mem_size,
&ldim_rmem.seg_hist_highmem_flag);
if (!ldim_rmem.seg_hist_vaddr) {
aml_ldim_rmem_unmap(ldim_rmem.global_hist_vaddr,
ldim_rmem.global_hist_highmem_flag);
goto ldim_malloc_t7_err0;
}
ldim_rmem.duty_paddr = ldim_rmem.rsv_mem_paddr + LDC_SEG_DUTY0_OFFSET;
ldim_rmem.duty_mem_size = LDC_MEM_END - LDC_SEG_DUTY0_OFFSET;
ldim_rmem.duty_vaddr = aml_ldim_rmem_map(ldim_rmem.duty_paddr,
ldim_rmem.duty_mem_size,
&ldim_rmem.duty_highmem_flag);
if (!ldim_rmem.duty_vaddr) {
aml_ldim_rmem_unmap(ldim_rmem.seg_hist_vaddr,
ldim_rmem.seg_hist_highmem_flag);
goto ldim_malloc_t7_err0;
}
if (!stts)
goto ldim_malloc_t7_err0;
stts->global_hist = kcalloc(64, sizeof(unsigned int), GFP_KERNEL);
if (!stts->global_hist) {
aml_ldim_rmem_unmap(ldim_rmem.duty_vaddr,
ldim_rmem.duty_highmem_flag);
goto ldim_malloc_t7_err0;
}
stts->seg_hist = kcalloc(zone_num, sizeof(*stts->seg_hist), GFP_KERNEL);
if (!stts->seg_hist)
goto ldim_malloc_t7_err1;
if (!fw)
goto ldim_malloc_t7_err2;
fw->bl_matrix = kcalloc(zone_num, sizeof(unsigned int), GFP_KERNEL);
if (!fw->bl_matrix)
goto ldim_malloc_t7_err2;
ldim_driver.local_bl_matrix = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.local_bl_matrix)
goto ldim_malloc_t7_err3;
ldim_driver.bl_matrix_cur = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.bl_matrix_cur)
goto ldim_malloc_t7_err4;
ldim_driver.bl_matrix_pre = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.bl_matrix_pre)
goto ldim_malloc_t7_err5;
ldim_driver.test_matrix = kcalloc(zone_num, sizeof(unsigned int),
GFP_KERNEL);
if (!ldim_driver.test_matrix)
goto ldim_malloc_t7_err6;
for (i = 0; i < zone_num; i++)
ldim_driver.test_matrix[i] = 2048;
return 0;
ldim_malloc_t7_err6:
kfree(ldim_driver.bl_matrix_pre);
ldim_malloc_t7_err5:
kfree(ldim_driver.bl_matrix_cur);
ldim_malloc_t7_err4:
kfree(ldim_driver.local_bl_matrix);
ldim_malloc_t7_err3:
kfree(fw->bl_matrix);
ldim_malloc_t7_err2:
kfree(stts->seg_hist);
ldim_malloc_t7_err1:
kfree(stts->global_hist);
ldim_malloc_t7_err0:
LDIMERR("%s failed\n", __func__);
return -1;
}
static void ldim_remap_update_t3(struct ld_reg_s *nprm,
unsigned int avg_update_en, unsigned int matrix_update_en)
{
unsigned int linecnt;
if (ldim_debug_print == 9) {
linecnt = lcd_vcbus_getb(VPU_VENCP_STAT, 16, 13);
LDIMPR("%s linecnt = %d\n", __func__, linecnt);
}
if (ldim_config.func_en != ldim_driver.func_en) {
if (ldim_debug_print)
LDIMPR("%s func_en = %d : %d\n", __func__,
ldim_driver.func_en, ldim_config.func_en);
if (ldim_driver.data && ldim_driver.data->func_ctrl)
ldim_driver.data->func_ctrl(&ldim_driver,
ldim_config.func_en);
ldim_driver.func_en = ldim_config.func_en;
} else if (ldim_config.remap_en != ldim_driver.remap_en) {
if (ldim_driver.func_en) {
if (ldim_debug_print)
LDIMPR("%s func_en = 1,remap_en = %d : %d\n", __func__,
ldim_driver.remap_en, ldim_config.remap_en);
ldim_config_update_t7(&ldim_driver);
ldim_hw_remap_en_t7(&ldim_driver, ldim_config.remap_en);
ldim_driver.remap_en = ldim_config.remap_en;
}
}
}
static struct ldim_drv_data_s ldim_data_tl1 = {
.h_zone_max = 31,
.v_zone_max = 16,
.total_zone_max = 128,
.wr_mem_size = 0,
.rd_mem_size = 0,
.remap_update = NULL,
.alloc_rmem = NULL,
.stts_init = ldim_hw_stts_initial_tl1,
.remap_init = NULL,
.vs_arithmetic = ldim_vs_arithmetic,
.memory_init = aml_ldim_malloc,
.drv_init = ldim_drv_init,
.func_ctrl = ldim_func_ctrl,
.remap_lut_update = NULL,
};
static struct ldim_drv_data_s ldim_data_tm2 = {
.h_zone_max = 48,
.v_zone_max = 32,
.total_zone_max = 1536,
.wr_mem_size = 0x3000,
.rd_mem_size = 0x1000,
.remap_update = ldim_hw_remap_update_tm2,
.alloc_rmem = ldim_rmem_tm2,
.stts_init = ldim_hw_stts_initial_tm2,
.remap_init = ldim_hw_remap_init_tm2,
.vs_arithmetic = ldim_vs_arithmetic_tm2,
.memory_init = aml_ldim_malloc,
.drv_init = ldim_drv_init,
.func_ctrl = ldim_func_ctrl,
.remap_lut_update = NULL,
};
static struct ldim_drv_data_s ldim_data_tm2b = {
.h_zone_max = 48,
.v_zone_max = 32,
.total_zone_max = 1536,
.wr_mem_size = 0x3000,
.rd_mem_size = 0x1000,
.remap_update = ldim_hw_remap_update_tm2b,
.alloc_rmem = ldim_rmem_tm2,
.stts_init = ldim_hw_stts_initial_tm2,
.remap_init = ldim_hw_remap_init_tm2,
.vs_arithmetic = ldim_vs_arithmetic_tm2,
.memory_init = aml_ldim_malloc,
.drv_init = ldim_drv_init,
.func_ctrl = ldim_func_ctrl,
.remap_lut_update = NULL,
};
static struct ldim_drv_data_s ldim_data_t7 = {
.h_zone_max = 48,
.v_zone_max = 32,
.total_zone_max = 1536,
.wr_mem_size = 0x400000,
.rd_mem_size = 0,
.remap_update = ldim_remap_update_t3,
.alloc_rmem = NULL,
.stts_init = NULL,
.remap_init = NULL,
.vs_arithmetic = ldim_vs_arithmetic_t7,
.memory_init = aml_ldim_malloc_t7,
.drv_init = ldim_drv_init_t7,
.func_ctrl = ldim_func_ctrl_t7,
.remap_lut_update = ldc_gain_lut_set_t7,
};
static struct ldim_drv_data_s ldim_data_t3 = {
.h_zone_max = 48,
.v_zone_max = 32,
.total_zone_max = 1536,
.wr_mem_size = 0x400000,
.rd_mem_size = 0,
.remap_update = ldim_remap_update_t3,
.alloc_rmem = NULL,
.stts_init = NULL,
.remap_init = NULL,
.vs_arithmetic = ldim_vs_arithmetic_t7,
.memory_init = aml_ldim_malloc_t7,
.drv_init = ldim_drv_init_t3,
.func_ctrl = ldim_func_ctrl_t7,
.remap_lut_update = ldc_gain_lut_set_t3,
};
static int ldim_region_num_check(struct ldim_drv_data_s *data)
{
unsigned short temp;
if (!data) {
LDIMERR("%s: data is NULL\n", __func__);
return -1;
}
if (ldim_config.seg_row > data->v_zone_max) {
LDIMERR("%s: blk row (%d) is out of support (%d)\n",
__func__, ldim_config.seg_row, data->v_zone_max);
return -1;
}
if (ldim_config.seg_col > data->h_zone_max) {
LDIMERR("%s: blk col (%d) is out of support (%d)\n",
__func__, ldim_config.seg_col, data->h_zone_max);
return -1;
}
temp = ldim_config.seg_row * ldim_config.seg_col;
if (temp > data->total_zone_max) {
LDIMERR("%s: blk total region (%d) is out of support (%d)\n",
__func__, temp, data->total_zone_max);
return -1;
}
return 0;
}
int aml_ldim_probe(struct platform_device *pdev)
{
int ret = 0;
unsigned int ldim_vsync_irq = 0;
struct ldim_dev_s *devp = &ldim_dev;
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
struct ldim_fw_s *fw = aml_ldim_get_fw();
struct ldim_fw_custom_s *fw_cus_pre = aml_ldim_get_fw_cus_pre();
struct ldim_fw_custom_s *fw_cus_post = aml_ldim_get_fw_cus_post();
memset(devp, 0, (sizeof(struct ldim_dev_s)));
#ifdef LDIM_DEBUG_INFO
ldim_debug_print = 1;
#endif
switch (bdrv->data->chip_type) {
case LCD_CHIP_TL1:
devp->data = &ldim_data_tl1;
ldim_driver.data = &ldim_data_tl1;
break;
case LCD_CHIP_TM2:
devp->data = &ldim_data_tm2;
ldim_driver.data = &ldim_data_tm2;
devp->vpu_dev = vpu_dev_register(VPU_DMA, "bl_ldim");
break;
case LCD_CHIP_TM2B:
devp->data = &ldim_data_tm2b;
ldim_driver.data = &ldim_data_tm2b;
devp->vpu_dev = vpu_dev_register(VPU_DMA, "bl_ldim");
break;
case LCD_CHIP_T7:
devp->data = &ldim_data_t7;
ldim_driver.data = &ldim_data_t7;
break;
case LCD_CHIP_T3:
devp->data = &ldim_data_t3;
ldim_driver.data = &ldim_data_t3;
break;
default:
devp->data = NULL;
LDIMERR("%s: don't support ldim for current chip\n", __func__);
return -1;
}
ret = ldim_region_num_check(devp->data);
if (ret)
return -1;
ldim_driver.level_update = 0;
ldim_driver.duty_update_flag = 0;
if (!fw) {
LDIMERR("%s: fw is null\n", __func__);
return -1;
}
ldim_driver.fw = fw;
ldim_driver.fw->seg_row = ldim_config.seg_row;
ldim_driver.fw->seg_col = ldim_config.seg_col;
ldim_driver.fw->valid = 1;
if (!fw_cus_pre) {
LDIMERR("%s: fw_cus_pre is null\n", __func__);
} else {
LDIMPR("%s: fw_cus_pre is exist\n", __func__);
ldim_driver.fw_cus_pre = fw_cus_pre;
ldim_driver.fw_cus_pre->seg_row = ldim_config.seg_row;
ldim_driver.fw_cus_pre->seg_col = ldim_config.seg_col;
ldim_driver.fw_cus_pre->valid = 1;
}
if (!fw_cus_post) {
LDIMERR("%s: fw_cus_post is null\n", __func__);
} else {
LDIMPR("%s: fw_cus_post is exist\n", __func__);
ldim_driver.fw_cus_post = fw_cus_post;
ldim_driver.fw_cus_post->seg_row = ldim_config.seg_row;
ldim_driver.fw_cus_post->seg_col = ldim_config.seg_col;
ldim_driver.fw_cus_post->valid = 1;
}
if (devp->data->memory_init) {
ret = devp->data->memory_init(pdev, devp->data,
ldim_config.seg_row, ldim_config.seg_col);
if (ret) {
LDIMERR("%s failed\n", __func__);
goto err;
}
ldim_driver.rmem = &ldim_rmem;
}
ret = alloc_chrdev_region(&devp->aml_ldim_devno, 0, 1, AML_LDIM_DEVICE_NAME);
if (ret < 0) {
LDIMERR("failed to alloc major number\n");
ret = -ENODEV;
goto err;
}
devp->aml_ldim_clsp = class_create(THIS_MODULE, "aml_ldim");
if (IS_ERR(devp->aml_ldim_clsp)) {
ret = PTR_ERR(devp->aml_ldim_clsp);
return ret;
}
ret = aml_ldim_debug_probe(devp->aml_ldim_clsp);
if (ret)
goto err1;
devp->aml_ldim_cdevp = kmalloc(sizeof(*devp->aml_ldim_cdevp), GFP_KERNEL);
if (!devp->aml_ldim_cdevp) {
ret = -ENOMEM;
goto err2;
}
/* connect the file operations with cdev */
cdev_init(devp->aml_ldim_cdevp, &ldim_fops);
devp->aml_ldim_cdevp->owner = THIS_MODULE;
/* connect the major/minor number to the cdev */
ret = cdev_add(devp->aml_ldim_cdevp, devp->aml_ldim_devno, 1);
if (ret) {
LDIMERR("failed to add device\n");
goto err3;
}
devp->dev = device_create(devp->aml_ldim_clsp, NULL,
devp->aml_ldim_devno, NULL, AML_LDIM_CLASS_NAME);
if (IS_ERR(devp->dev)) {
ret = PTR_ERR(devp->dev);
goto err4;
}
ldim_queue = create_workqueue("ldim workqueue");
if (!ldim_queue) {
LDIMERR("ldim_queue create failed\n");
ret = -1;
goto err4;
}
INIT_WORK(&ldim_on_vs_work, ldim_on_update_brightness);
INIT_WORK(&ldim_off_vs_work, ldim_off_update_brightness);
INIT_WORK(&ldim_err_handle_work, ldim_err_handler);
spin_lock_init(&ldim_isr_lock);
spin_lock_init(&rdma_ldim_isr_lock);
bdrv->res_ldim_vsync_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
if (!bdrv->res_ldim_vsync_irq) {
ret = -ENODEV;
LDIMERR("ldim_vsync_irq resource error\n");
goto err4;
}
ldim_vsync_irq = bdrv->res_ldim_vsync_irq->start;
if (request_irq(ldim_vsync_irq, ldim_vsync_isr, IRQF_SHARED,
"ldim_vsync", (void *)"ldim_vsync")) {
LDIMERR("can't request ldim_vsync_irq(%d)\n", ldim_vsync_irq);
}
ldim_driver.valid_flag = 1;
if (bdrv->bconf.method != BL_CTRL_LOCAL_DIMMING)
ldim_dev_add_virtual_driver(&ldim_driver);
LDIMPR("%s ok\n", __func__);
return 0;
err4:
cdev_del(&devp->cdev);
err3:
kfree(devp->aml_ldim_cdevp);
err2:
aml_ldim_debug_remove(devp->aml_ldim_clsp);
class_destroy(devp->aml_ldim_clsp);
err1:
unregister_chrdev_region(devp->aml_ldim_devno, 1);
err:
return ret;
}
int aml_ldim_remove(void)
{
struct ldim_dev_s *devp = &ldim_dev;
struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0);
kfree(ldim_driver.bl_matrix_cur);
kfree(ldim_driver.stts->hist_matrix);
kfree(ldim_driver.stts->max_rgb);
kfree(ldim_driver.test_matrix);
kfree(ldim_driver.local_bl_matrix);
free_irq(bdrv->res_ldim_vsync_irq->start, (void *)"ldim_vsync");
cdev_del(devp->aml_ldim_cdevp);
kfree(devp->aml_ldim_cdevp);
aml_ldim_debug_remove(devp->aml_ldim_clsp);
class_destroy(devp->aml_ldim_clsp);
unregister_chrdev_region(devp->aml_ldim_devno, 1);
LDIMPR("%s ok\n", __func__);
return 0;
}
static int __init ldim_buf_device_init(struct reserved_mem *rmem,
struct device *dev)
{
return 0;
}
static const struct reserved_mem_ops ldim_buf_ops = {
.device_init = ldim_buf_device_init,
};
static int __init ldim_buf_setup(struct reserved_mem *rmem)
{
ldim_rmem.rsv_mem_paddr = rmem->base;
ldim_rmem.rsv_mem_size = rmem->size;
rmem->ops = &ldim_buf_ops;
LDIMPR("Reserved memory: created buf at 0x%lx, size %ld MiB\n",
(unsigned long)rmem->base, (unsigned long)rmem->size / SZ_1M);
return 0;
}
RESERVEDMEM_OF_DECLARE(buf, "amlogic, ldc-memory", ldim_buf_setup);