blob: 956cb6ae63ce5399fe44c2125af4b4f76c5e254e [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/backlight.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/pwm.h>
#include <linux/amlogic/pwm-meson.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/aml_gpio_consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
#include "lcd_bl.h"
#include "../lcd_reg.h"
struct bl_pwm_init_config_s {
unsigned int *pwm_vs_reg;
unsigned int pwm_vs_reg_cnt;
unsigned int pwm_vs_flag;
};
static struct bl_pwm_init_config_s pwm_init_cfg = {
.pwm_vs_reg = NULL,
.pwm_vs_reg_cnt = 0,
.pwm_vs_flag = 0,
};
static char *bl_pwm_name[] = {
"PWM_A",
"PWM_B",
"PWM_C",
"PWM_D",
"PWM_E",
"PWM_F",
"PWM_G",
"PWM_H",
"PWM_I",
"PWM_J"
};
static char *bl_pwm_ao_name[] = {
"PWM_AO_A",
"PWM_AO_B",
"PWM_AO_C",
"PWM_AO_D",
"PWM_AO_E",
"PWM_AO_F",
"PWM_AO_G",
"PWM_AO_H"
};
static char *bl_pwm_vs_name[] = {"PWM_VS"};
static char *bl_pwm_invalid_name[] = {"invalid"};
enum bl_pwm_port_e bl_pwm_str_to_num(const char *str)
{
enum bl_pwm_port_e pwm_port = BL_PWM_MAX;
int i, cnt;
cnt = ARRAY_SIZE(bl_pwm_name);
for (i = 0; i < cnt; i++) {
if (strcmp(str, bl_pwm_name[i]) == 0) {
pwm_port = i + BL_PWM_A;
return pwm_port;
}
}
cnt = ARRAY_SIZE(bl_pwm_ao_name);
for (i = 0; i < cnt; i++) {
if (strcmp(str, bl_pwm_ao_name[i]) == 0) {
pwm_port = i + BL_PWM_AO_A;
return pwm_port;
}
}
if (strcmp(str, bl_pwm_vs_name[0]) == 0) {
pwm_port = BL_PWM_VS;
return pwm_port;
}
return BL_PWM_MAX;
}
char *bl_pwm_num_to_str(unsigned int num)
{
unsigned int temp, cnt;
if (num < BL_PWM_AO_A) {
temp = num - BL_PWM_A;
cnt = ARRAY_SIZE(bl_pwm_name);
if (temp >= cnt)
return bl_pwm_invalid_name[0];
return bl_pwm_name[temp];
} else if (num < BL_PWM_VS) {
temp = num - BL_PWM_AO_A;
cnt = ARRAY_SIZE(bl_pwm_ao_name);
if (temp >= cnt)
return bl_pwm_invalid_name[0];
return bl_pwm_ao_name[temp];
} else if (num == BL_PWM_VS) {
return bl_pwm_vs_name[0];
}
return bl_pwm_invalid_name[0];
}
static unsigned int bl_level_mapping(struct aml_bl_drv_s *bdrv, unsigned int level)
{
unsigned int mid = bdrv->bconf.level_mid;
unsigned int mid_map = bdrv->bconf.level_mid_mapping;
unsigned int max = bdrv->bconf.level_max;
unsigned int min = bdrv->bconf.level_min;
if (mid == mid_map)
return level;
level = level > max ? max : level;
if (level >= mid && level <= max) {
level = (((level - mid) * (max - mid_map)) / (max - mid)) +
mid_map;
} else if (level >= min && level < mid) {
level = (((level - min) * (mid_map - min)) / (mid - min)) + min;
} else {
level = 0;
}
return level;
}
/* ****************************************************** */
static int bl_pwm_out_level_check(struct bl_pwm_config_s *bl_pwm)
{
int out_level = 0xff;
unsigned int pwm_range;
if (bl_pwm->pwm_duty_max > 100)
pwm_range = 255;
else
pwm_range = 100;
switch (bl_pwm->pwm_method) {
case BL_PWM_POSITIVE:
if (bl_pwm->pwm_duty == 0)
out_level = 0;
else if (bl_pwm->pwm_duty == pwm_range)
out_level = 1;
else
out_level = 0xff;
break;
case BL_PWM_NEGATIVE:
if (bl_pwm->pwm_duty == 0)
out_level = 1;
else if (bl_pwm->pwm_duty == pwm_range)
out_level = 0;
else
out_level = 0xff;
break;
default:
BLERR("%s: port %d: invalid pwm_method %d\n",
__func__, bl_pwm->pwm_port, bl_pwm->pwm_method);
break;
}
return out_level;
}
static void bl_set_pwm_vs(struct bl_pwm_config_s *bl_pwm,
unsigned int pol, unsigned int out_level)
{
unsigned int pwm_hi, n, sw;
unsigned int reg[5], vs[8], ve[8];
int i;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("%s: pwm_duty=%d, out_level=%d\n",
__func__, bl_pwm->pwm_duty, out_level);
}
if (out_level == 0) {
for (i = 0; i < 8; i++) {
vs[i] = 0x1fff;
ve[i] = 0;
}
} else if (out_level == 1) {
for (i = 0; i < 8; i++) {
vs[i] = 0;
ve[i] = 0x1fff;
}
} else {
pwm_hi = bl_pwm->pwm_level;
n = bl_pwm->pwm_freq;
sw = (bl_pwm->pwm_cnt * 10 / n + 5) / 10;
pwm_hi = (pwm_hi * 10 / n + 5) / 10;
pwm_hi = (pwm_hi > 1) ? pwm_hi : 1;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("pwm_vs: n=%d, sw=%d, pwm_high=%d\n",
n, sw, pwm_hi);
}
for (i = 0; i < n; i++) {
vs[i] = 1 + (sw * i);
ve[i] = vs[i] + pwm_hi - 1;
}
for (i = n; i < 8; i++) {
vs[i] = 0x1fff;
ve[i] = 0x1fff;
}
}
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
for (i = 0; i < 8; i++) {
BLPR("pwm_vs: vs[%d]=%d, ve[%d]=%d\n",
i, vs[i], i, ve[i]);
}
}
if (pwm_init_cfg.pwm_vs_reg_cnt < 5) {
BLERR("%s: pwm_vs reg error\n", __func__);
return;
}
for (i = 0; i < pwm_init_cfg.pwm_vs_reg_cnt; i++)
reg[i] = pwm_init_cfg.pwm_vs_reg[i];
lcd_vcbus_write(reg[0], (pol << 31) |
(2 << 14) | /* vsync latch */
(ve[0] << 16) | (vs[0]));
lcd_vcbus_write(reg[1], (ve[1] << 16) | (vs[1]));
lcd_vcbus_write(reg[2], (ve[2] << 16) | (vs[2]));
lcd_vcbus_write(reg[3], (ve[3] << 16) | (vs[3]));
if (pwm_init_cfg.pwm_vs_flag) {
lcd_vcbus_setb(reg[4], 1, 31, 1);
lcd_vcbus_setb(reg[0], vs[4], 0, 13);
lcd_vcbus_setb(reg[0], ve[4], 16, 13);
lcd_vcbus_write(reg[1], (ve[5] << 16) | (vs[5]));
lcd_vcbus_write(reg[2], (ve[6] << 16) | (vs[6]));
lcd_vcbus_write(reg[3], (ve[7] << 16) | (vs[7]));
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("VPU_VPU_PWM_V4=0x%08x\n", lcd_vcbus_read(reg[0]));
BLPR("VPU_VPU_PWM_V5=0x%08x\n", lcd_vcbus_read(reg[1]));
BLPR("VPU_VPU_PWM_V6=0x%08x\n", lcd_vcbus_read(reg[2]));
BLPR("VPU_VPU_PWM_V7=0x%08x\n", lcd_vcbus_read(reg[3]));
}
lcd_vcbus_setb(reg[4], 0, 31, 1);
}
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("VPU_VPU_PWM_V0=0x%08x\n", lcd_vcbus_read(reg[0]));
BLPR("VPU_VPU_PWM_V1=0x%08x\n", lcd_vcbus_read(reg[1]));
BLPR("VPU_VPU_PWM_V2=0x%08x\n", lcd_vcbus_read(reg[2]));
BLPR("VPU_VPU_PWM_V3=0x%08x\n", lcd_vcbus_read(reg[3]));
}
}
static inline void bl_pwm_normal_state_print(struct pwm_state *state)
{
BLPR("pwm state: polarity=%d, duty_cycle=%lld, period=%lld, enabled=%d\n",
state->polarity, state->duty_cycle,
state->period, state->enabled);
}
static void bl_set_pwm_normal(struct bl_pwm_config_s *bl_pwm,
unsigned int pol, unsigned int out_level)
{
if (IS_ERR_OR_NULL(bl_pwm->pwm_data.pwm)) {
BLERR("%s: invalid bl_pwm_ch\n", __func__);
return;
}
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("pwm: pwm=0x%p, port=%d, meson_index=%d\n",
bl_pwm->pwm_data.pwm, bl_pwm->pwm_port,
bl_pwm->pwm_data.meson_index);
}
bl_pwm->pwm_data.state.polarity = pol;
bl_pwm->pwm_data.state.duty_cycle = bl_pwm->pwm_level;
bl_pwm->pwm_data.state.period = bl_pwm->pwm_cnt;
bl_pwm->pwm_data.state.enabled = true;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV)
bl_pwm_normal_state_print(&bl_pwm->pwm_data.state);
if (out_level == 0xff) {
pwm_constant_disable(bl_pwm->pwm_data.meson,
bl_pwm->pwm_data.meson_index);
} else {
/* pwm duty 100% or 0% special control */
pwm_constant_enable(bl_pwm->pwm_data.meson,
bl_pwm->pwm_data.meson_index);
}
pwm_apply_state(bl_pwm->pwm_data.pwm, &bl_pwm->pwm_data.state);
}
void bl_pwm_ctrl(struct bl_pwm_config_s *bl_pwm, int status)
{
struct pwm_state pstate;
unsigned int pol = 0, out_level = 0xff;
if (bl_pwm->pwm_method == BL_PWM_NEGATIVE)
pol = 1;
if (status) {
/* enable pwm */
out_level = bl_pwm_out_level_check(bl_pwm);
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("port %d: pwm_duty=%d, out_level=%d, pol=%s\n",
bl_pwm->pwm_port, bl_pwm->pwm_duty, out_level,
(pol ? "negative" : "positive"));
}
switch (bl_pwm->pwm_port) {
case BL_PWM_A:
case BL_PWM_B:
case BL_PWM_C:
case BL_PWM_D:
case BL_PWM_E:
case BL_PWM_F:
case BL_PWM_G:
case BL_PWM_H:
case BL_PWM_I:
case BL_PWM_J:
case BL_PWM_AO_A:
case BL_PWM_AO_B:
case BL_PWM_AO_C:
case BL_PWM_AO_D:
case BL_PWM_AO_E:
case BL_PWM_AO_F:
case BL_PWM_AO_G:
case BL_PWM_AO_H:
bl_set_pwm_normal(bl_pwm, pol, out_level);
break;
case BL_PWM_VS:
bl_set_pwm_vs(bl_pwm, pol, out_level);
break;
default:
BLERR("%s: invalid pwm_port: 0x%x\n",
__func__, bl_pwm->pwm_port);
break;
}
} else {
/* disable pwm */
switch (bl_pwm->pwm_port) {
case BL_PWM_A:
case BL_PWM_B:
case BL_PWM_C:
case BL_PWM_D:
case BL_PWM_E:
case BL_PWM_F:
case BL_PWM_G:
case BL_PWM_H:
case BL_PWM_I:
case BL_PWM_J:
case BL_PWM_AO_A:
case BL_PWM_AO_B:
case BL_PWM_AO_C:
case BL_PWM_AO_D:
case BL_PWM_AO_E:
case BL_PWM_AO_F:
case BL_PWM_AO_G:
case BL_PWM_AO_H:
if (IS_ERR_OR_NULL(bl_pwm->pwm_data.pwm)) {
BLERR("%s: invalid bl_pwm_ch\n", __func__);
return;
}
pwm_get_state(bl_pwm->pwm_data.pwm, &pstate);
pwm_constant_enable(bl_pwm->pwm_data.meson,
bl_pwm->pwm_data.meson_index);
if (bl_pwm->pwm_method)
pstate.polarity = 0;
else
pstate.polarity = 1;
pstate.duty_cycle = 0;
pstate.enabled = 1;
pstate.period = bl_pwm->pwm_data.state.period;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV)
bl_pwm_normal_state_print(&pstate);
pwm_apply_state(bl_pwm->pwm_data.pwm, &(pstate));
break;
case BL_PWM_VS:
bl_set_pwm_vs(bl_pwm, pol, 0);
break;
default:
BLERR("%s: invalid pwm_port: 0x%x\n",
__func__, bl_pwm->pwm_port);
break;
}
}
}
static void bl_set_pwm(struct aml_bl_drv_s *bdrv, struct bl_pwm_config_s *bl_pwm)
{
if ((bdrv->state & BL_STATE_BL_INIT_ON) == 0)
bl_pwm->pwm_duty_save = bl_pwm->pwm_duty;
if (bdrv->state & BL_STATE_BL_ON ||
bdrv->state & BL_STATE_BL_INIT_ON) {
bl_pwm_ctrl(bl_pwm, 1);
} else {
if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL)
BLERR("%s: bl_drv state is off\n", __func__);
}
}
void bl_pwm_set_duty(struct aml_bl_drv_s *bdrv, struct bl_pwm_config_s *bl_pwm)
{
unsigned long long temp;
if (bdrv->pwm_bypass)
return;
if (bdrv->pwm_duty_free) {
if (bl_pwm->pwm_duty_max > 100) {
if (bl_pwm->pwm_duty > 255) {
BLERR("pwm_duty %d > 255, reset to 255\n",
bl_pwm->pwm_duty);
bl_pwm->pwm_duty = 255;
}
} else {
if (bl_pwm->pwm_duty > 100) {
BLERR("pwm_duty %d%% > 100%%, reset to 100%%\n",
bl_pwm->pwm_duty);
bl_pwm->pwm_duty = 100;
}
}
} else {
if (bl_pwm->pwm_duty > bl_pwm->pwm_duty_max) {
BLERR("pwm_duty %d > duty_max %d, reset to duty_max\n",
bl_pwm->pwm_duty, bl_pwm->pwm_duty_max);
bl_pwm->pwm_duty = bl_pwm->pwm_duty_max;
} else if (bl_pwm->pwm_duty < bl_pwm->pwm_duty_min) {
BLERR("pwm_duty %d is < %d, reset to duty_min\n",
bl_pwm->pwm_duty, bl_pwm->pwm_duty_min);
bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
}
}
temp = bl_pwm->pwm_cnt;
if (bl_pwm->pwm_duty_max > 100) {
bl_pwm->pwm_level =
bl_do_div(((temp * bl_pwm->pwm_duty) + 127), 255);
} else {
bl_pwm->pwm_level =
bl_do_div(((temp * bl_pwm->pwm_duty) + 50), 100);
}
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("pwm port %d: pwm_max=%d, pwm_min=%d, pwm_level=%d\n",
bl_pwm->pwm_port,
bl_pwm->pwm_max, bl_pwm->pwm_min, bl_pwm->pwm_level);
if (bl_pwm->pwm_duty_max > 100) {
BLPR("duty=%d(%d%%), duty_max=%d, duty_min=%d\n",
bl_pwm->pwm_duty,
bl_pwm->pwm_duty * 100 / 255,
bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min);
} else {
BLPR("duty=%d%%, duty_max=%d%%, duty_min=%d%%\n",
bl_pwm->pwm_duty,
bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min);
}
}
bl_set_pwm(bdrv, bl_pwm);
}
void bl_pwm_set_level(struct aml_bl_drv_s *bdrv,
struct bl_pwm_config_s *bl_pwm, unsigned int level)
{
unsigned int min = bl_pwm->level_min;
unsigned int max = bl_pwm->level_max;
unsigned int pwm_max = bl_pwm->pwm_max;
unsigned int pwm_min = bl_pwm->pwm_min;
unsigned long long temp;
if (bdrv->pwm_bypass)
return;
level = bl_level_mapping(bdrv, level);
max = bl_level_mapping(bdrv, max);
min = bl_level_mapping(bdrv, min);
if (max <= min || level < min) {
bl_pwm->pwm_level = pwm_min;
} else {
temp = pwm_max - pwm_min;
bl_pwm->pwm_level =
bl_do_div((temp * (level - min)), (max - min)) +
pwm_min;
}
temp = bl_pwm->pwm_level;
if (bl_pwm->pwm_duty_max > 100) {
bl_pwm->pwm_duty =
(bl_do_div((temp * 2550), bl_pwm->pwm_cnt) + 5) / 10;
} else {
bl_pwm->pwm_duty =
(bl_do_div((temp * 1000), bl_pwm->pwm_cnt) + 5) / 10;
}
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
BLPR("pwm port %d: level=%d, level_max=%d, level_min=%d\n",
bl_pwm->pwm_port, level, max, min);
if (bl_pwm->pwm_duty_max > 100) {
BLPR("duty=%d(%d%%), pwm max=%d, min=%d, pwm_lvl=%d\n",
bl_pwm->pwm_duty,
bl_pwm->pwm_duty * 100 / 255,
bl_pwm->pwm_max, bl_pwm->pwm_min,
bl_pwm->pwm_level);
} else {
BLPR("duty=%d%%, pwm max=%d, min=%d, pwm_level=%d\n",
bl_pwm->pwm_duty,
bl_pwm->pwm_max, bl_pwm->pwm_min,
bl_pwm->pwm_level);
}
}
bl_set_pwm(bdrv, bl_pwm);
}
void bl_pwm_config_init(struct bl_pwm_config_s *bl_pwm)
{
unsigned int cnt;
unsigned long long temp;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) {
BLPR("%s pwm_port %d: freq = %u\n",
__func__, bl_pwm->pwm_port, bl_pwm->pwm_freq);
}
switch (bl_pwm->pwm_port) {
case BL_PWM_VS:
cnt = lcd_vcbus_read(ENCL_VIDEO_MAX_LNCNT) + 1;
bl_pwm->pwm_cnt = cnt;
break;
default:
/* for pwm api: pwm_period */
bl_pwm->pwm_cnt = 1000000000 / bl_pwm->pwm_freq;
break;
}
temp = bl_pwm->pwm_cnt;
if (bl_pwm->pwm_duty_max > 100) {
bl_pwm->pwm_max = bl_do_div((temp * bl_pwm->pwm_duty_max), 255);
bl_pwm->pwm_min = bl_do_div((temp * bl_pwm->pwm_duty_min), 255);
} else {
bl_pwm->pwm_max = bl_do_div((temp * bl_pwm->pwm_duty_max), 100);
bl_pwm->pwm_min = bl_do_div((temp * bl_pwm->pwm_duty_min), 100);
}
if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) {
BLPR("pwm_cnt = %u, pwm_max = %u, pwm_min = %u\n",
bl_pwm->pwm_cnt, bl_pwm->pwm_max, bl_pwm->pwm_min);
}
}
int bl_pwm_channel_register(struct device *dev, phandle pwm_phandle,
struct bl_pwm_config_s *bl_pwm)
{
struct device_node *pnode = NULL;
struct device_node *child;
const char *pwm_str;
unsigned int pwm_port, pwm_index = BL_PWM_MAX;
int ret = 0;
if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL)
BLPR("%s\n", __func__);
if (!bl_pwm) {
BLERR("%s: bl_pwm is null\n", __func__);
return -1;
}
pnode = of_find_node_by_phandle(pwm_phandle);
if (!pnode) {
BLERR("%s: can't find bl_pwm_config node\n", __func__);
return -1;
}
/*request for pwm device */
for_each_child_of_node(pnode, child) {
ret = of_property_read_string(child, "pwm_port", &pwm_str);
if (ret) {
BLERR("%s: failed to get pwm_port\n", __func__);
return -1;
}
pwm_port = bl_pwm_str_to_num(pwm_str);
if (pwm_port >= BL_PWM_VS)
continue;
ret = of_property_read_u32_index(child, "pwms", 1, &pwm_index);
if (ret) {
BLERR("failed to get meson_pwm_index\n");
return ret;
}
if (pwm_port != bl_pwm->pwm_port)
continue;
bl_pwm->pwm_data.meson_index = pwm_index;
bl_pwm->pwm_data.pwm = devm_of_pwm_get(dev, child, NULL);
if (IS_ERR_OR_NULL(bl_pwm->pwm_data.pwm)) {
ret = PTR_ERR(bl_pwm->pwm_data.pwm);
BLERR("unable to request %s(%d): 0x%x\n", pwm_str, pwm_port, ret);
continue;
}
bl_pwm->pwm_data.meson = to_meson_pwm(bl_pwm->pwm_data.pwm->chip);
pwm_init_state(bl_pwm->pwm_data.pwm, &bl_pwm->pwm_data.state);
BLPR("register %s(%d) 0x%px\n",
pwm_str, bl_pwm->pwm_data.meson_index, bl_pwm->pwm_data.pwm);
}
return ret;
}
static unsigned int pwm_vs_reg[] = {
VPU_VPU_PWM_V0,
VPU_VPU_PWM_V1,
VPU_VPU_PWM_V2,
VPU_VPU_PWM_V3,
VPU_VPU_PWM_H0
};
static unsigned int pwm_vs_reg_t7[] = {
VPU_VPU_PWM_V0_T7,
VPU_VPU_PWM_V1_T7,
VPU_VPU_PWM_V2_T7,
VPU_VPU_PWM_V3_T7,
VPU_VPU_PWM_H0_T7
};
int bl_pwm_init_config_probe(struct bl_data_s *bdata)
{
switch (bdata->chip_type) {
case LCD_CHIP_T7:
case LCD_CHIP_T3:
pwm_init_cfg.pwm_vs_reg = pwm_vs_reg_t7;
pwm_init_cfg.pwm_vs_reg_cnt = ARRAY_SIZE(pwm_vs_reg_t7);
break;
default:
pwm_init_cfg.pwm_vs_reg = pwm_vs_reg;
pwm_init_cfg.pwm_vs_reg_cnt = ARRAY_SIZE(pwm_vs_reg);
break;
}
pwm_init_cfg.pwm_vs_flag = bdata->pwm_vs_flag;
return 0;
}