/*
|
* Allwinner SoCs display driver.
|
*
|
* Copyright (C) 2016 Allwinner.
|
*
|
* This file is licensed under the terms of the GNU General Public
|
* License version 2. This program is licensed "as is" without any
|
* warranty of any kind, whether express or implied.
|
*/
|
|
#include "de/bsp_display.h"
|
#include "disp_sys_intf.h"
|
#include "asm/cacheflush.h"
|
#include <linux/clk/sunxi.h>
|
#include <linux/sunxi-gpio.h>
|
#include "dev_disp.h"
|
|
/* cache flush flags */
|
#define CACHE_FLUSH_I_CACHE_REGION 0
|
#define CACHE_FLUSH_D_CACHE_REGION 1
|
#define CACHE_FLUSH_CACHE_REGION 2
|
#define CACHE_CLEAN_D_CACHE_REGION 3
|
#define CACHE_CLEAN_FLUSH_D_CACHE_REGION 4
|
#define CACHE_CLEAN_FLUSH_CACHE_REGION 5
|
|
void *disp_sys_malloc(u32 size)
|
{
|
return kmalloc(size, GFP_KERNEL | __GFP_ZERO);
|
}
|
/*
|
*******************************************************************************
|
* OSAL_CacheRangeFlush
|
*
|
* Description:
|
* Cache flush
|
*
|
* Parameters:
|
* address : start address to be flush
|
* length : size
|
* flags : flush flags
|
*
|
* Return value:
|
* void
|
*
|
* note:
|
* void
|
*
|
*******************************************************************************
|
*/
|
void disp_sys_cache_flush(void *address, u32 length, u32 flags)
|
{
|
if (address == NULL || length == 0)
|
return;
|
|
switch (flags) {
|
case CACHE_FLUSH_I_CACHE_REGION:
|
break;
|
|
case CACHE_FLUSH_D_CACHE_REGION:
|
break;
|
|
case CACHE_FLUSH_CACHE_REGION:
|
break;
|
|
case CACHE_CLEAN_D_CACHE_REGION:
|
break;
|
|
case CACHE_CLEAN_FLUSH_D_CACHE_REGION:
|
break;
|
|
case CACHE_CLEAN_FLUSH_CACHE_REGION:
|
break;
|
|
default:
|
break;
|
}
|
}
|
|
/*
|
*******************************************************************************
|
* disp_sys_register_irq
|
*
|
* Description:
|
* irq register
|
*
|
* Parameters:
|
* irqno ��input. irq no
|
* flags ��input.
|
* Handler ��input. isr handler
|
* pArg ��input. para
|
* DataSize ��input. len of para
|
* prio ��input. priority
|
|
*
|
* Return value:
|
*
|
*
|
* note:
|
* typedef s32 (*ISRCallback)( void *pArg)��
|
*
|
*******************************************************************************
|
*/
|
int disp_sys_register_irq(u32 IrqNo, u32 Flags, void *Handler, void *pArg,
|
u32 DataSize, u32 Prio)
|
{
|
__inf("%s, irqNo=%d, Handler=0x%p, pArg=0x%p\n", __func__, IrqNo,
|
Handler, pArg);
|
return request_irq(IrqNo, (irq_handler_t) Handler, 0x0,
|
"dispaly", pArg);
|
}
|
|
/*
|
*******************************************************************************
|
* disp_sys_unregister_irq
|
*
|
* Description:
|
* irq unregister
|
*
|
* Parameters:
|
* irqno ��input. irq no
|
* handler ��input. isr handler
|
* Argment ��input. para
|
*
|
* Return value:
|
* void
|
*
|
* note:
|
* void
|
*
|
*******************************************************************************
|
*/
|
void disp_sys_unregister_irq(u32 IrqNo, void *Handler, void *pArg)
|
{
|
free_irq(IrqNo, pArg);
|
}
|
|
/*
|
*******************************************************************************
|
* disp_sys_enable_irq
|
*
|
* Description:
|
* enable irq
|
*
|
* Parameters:
|
* irqno ��input. irq no
|
*
|
* Return value:
|
* void
|
*
|
* note:
|
* void
|
*
|
*******************************************************************************
|
*/
|
void disp_sys_enable_irq(u32 IrqNo)
|
{
|
/* enable_irq(IrqNo); */
|
}
|
|
/*
|
*******************************************************************************
|
* disp_sys_disable_irq
|
*
|
* Description:
|
* disable irq
|
*
|
* Parameters:
|
* irqno ��input. irq no
|
*
|
* Return value:
|
* void
|
*
|
* note:
|
* void
|
*
|
*******************************************************************************
|
*/
|
void disp_sys_disable_irq(u32 IrqNo)
|
{
|
/* disable_irq(IrqNo); */
|
}
|
|
/* type: 0:invalid, 1: int; 2:str, 3: gpio */
|
int disp_sys_script_get_item(char *main_name, char *sub_name, int value[],
|
int type)
|
{
|
char compat[32];
|
u32 len = 0;
|
struct device_node *node;
|
int ret = 0;
|
struct gpio_config config;
|
|
len = sprintf(compat, "allwinner,sunxi-%s", main_name);
|
if (len > 32)
|
__wrn("size of mian_name is out of range\n");
|
|
node = of_find_compatible_node(NULL, NULL, compat);
|
if (!node) {
|
__wrn("of_find_compatible_node %s fail\n", compat);
|
return ret;
|
}
|
|
if (type == 1) {
|
if (of_property_read_u32_array(node, sub_name, value, 1))
|
__inf("of_property_read_u32_array %s.%s fail\n",
|
main_name, sub_name);
|
else
|
ret = type;
|
} else if (type == 2) {
|
const char *str;
|
|
if (of_property_read_string(node, sub_name, &str))
|
__inf("of_property_read_string %s.%s fail\n", main_name,
|
sub_name);
|
else {
|
ret = type;
|
memcpy((void *)value, str, strlen(str) + 1);
|
}
|
} else if (type == 3) {
|
struct disp_gpio_set_t *gpio_info =
|
(struct disp_gpio_set_t *)value;
|
int gpio;
|
|
gpio =
|
of_get_named_gpio_flags(node, sub_name, 0,
|
(enum of_gpio_flags *)&config);
|
if (!gpio_is_valid(gpio))
|
goto exit;
|
|
gpio_info->gpio = config.gpio;
|
gpio_info->mul_sel = config.mul_sel;
|
gpio_info->pull = config.pull;
|
gpio_info->drv_level = config.drv_level;
|
gpio_info->data = config.data;
|
memcpy(gpio_info->gpio_name, sub_name, strlen(sub_name) + 1);
|
__inf("%s.%s gpio=%d,mul_sel=%d,data:%d\n", main_name, sub_name,
|
gpio_info->gpio, gpio_info->mul_sel, gpio_info->data);
|
ret = type;
|
}
|
|
exit:
|
return ret;
|
}
|
EXPORT_SYMBOL(disp_sys_script_get_item);
|
|
int disp_sys_get_ic_ver(void)
|
{
|
return 0;
|
}
|
|
int disp_sys_gpio_request(struct disp_gpio_set_t *gpio_list,
|
u32 group_count_max)
|
{
|
int ret = 0;
|
struct gpio_config pin_cfg;
|
char pin_name[32];
|
u32 config;
|
char pintype[50] = SUNXI_PINCTRL;
|
|
if (gpio_list == NULL) {
|
__wrn("%s: gpio list is null\n", __func__);
|
return 0;
|
}
|
|
pin_cfg.gpio = gpio_list->gpio;
|
pin_cfg.mul_sel = gpio_list->mul_sel;
|
pin_cfg.pull = gpio_list->pull;
|
pin_cfg.drv_level = gpio_list->drv_level;
|
pin_cfg.data = gpio_list->data;
|
ret = gpio_request(pin_cfg.gpio, NULL);
|
if (ret != 0) {
|
__wrn("%s failed, gpio_name=%s, gpio=%d, ret=%d\n", __func__,
|
gpio_list->gpio_name, gpio_list->gpio, ret);
|
return 0;
|
}
|
|
__inf("%s, gpio_name=%s, gpio=%d, <%d,%d,%d,%d>ret=%d\n",
|
__func__, gpio_list->gpio_name, gpio_list->gpio,
|
gpio_list->mul_sel, gpio_list->pull, gpio_list->drv_level,
|
gpio_list->data, ret);
|
|
ret = pin_cfg.gpio;
|
|
if (!IS_AXP_PIN(pin_cfg.gpio)) {
|
/*
|
* valid pin of sunxi-pinctrl,
|
* config pin attributes individually.
|
*/
|
sunxi_gpio_to_name(pin_cfg.gpio, pin_name);
|
config =
|
SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, pin_cfg.mul_sel);
|
ret = pin_config_set(pintype, pin_name, config);
|
if (ret == -EINVAL) {
|
/*try r_pio*/
|
snprintf(pintype, 50, SUNXI_R_PINCTRL);
|
ret = pin_config_set(pintype, pin_name, config);
|
}
|
if (pin_cfg.pull != GPIO_PULL_DEFAULT) {
|
config =
|
SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
|
pin_cfg.pull);
|
pin_config_set(pintype, pin_name, config);
|
}
|
if (pin_cfg.drv_level != GPIO_DRVLVL_DEFAULT) {
|
config =
|
SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV,
|
pin_cfg.drv_level);
|
pin_config_set(pintype, pin_name, config);
|
}
|
if (pin_cfg.data != GPIO_DATA_DEFAULT) {
|
config =
|
SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
|
pin_cfg.data);
|
pin_config_set(pintype, pin_name, config);
|
}
|
} else if (IS_AXP_PIN(pin_cfg.gpio)) {
|
/* valid pin of axp-pinctrl,
|
* config pin attributes individually.
|
*/
|
sunxi_gpio_to_name(pin_cfg.gpio, pin_name);
|
if (pin_cfg.data != GPIO_DATA_DEFAULT) {
|
config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
|
pin_cfg.data);
|
pin_config_set(AXP_PINCTRL, pin_name, config);
|
}
|
} else {
|
pr_warn("invalid pin [%d] from sys-config\n", pin_cfg.gpio);
|
}
|
|
return pin_cfg.gpio;
|
}
|
EXPORT_SYMBOL(disp_sys_gpio_request);
|
|
int disp_sys_gpio_request_simple(struct disp_gpio_set_t *gpio_list,
|
u32 group_count_max)
|
{
|
#if 0
|
int ret = 0;
|
struct gpio_config pin_cfg;
|
|
if (gpio_list == NULL)
|
return 0;
|
|
pin_cfg.gpio = gpio_list->gpio;
|
pin_cfg.mul_sel = gpio_list->mul_sel;
|
pin_cfg.pull = gpio_list->pull;
|
pin_cfg.drv_level = gpio_list->drv_level;
|
pin_cfg.data = gpio_list->data;
|
ret = gpio_request(pin_cfg.gpio, NULL);
|
if (ret != 0) {
|
__wrn("%s failed, gpio_name=%s, gpio=%d, ret=%d\n", __func__,
|
gpio_list->gpio_name, gpio_list->gpio, ret);
|
return ret;
|
}
|
|
__inf("%s, gpio_name=%s, gpio=%d, ret=%d\n", __func__,
|
gpio_list->gpio_name, gpio_list->gpio, ret);
|
ret = pin_cfg.gpio;
|
|
return ret;
|
#endif
|
return 0;
|
}
|
int disp_sys_gpio_release(int p_handler, s32 if_release_to_default_status)
|
{
|
if (p_handler)
|
gpio_free(p_handler);
|
else
|
__wrn("OSAL_GPIO_Release, hdl is NULL\n");
|
|
return 0;
|
}
|
EXPORT_SYMBOL(disp_sys_gpio_release);
|
|
/* direction: 0:input, 1:output */
|
int disp_sys_gpio_set_direction(u32 p_handler, u32 direction,
|
const char *gpio_name)
|
{
|
int ret = -1;
|
|
if (p_handler) {
|
if (direction) {
|
s32 value;
|
|
value = __gpio_get_value(p_handler);
|
ret = gpio_direction_output(p_handler, value);
|
if (ret != 0)
|
__wrn("gpio_direction_output fail!\n");
|
} else {
|
ret = gpio_direction_input(p_handler);
|
if (ret != 0)
|
__wrn("gpio_direction_input fail!\n");
|
}
|
} else {
|
__wrn("OSAL_GPIO_DevSetONEPIN_IO_STATUS, hdl is NULL\n");
|
ret = -1;
|
}
|
return ret;
|
}
|
|
int disp_sys_gpio_get_value(u32 p_handler, const char *gpio_name)
|
{
|
if (p_handler)
|
return __gpio_get_value(p_handler);
|
__wrn("OSAL_GPIO_DevREAD_ONEPIN_DATA, hdl is NULL\n");
|
|
return -1;
|
}
|
|
int disp_sys_gpio_set_value(u32 p_handler, u32 value_to_gpio,
|
const char *gpio_name)
|
{
|
if (p_handler)
|
__gpio_set_value(p_handler, value_to_gpio);
|
else
|
__wrn("OSAL_GPIO_DevWRITE_ONEPIN_DATA, hdl is NULL\n");
|
|
return 0;
|
}
|
|
int disp_sys_pin_set_state(char *dev_name, char *name)
|
{
|
char compat[32];
|
u32 len = 0;
|
struct device_node *node;
|
struct platform_device *pdev;
|
struct pinctrl *pctl;
|
struct pinctrl_state *state;
|
int ret = -1;
|
|
len = sprintf(compat, "allwinner,sunxi-%s", dev_name);
|
if (len > 32)
|
__wrn("size of mian_name is out of range\n");
|
|
node = of_find_compatible_node(NULL, NULL, compat);
|
if (!node) {
|
__wrn("of_find_compatible_node %s fail\n", compat);
|
goto exit;
|
}
|
|
pdev = of_find_device_by_node(node);
|
if (!node) {
|
__wrn("of_find_device_by_node for %s fail\n", compat);
|
goto exit;
|
}
|
|
pctl = pinctrl_get(&pdev->dev);
|
if (IS_ERR(pctl)) {
|
/*not every lcd need pin config*/
|
__inf("pinctrl_get for %s fail\n", compat);
|
ret = PTR_ERR(pctl);
|
goto exit;
|
}
|
|
state = pinctrl_lookup_state(pctl, name);
|
if (IS_ERR(state)) {
|
__wrn("pinctrl_lookup_state for %s fail\n", compat);
|
ret = PTR_ERR(state);
|
goto exit;
|
}
|
|
ret = pinctrl_select_state(pctl, state);
|
if (ret < 0) {
|
__wrn("pinctrl_select_state(%s) for %s fail\n", name, compat);
|
goto exit;
|
}
|
ret = 0;
|
exit:
|
return ret;
|
}
|
EXPORT_SYMBOL(disp_sys_pin_set_state);
|
|
int disp_sys_power_enable(char *name)
|
{
|
int ret = 0;
|
#if defined(CONFIG_AW_AXP) || defined(CONFIG_REGULATOR)
|
struct regulator *regu = NULL;
|
|
|
#ifdef CONFIG_SUNXI_REGULATOR_DT
|
regu = regulator_get(g_disp_drv.dev, name);
|
#else
|
regu = regulator_get(NULL, name);
|
#endif
|
if (IS_ERR(regu)) {
|
__wrn("some error happen, fail to get regulator %s\n", name);
|
goto exit;
|
}
|
/* enalbe regulator */
|
ret = regulator_enable(regu);
|
if (ret != 0) {
|
__wrn("some error happen, fail to enable regulator %s!\n",
|
name);
|
goto exit1;
|
} else {
|
__inf("suceess to enable regulator %s!\n", name);
|
}
|
|
exit1:
|
/* put regulater, when module exit */
|
regulator_put(regu);
|
exit:
|
#endif
|
return ret;
|
}
|
EXPORT_SYMBOL(disp_sys_power_enable);
|
|
int disp_sys_power_disable(char *name)
|
{
|
int ret = 0;
|
#if defined(CONFIG_AW_AXP) || defined(CONFIG_REGULATOR)
|
struct regulator *regu = NULL;
|
|
#ifdef CONFIG_SUNXI_REGULATOR_DT
|
regu = regulator_get(g_disp_drv.dev, name);
|
#else
|
regu = regulator_get(NULL, name);
|
#endif
|
if (IS_ERR(regu)) {
|
__wrn("some error happen, fail to get regulator %s\n", name);
|
goto exit;
|
}
|
/* disalbe regulator */
|
ret = regulator_disable(regu);
|
if (ret != 0) {
|
__wrn("some error happen, fail to disable regulator %s!\n",
|
name);
|
goto exit1;
|
} else {
|
__inf("suceess to disable regulator %s!\n", name);
|
}
|
|
exit1:
|
/* put regulater, when module exit */
|
regulator_put(regu);
|
exit:
|
#endif
|
return ret;
|
}
|
EXPORT_SYMBOL(disp_sys_power_disable);
|
|
#if defined(CONFIG_PWM_SUNXI) || defined(CONFIG_PWM_SUNXI_NEW) || \
|
defined(CONFIG_PWM_SUNXI_GROUP)
|
uintptr_t disp_sys_pwm_request(u32 pwm_id)
|
{
|
uintptr_t ret = 0;
|
|
struct pwm_device *pwm_dev;
|
|
pwm_dev = pwm_request(pwm_id, "lcd");
|
|
if ((pwm_dev == NULL) || IS_ERR(pwm_dev)) {
|
__inf("disp_sys_pwm_request pwm %d fail!\n", pwm_id);
|
pwm_dev = NULL;
|
} else
|
__inf("disp_sys_pwm_request pwm %d success!\n", pwm_id);
|
ret = (uintptr_t) pwm_dev;
|
|
return ret;
|
}
|
|
int disp_sys_pwm_free(uintptr_t p_handler)
|
{
|
int ret = 0;
|
struct pwm_device *pwm_dev;
|
|
pwm_dev = (struct pwm_device *)p_handler;
|
if ((pwm_dev == NULL) || IS_ERR(pwm_dev)) {
|
__inf("disp_sys_pwm_free, handle is NULL!\n");
|
ret = -1;
|
} else {
|
pwm_free(pwm_dev);
|
__inf("disp_sys_pwm_free pwm %d\n", pwm_dev->pwm);
|
}
|
|
return ret;
|
}
|
|
int disp_sys_pwm_enable(uintptr_t p_handler)
|
{
|
int ret = 0;
|
struct pwm_device *pwm_dev;
|
|
pwm_dev = (struct pwm_device *)p_handler;
|
if ((pwm_dev == NULL) || IS_ERR(pwm_dev)) {
|
__inf("disp_sys_pwm_Enable, handle is NULL!\n");
|
ret = -1;
|
} else {
|
ret = pwm_enable(pwm_dev);
|
__inf("disp_sys_pwm_Enable pwm %d\n", pwm_dev->pwm);
|
}
|
|
return ret;
|
}
|
|
int disp_sys_pwm_disable(uintptr_t p_handler)
|
{
|
int ret = 0;
|
struct pwm_device *pwm_dev;
|
|
pwm_dev = (struct pwm_device *)p_handler;
|
if ((pwm_dev == NULL) || IS_ERR(pwm_dev)) {
|
__inf("disp_sys_pwm_Disable, handle is NULL!\n");
|
ret = -1;
|
} else {
|
pwm_disable(pwm_dev);
|
__inf("disp_sys_pwm_Disable pwm %d\n", pwm_dev->pwm);
|
}
|
|
return ret;
|
}
|
|
int disp_sys_pwm_config(uintptr_t p_handler, int duty_ns, int period_ns)
|
{
|
int ret = 0;
|
struct pwm_device *pwm_dev;
|
|
pwm_dev = (struct pwm_device *)p_handler;
|
if ((pwm_dev == NULL) || IS_ERR(pwm_dev)) {
|
__inf("disp_sys_pwm_Config, handle is NULL!\n");
|
ret = -1;
|
} else {
|
ret = pwm_config(pwm_dev, duty_ns, period_ns);
|
__debug("disp_sys_pwm_Config pwm %d, <%d / %d>\n",
|
pwm_dev->pwm, duty_ns, period_ns);
|
}
|
|
return ret;
|
}
|
|
int disp_sys_pwm_set_polarity(uintptr_t p_handler, int polarity)
|
{
|
int ret = 0;
|
struct pwm_device *pwm_dev;
|
|
pwm_dev = (struct pwm_device *)p_handler;
|
if ((pwm_dev == NULL) || IS_ERR(pwm_dev)) {
|
__inf("disp_sys_pwm_Set_Polarity, handle is NULL!\n");
|
ret = -1;
|
} else {
|
ret = pwm_set_polarity(pwm_dev, polarity);
|
__inf("disp_sys_pwm_Set_Polarity pwm %d, active %s\n",
|
pwm_dev->pwm, (polarity == 0) ? "high" : "low");
|
}
|
|
return ret;
|
}
|
#else
|
uintptr_t disp_sys_pwm_request(u32 pwm_id)
|
{
|
uintptr_t ret = 0;
|
|
return ret;
|
}
|
|
int disp_sys_pwm_free(uintptr_t p_handler)
|
{
|
int ret = 0;
|
|
return ret;
|
}
|
|
int disp_sys_pwm_enable(uintptr_t p_handler)
|
{
|
int ret = 0;
|
|
return ret;
|
}
|
|
int disp_sys_pwm_disable(uintptr_t p_handler)
|
{
|
int ret = 0;
|
|
return ret;
|
}
|
|
int disp_sys_pwm_config(uintptr_t p_handler, int duty_ns, int period_ns)
|
{
|
int ret = 0;
|
|
return ret;
|
}
|
|
int disp_sys_pwm_set_polarity(uintptr_t p_handler, int polarity)
|
{
|
int ret = 0;
|
|
return ret;
|
}
|
|
#endif
|