blob: e39c67f0c4f53234b5d30098bcb96b8616330794 [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/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/compat.h>
#include <linux/clk.h>
#include <linux/of_device.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vrr/vrr.h>
#include "vrr_drv.h"
#include "vrr_reg.h"
#include <linux/amlogic/gki_module.h>
#define VRR_CDEV_NAME "aml_vrr"
struct vrr_cdev_s {
dev_t devno;
struct class *class;
};
static struct vrr_cdev_s *vrr_cdev;
/* for driver global resource init:
* 0: none
* n: initialized cnt
*/
static unsigned char vrr_global_init_flag;
static unsigned int vrr_drv_init_state;
static struct aml_vrr_drv_s *vrr_drv[VRR_MAX_DRV];
static struct mutex vrr_mutex;
static unsigned int vrr_debug_print;
struct aml_vrr_drv_s *vrr_drv_get(int index)
{
if (index >= VRR_MAX_DRV)
return NULL;
return vrr_drv[index];
}
static int vrr_config_load(struct aml_vrr_drv_s *vdrv,
struct platform_device *pdev)
{
unsigned int temp;
int ret;
ret = of_property_read_u32(pdev->dev.of_node, "line_delay", &temp);
if (ret) {
VRRPR("[%d]: %s: can't find line_delay\n",
vdrv->index, __func__);
vdrv->line_dly = 150;
} else {
vdrv->line_dly = temp;
}
VRRPR("[%d]: %s: line_delay: %d\n",
vdrv->index, __func__, vdrv->line_dly);
return 0;
}
static void vrr_lcd_enable(struct aml_vrr_drv_s *vdrv, unsigned int mode)
{
unsigned int vsp_in, vsp_sel;
unsigned int line_dly, v_max, v_min;
unsigned int offset = 0;
if (!vdrv->vrr_dev) {
VRRERR("[%d]: %s: vrr_dev is null\n", vdrv->index, __func__);
return;
}
if (vdrv->state & VRR_STATE_EN)
return;
offset = vdrv->data->offset[vdrv->index];
v_max = vdrv->vrr_dev->vline_max;
v_min = vdrv->vrr_dev->vline_min;
line_dly = vdrv->line_dly;
if (mode) {
vsp_in = 2; //gpu source
vsp_sel = 0; //gpu input
vdrv->state |= VRR_STATE_MODE_SW;
} else {
vsp_in = 1; //hmdi source
vsp_sel = 1; //hdmi input
vdrv->state |= VRR_STATE_MODE_HW;
}
//vrr setting
vrr_reg_write((VENC_VRR_CTRL + offset),
(line_dly << 8) | //cfg_vsp_dly_num number
(0 << 4) | //cfg_vrr_frm_ths frame delay number
//cfg_vrr_vsp_en bit[2]=hdmi_in, bit[3]=gpu_in
(vsp_in << 2) |
(1 << 1) | //cfg_vrr_mode 0:normal 1:vrr
(vsp_sel << 0)); //cfg_vrr_vsp_sel 1:hdmi in 0:gpu in
vrr_reg_write((VENC_VRR_ADJ_LMT + offset),
(v_min << 16) | //cfg_vrr_min_vnum <= am_spdat[31:16]
(v_max << 0)); //cfg_vrr_max_vnum <= am_spdat[15:0]
vdrv->state |= (VRR_STATE_ENCL | VRR_STATE_EN);
if (vrr_debug_print & VRR_DBG_PR_NORMAL) {
VRRPR("VENC_VRR_CTRL = 0x%x\n",
vrr_reg_read(VENC_VRR_CTRL + offset));
VRRPR("VENC_VRR_ADJ_LMT = 0x%x\n",
vrr_reg_read(VENC_VRR_ADJ_LMT + offset));
}
VRRPR("[%d]: %s: state = 0x%x\n", vdrv->index, __func__, vdrv->state);
}
static void vrr_hdmi_enable(struct aml_vrr_drv_s *vdrv, unsigned int mode)
{
unsigned int vsp_in, vsp_sel;
unsigned int line_dly, v_max, v_min;
unsigned int offset = 0;
if (!vdrv->vrr_dev) {
VRRERR("[%d]: %s: vrr_dev is null\n", vdrv->index, __func__);
return;
}
if (vdrv->state & VRR_STATE_EN)
return;
offset = vdrv->data->offset[vdrv->index];
v_max = vdrv->vrr_dev->vline_max;
v_min = vdrv->vrr_dev->vline_min;
line_dly = vdrv->line_dly;
if (mode) {
vsp_in = 2; //gpu source
vsp_sel = 0; //gpu input
vdrv->state |= VRR_STATE_MODE_SW;
} else {
vsp_in = 1; //hmdi source
vsp_sel = 1; //hdmi input
vdrv->state |= VRR_STATE_MODE_HW;
}
//vrr setting
vrr_reg_write(VENP_VRR_CTRL + offset,
(line_dly << 8) | //cfg_vsp_dly_num number
(0 << 4) | //cfg_vrr_frm_ths frame delay number
//cfg_vrr_vsp_en bit[2]=hdmi_in, bit[3]=gpu_in
(vsp_in << 2) |
(1 << 1) | //cfg_vrr_mode 0:normal 1:vrr
(vsp_sel << 0)); //cfg_vrr_vsp_sel 1:hdmi in 0:gpu in
vrr_reg_write(VENP_VRR_ADJ_LMT + offset,
(v_min << 16) | //cfg_vrr_min_vnum
(v_max << 0)); //cfg_vrr_max_vnum
vdrv->state |= (VRR_STATE_ENCP | VRR_STATE_EN);
if (vrr_debug_print & VRR_DBG_PR_NORMAL) {
VRRPR("VENP_VRR_CTRL = 0x%x\n",
vrr_reg_read(VENP_VRR_CTRL + offset));
VRRPR("VENP_VRR_ADJ_LMT = 0x%x\n",
vrr_reg_read(VENP_VRR_ADJ_LMT + offset));
}
VRRPR("[%d]: %s: state = 0x%x\n", vdrv->index, __func__, vdrv->state);
}
static void vrr_line_delay_update(struct aml_vrr_drv_s *vdrv)
{
unsigned int reg, temp, offset = 0;
if (!vdrv->vrr_dev) {
VRRERR("[%d]: %s: no vrr_dev\n", vdrv->index, __func__);
return;
}
offset = vdrv->data->offset[vdrv->index];
switch (vdrv->vrr_dev->output_src) {
case VRR_OUTPUT_ENCP:
reg = VENP_VRR_CTRL + offset;
break;
case VRR_OUTPUT_ENCL:
reg = VENC_VRR_CTRL + offset;
break;
default:
VRRERR("[%d]: %s: invalid output_src\n", vdrv->index, __func__);
return;
}
temp = vrr_reg_getb(reg + offset, 8, 16);
if (temp == vdrv->line_dly)
return;
vrr_reg_setb(reg, vdrv->line_dly, 8, 16);
VRRPR("[%d]: %s: %d->%d\n",
vdrv->index, __func__, temp, vdrv->line_dly);
}
static void vrr_work_disable(struct aml_vrr_drv_s *vdrv)
{
unsigned int offset = 0;
offset = vdrv->data->offset[vdrv->index];
if (vdrv->state & VRR_STATE_ENCL)
vrr_reg_setb((VENC_VRR_CTRL + offset), 0, 1, 1);
if (vdrv->state & VRR_STATE_ENCP)
vrr_reg_setb((VENP_VRR_CTRL + offset), 0, 1, 1);
vdrv->state = 0;
}
static void vrr_set_venc_vspin(struct aml_vrr_drv_s *vdrv)
{
vrr_reg_setb(0x1202, 1, 28, 1);//vdin vsync input pulse
}
static int vrr_timert_start(struct aml_vrr_drv_s *vdrv)
{
vdrv->sw_timer.expires = jiffies + msecs_to_jiffies(vdrv->sw_timer_cnt);
add_timer(&vdrv->sw_timer);
return 0;
}
static int vrr_timer_stop(struct aml_vrr_drv_s *vdrv)
{
del_timer_sync(&vdrv->sw_timer);
return 0;
}
static void vrr_timer_handler(struct timer_list *timer)
{
struct aml_vrr_drv_s *vdrv = from_timer(vdrv, timer, sw_timer);
vrr_set_venc_vspin(vdrv);
if (vrr_debug_print & VRR_DBG_PR_ISR)
VRRPR("[%d]: %s\n", vdrv->index, __func__);
vrr_timert_start(vdrv);
}
int vrr_drv_func_en(struct aml_vrr_drv_s *vdrv, int flag)
{
VRRPR("[%d]: %s, flag=%d\n", vdrv->index, __func__, flag);
if (!vdrv->vrr_dev) {
VRRERR("[%d]: %s: invalid vrr_dev\n",
vdrv->index, __func__);
return -1;
}
if (flag) {
if (vdrv->vrr_dev->enable == 0) {
VRRERR("[%d]: %s: vrr_dev %s(%d) is forbidden\n",
vdrv->index, __func__,
vdrv->vrr_dev->name,
vdrv->vrr_dev->output_src);
return -1;
}
if (vdrv->enable)
return 0;
vdrv->enable = 1;
switch (vdrv->vrr_dev->output_src) {
case VRR_OUTPUT_ENCP:
vrr_hdmi_enable(vdrv, 0);
break;
case VRR_OUTPUT_ENCL:
vrr_lcd_enable(vdrv, 0);
break;
default:
break;
}
} else {
if (vdrv->enable == 0)
return 0;
vdrv->enable = 0;
if (vdrv->sw_timer_flag) {
vrr_timer_stop(vdrv);
vdrv->sw_timer_flag = 0;
VRRPR("[%d]: %s: stop sw_timer\n",
vdrv->index, __func__);
}
vrr_work_disable(vdrv);
}
return 0;
}
static int vrr_test_en(struct aml_vrr_drv_s *vdrv, int flag)
{
VRRPR("[%d]: %s, flag=%d\n", vdrv->index, __func__, flag);
switch (flag) {
case 1: /* lcd hw */
if (vdrv->state & VRR_STATE_EN)
vrr_work_disable(vdrv);
if (vdrv->sw_timer_flag) {
vrr_timer_stop(vdrv);
vdrv->sw_timer_flag = 0;
VRRPR("[%d]: %s: stop sw_timer\n",
vdrv->index, __func__);
}
vrr_lcd_enable(vdrv, 0);
break;
case 2: /* lcd sw */
if (vdrv->state & VRR_STATE_EN)
vrr_work_disable(vdrv);
vrr_lcd_enable(vdrv, 0);
if (vdrv->sw_timer_flag == 0) {
VRRPR("[%d]: %s: start sw_timer\n",
vdrv->index, __func__);
vrr_timert_start(vdrv);
vdrv->sw_timer_flag = 1;
}
break;
case 3: /* hdmi hw */
if (vdrv->state & VRR_STATE_EN)
vrr_work_disable(vdrv);
if (vdrv->sw_timer_flag) {
vrr_timer_stop(vdrv);
vdrv->sw_timer_flag = 0;
VRRPR("[%d]: %s: stop sw_timer\n",
vdrv->index, __func__);
}
vrr_hdmi_enable(vdrv, 0);
break;
case 4: /* hdmi sw */
if (vdrv->state & VRR_STATE_EN)
vrr_work_disable(vdrv);
vrr_hdmi_enable(vdrv, 0);
if (vdrv->sw_timer_flag == 0) {
VRRPR("[%d]: %s: start sw_timer\n",
vdrv->index, __func__);
vrr_timert_start(vdrv);
vdrv->sw_timer_flag = 1;
}
break;
case 0: /* disable */
default:
if (vdrv->sw_timer_flag) {
vrr_timer_stop(vdrv);
vdrv->sw_timer_flag = 0;
VRRPR("[%d]: %s: stop sw_timer\n",
vdrv->index, __func__);
}
vrr_work_disable(vdrv);
break;
}
return 0;
}
static int vrr_func_init(struct aml_vrr_drv_s *vdrv)
{
vdrv->sw_timer_flag = 0;
vdrv->sw_timer_cnt = 20;
timer_setup(&vdrv->sw_timer, &vrr_timer_handler, 0);
vdrv->sw_timer.expires = jiffies + msecs_to_jiffies(vdrv->sw_timer_cnt);
if (vrr_debug_print & VRR_DBG_PR_NORMAL)
VRRPR("[%d]: %s\n", vdrv->index, __func__);
return 0;
}
//===========================================================================
// For VRR Debug
//===========================================================================
static const char *vrr_debug_usage_str = {
"Usage:\n"
" cat /sys/class/aml_vrr/vrrx/status ; dump vrr status\n"
" echo en <val> >/sys/class/aml_vrr/vrrx/debug ; enable/disable vrr\n"
" echo line_dly <val> >/sys/class/aml_vrr/vrrx/debug ; set vrr lock line_dly\n"
"\n"
};
static ssize_t vrr_debug_help(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", vrr_debug_usage_str);
}
static ssize_t vrr_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct aml_vrr_drv_s *vdrv = dev_get_drvdata(dev);
unsigned int offset;
ssize_t len = 0;
offset = vdrv->data->offset[vdrv->index];
len += sprintf(buf + len, "vrr[%d] info\n", vdrv->index);
if (vdrv->vrr_dev) {
len += sprintf(buf + len, "dev->name: %s\n",
vdrv->vrr_dev->name);
len += sprintf(buf + len, "dev->output_src: %d\n",
vdrv->vrr_dev->output_src);
len += sprintf(buf + len, "dev->enable: %d\n",
vdrv->vrr_dev->enable);
len += sprintf(buf + len, "dev->vline_max: %d\n",
vdrv->vrr_dev->vline_max);
len += sprintf(buf + len, "dev->vline_min: %d\n",
vdrv->vrr_dev->vline_min);
len += sprintf(buf + len, "dev->vfreq_max: %d\n",
vdrv->vrr_dev->vfreq_max);
len += sprintf(buf + len, "dev->vfreq_min: %d\n",
vdrv->vrr_dev->vfreq_min);
}
len += sprintf(buf + len, "line_dly: %d\n", vdrv->line_dly);
len += sprintf(buf + len, "state: 0x%x\n", vdrv->state);
len += sprintf(buf + len, "enable: 0x%x\n", vdrv->enable);
/** vrr reg info **/
len += sprintf(buf + len, "VENC_VRR_CTRL: 0x%x\n",
vrr_reg_read(VENC_VRR_CTRL + offset));
len += sprintf(buf + len, "VENC_VRR_ADJ_LMT: 0x%x\n",
vrr_reg_read(VENC_VRR_ADJ_LMT + offset));
len += sprintf(buf + len, "VENC_VRR_CTRL1: 0x%x\n",
vrr_reg_read(VENC_VRR_CTRL1 + offset));
len += sprintf(buf + len, "VENP_VRR_CTRL: 0x%x\n",
vrr_reg_read(VENP_VRR_CTRL + offset));
len += sprintf(buf + len, "VENP_VRR_ADJ_LMT: 0x%x\n",
vrr_reg_read(VENP_VRR_ADJ_LMT + offset));
len += sprintf(buf + len, "VENP_VRR_CTRL1: 0x%x\n",
vrr_reg_read(VENP_VRR_CTRL1 + offset));
/** vrr timer **/
len += sprintf(buf + len, "sw_timer info\n");
len += sprintf(buf + len, "sw_timer_cnt: %d\n", vdrv->sw_timer_cnt);
len += sprintf(buf + len, "sw_timer_cnt state: %d\n",
vdrv->sw_timer_flag);
return len;
}
static ssize_t vrr_debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct aml_vrr_drv_s *vdrv = dev_get_drvdata(dev);
unsigned int temp;
int ret = 0;
switch (buf[0]) {
case 'e': /* en */
ret = sscanf(buf, "en %d", &temp);
if (ret == 1) {
mutex_lock(&vrr_mutex);
vrr_drv_func_en(vdrv, temp);
mutex_unlock(&vrr_mutex);
} else {
VRRERR("invalid data\n");
return -EINVAL;
}
break;
case 't': /* test */
ret = sscanf(buf, "test %d", &temp);
if (ret == 1) {
mutex_lock(&vrr_mutex);
vrr_test_en(vdrv, temp);
mutex_unlock(&vrr_mutex);
} else {
VRRERR("invalid data\n");
return -EINVAL;
}
break;
case 'p':
ret = sscanf(buf, "print %x", &temp);
if (ret == 1)
vrr_debug_print = temp;
VRRPR("[%d]: vrr_debug_print: 0x%x\n", vdrv->index, temp);
break;
case 'l':
ret = sscanf(buf, "line_dly %d", &temp);
if (ret == 1) {
mutex_lock(&vrr_mutex);
vdrv->line_dly = temp;
vrr_line_delay_update(vdrv);
mutex_unlock(&vrr_mutex);
}
VRRPR("[%d]: line_dly: %d\n", vdrv->index, vdrv->line_dly);
break;
case 'c':
ret = sscanf(buf, "cnt %d", &temp);
if (ret == 1)
vdrv->sw_timer_cnt = temp;
VRRPR("[%d]: sw_timer_cnt: %d\n", vdrv->index, temp);
break;
default:
VRRERR("wrong command\n");
break;
}
return count;
}
static struct device_attribute vrr_debug_attrs[] = {
__ATTR(help, 0444, vrr_debug_help, NULL),
__ATTR(status, 0444, vrr_status_show, NULL),
__ATTR(active, 0444, vrr_active_status_show, NULL),
__ATTR(debug, 0644, vrr_debug_help, vrr_debug_store),
};
static int vrr_debug_file_creat(struct aml_vrr_drv_s *vdrv)
{
int i;
for (i = 0; i < ARRAY_SIZE(vrr_debug_attrs); i++) {
if (device_create_file(vdrv->dev, &vrr_debug_attrs[i])) {
VRRERR("[%d]: create lcd debug attribute %s fail\n",
vdrv->index, vrr_debug_attrs[i].attr.name);
}
}
return 0;
}
static int vrr_debug_file_remove(struct aml_vrr_drv_s *vdrv)
{
int i;
for (i = 0; i < ARRAY_SIZE(vrr_debug_attrs); i++)
device_remove_file(vdrv->dev, &vrr_debug_attrs[i]);
return 0;
}
/* ************************************************************* */
int aml_vrr_register_device(struct vrr_device_s *vrr_dev, int index)
{
if (vrr_global_init_flag == 0)
return 0;
if (!vrr_dev) {
VRRERR("%s: vrr_dev is null\n", __func__);
return -1;
}
if (index >= VRR_MAX_DRV) {
VRRERR("%s: invalid index: %d, (%s %d)\n",
__func__, index, vrr_dev->name, vrr_dev->output_src);
return -1;
}
if (!vrr_drv[index]) {
VRRERR("[%d]: %s: vrr_drv is null\n", index, __func__);
return -1;
}
if (vrr_drv[index]->vrr_dev) {
VRRERR("[%d]: %s: dev(%s %d) is busy, register failed: %s %d\n",
index, __func__,
vrr_drv[index]->vrr_dev->name,
vrr_drv[index]->vrr_dev->output_src,
vrr_dev->name, vrr_dev->output_src);
return -1;
}
vrr_drv[index]->vrr_dev = vrr_dev;
VRRPR("[%d]: %s: %s %d successfully\n",
index, __func__, vrr_dev->name, vrr_dev->output_src);
return 0;
}
int aml_vrr_unregister_device(int index)
{
if (vrr_global_init_flag == 0)
return 0;
if (index >= VRR_MAX_DRV) {
VRRERR("%s: invalid index: %d\n", __func__, index);
return -1;
}
if (!vrr_drv[index]) {
VRRERR("[%d]: %s: vrr_drv is null\n", index, __func__);
return -1;
}
if (!vrr_drv[index]->vrr_dev)
return 0;
if (vrr_drv[index]->enable) {
vrr_drv[index]->enable = 0;
vrr_work_disable(vrr_drv[index]);
}
VRRPR("[%d]: %s: %s %d unregister successfully\n",
index, __func__,
vrr_drv[index]->vrr_dev->name,
vrr_drv[index]->vrr_dev->output_src);
vrr_drv[index]->vrr_dev = NULL;
return 0;
}
/* ************************************************************* */
static int vrr_io_open(struct inode *inode, struct file *file)
{
struct aml_vrr_drv_s *vdrv;
vdrv = container_of(inode->i_cdev, struct aml_vrr_drv_s, cdev);
file->private_data = vdrv;
return 0;
}
static int vrr_io_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static long vrr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct aml_vrr_drv_s *vdrv;
void __user *argp;
unsigned int mcd_nr, temp;
int ret = 0;
vdrv = (struct aml_vrr_drv_s *)file->private_data;
if (!vdrv)
return -EFAULT;
mutex_lock(&vrr_mutex);
mcd_nr = _IOC_NR(cmd);
VRRPR("[%d]: %s: cmd_dir = 0x%x, cmd_nr = 0x%x\n",
vdrv->index, __func__, _IOC_DIR(cmd), mcd_nr);
argp = (void __user *)arg;
switch (mcd_nr) {
case VRR_IOC_GET_CAP:
if (!vdrv->vrr_dev) {
VRRERR("[%d]: %s: invalid vrr_dev\n",
vdrv->index, __func__);
temp = 0;
} else {
temp = vdrv->vrr_dev->enable;
}
if (copy_to_user(argp, &temp, sizeof(unsigned int)))
ret = -EFAULT;
break;
case VRR_IOC_ENABLE:
vrr_drv_func_en(vdrv, 1);
break;
case VRR_IOC_DISABLE:
vrr_drv_func_en(vdrv, 0);
break;
case VRR_IOC_GET_EN:
if (copy_to_user(argp, &vdrv->enable, sizeof(unsigned int)))
ret = -EFAULT;
break;
default:
VRRERR("[%d]: not support ioctl cmd_nr: 0x%x\n",
vdrv->index, mcd_nr);
ret = -EINVAL;
break;
}
mutex_unlock(&vrr_mutex);
return ret;
}
#ifdef CONFIG_COMPAT
static long vrr_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = vrr_ioctl(file, cmd, arg);
return ret;
}
#endif
static const struct file_operations vrr_fops = {
.owner = THIS_MODULE,
.open = vrr_io_open,
.release = vrr_io_release,
.unlocked_ioctl = vrr_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vrr_compat_ioctl,
#endif
};
static int vrr_cdev_add(struct aml_vrr_drv_s *vdrv, struct device *parent)
{
dev_t devno;
int ret = 0;
if (!vrr_cdev || !vdrv) {
ret = 1;
VRRERR("%s: vdrv is null\n", __func__);
return -1;
}
devno = MKDEV(MAJOR(vrr_cdev->devno), vdrv->index);
cdev_init(&vdrv->cdev, &vrr_fops);
vdrv->cdev.owner = THIS_MODULE;
ret = cdev_add(&vdrv->cdev, devno, 1);
if (ret) {
ret = 2;
goto vrr_cdev_add_failed;
}
vdrv->dev = device_create(vrr_cdev->class, parent,
devno, NULL, "vrr%d", vdrv->index);
if (IS_ERR_OR_NULL(vdrv->dev)) {
ret = 3;
goto vrr_cdev_add_failed1;
}
dev_set_drvdata(vdrv->dev, vdrv);
vdrv->dev->of_node = parent->of_node;
VRRPR("[%d]: %s OK\n", vdrv->index, __func__);
return 0;
vrr_cdev_add_failed1:
cdev_del(&vdrv->cdev);
vrr_cdev_add_failed:
VRRERR("[%d]: %s: failed: %d\n", vdrv->index, __func__, ret);
return -1;
}
static void vrr_cdev_remove(struct aml_vrr_drv_s *vdrv)
{
dev_t devno;
if (!vrr_cdev || !vdrv)
return;
devno = MKDEV(MAJOR(vrr_cdev->devno), vdrv->index);
device_destroy(vrr_cdev->class, devno);
cdev_del(&vdrv->cdev);
}
static int vrr_global_init_once(void)
{
int ret;
if (vrr_global_init_flag) {
vrr_global_init_flag++;
return 0;
}
vrr_global_init_flag++;
mutex_init(&vrr_mutex);
vrr_cdev = kzalloc(sizeof(*vrr_cdev), GFP_KERNEL);
if (!vrr_cdev)
return -1;
ret = alloc_chrdev_region(&vrr_cdev->devno, 0,
VRR_MAX_DRV, VRR_CDEV_NAME);
if (ret) {
ret = 1;
goto vrr_global_init_once_err;
}
vrr_cdev->class = class_create(THIS_MODULE, "aml_vrr");
if (IS_ERR_OR_NULL(vrr_cdev->class)) {
ret = 2;
goto vrr_global_init_once_err_1;
}
aml_vrr_if_probe();
return 0;
vrr_global_init_once_err_1:
unregister_chrdev_region(vrr_cdev->devno, VRR_MAX_DRV);
vrr_global_init_once_err:
kfree(vrr_cdev);
vrr_cdev = NULL;
VRRERR("%s: failed: %d\n", __func__, ret);
return -1;
}
static void vrr_global_remove_once(void)
{
if (vrr_global_init_flag > 1) {
vrr_global_init_flag--;
return;
}
vrr_global_init_flag--;
if (!vrr_cdev)
return;
aml_vrr_if_remove();
class_destroy(vrr_cdev->class);
unregister_chrdev_region(vrr_cdev->devno, VRR_MAX_DRV);
kfree(vrr_cdev);
vrr_cdev = NULL;
}
#ifdef CONFIG_OF
static struct vrr_data_s vrr_data_t7 = {
.chip_type = VRR_CHIP_T7,
.chip_name = "t7",
.drv_max = 3,
.offset = {0x0, 0x600, 0x800},
};
static struct vrr_data_s vrr_data_t3 = {
.chip_type = VRR_CHIP_T3,
.chip_name = "t3",
.drv_max = 1,
.offset = {0x0},
};
static const struct of_device_id vrr_dt_match_table[] = {
{
.compatible = "amlogic, vrr-t7",
.data = &vrr_data_t7,
},
{
.compatible = "amlogic, vrr-t3",
.data = &vrr_data_t3,
},
{}
};
#endif
static int vrr_probe(struct platform_device *pdev)
{
struct aml_vrr_drv_s *vdrv;
const struct of_device_id *match;
struct vrr_data_s *vdata;
unsigned int index = 0;
int ret = 0;
vrr_global_init_once();
if (!pdev->dev.of_node)
return -1;
ret = of_property_read_u32(pdev->dev.of_node, "index", &index);
if (ret) {
if (vrr_debug_print & VRR_DBG_PR_NORMAL)
VRRPR("%s: no index exist, default to 0\n", __func__);
index = 0;
}
if (index >= VRR_MAX_DRV) {
VRRERR("%s: invalid index %d\n", __func__, index);
return -1;
}
if (vrr_drv_init_state & (1 << index)) {
VRRERR("%s: index %d driver already registered\n",
__func__, index);
return -1;
}
vrr_drv_init_state |= (1 << index);
match = of_match_device(vrr_dt_match_table, &pdev->dev);
if (!match) {
VRRERR("%s: no match table\n", __func__);
return -1;
}
vdata = (struct vrr_data_s *)match->data;
if (index >= vdata->drv_max) {
VRRERR("%s: invalid index %d\n", __func__, index);
return -1;
}
vdrv = kzalloc(sizeof(*vdrv), GFP_KERNEL);
if (!vdrv)
return -ENOMEM;
vdrv->index = index;
vdrv->data = vdata;
vrr_drv[index] = vdrv;
VRRPR("[%d]: driver version: %s(%d-%s)\n",
index, VRR_DRV_VERSION,
vdata->chip_type, vdata->chip_name);
/* set drvdata */
platform_set_drvdata(pdev, vdrv);
vrr_cdev_add(vdrv, &pdev->dev);
ret = vrr_config_load(vdrv, pdev);
if (ret)
goto vrr_probe_err;
vrr_debug_file_creat(vdrv);
vrr_func_init(vdrv);
VRRPR("[%d]: %s ok, init_state:0x%x\n",
index, __func__, vrr_drv_init_state);
return 0;
vrr_probe_err:
/* free drvdata */
platform_set_drvdata(pdev, NULL);
vrr_drv[index] = NULL;
/* free drv */
kfree(vdrv);
VRRPR("[%d]: %s failed\n", index, __func__);
return -1;
}
static int vrr_remove(struct platform_device *pdev)
{
struct aml_vrr_drv_s *vdrv = platform_get_drvdata(pdev);
int index;
if (!vdrv)
return 0;
index = vdrv->index;
vrr_debug_file_remove(vdrv);
/* free drvdata */
platform_set_drvdata(pdev, NULL);
vrr_cdev_remove(vdrv);
kfree(vdrv);
vrr_drv[index] = NULL;
vrr_global_remove_once();
return 0;
}
static int vrr_resume(struct platform_device *pdev)
{
return 0;
}
static int vrr_suspend(struct platform_device *pdev, pm_message_t state)
{
struct aml_vrr_drv_s *vdrv = platform_get_drvdata(pdev);
if (vdrv->sw_timer_flag) {
vrr_timer_stop(vdrv);
vdrv->sw_timer_flag = 0;
VRRPR("[%d]: %s: stop sw_timer\n", vdrv->index, __func__);
}
vrr_work_disable(vdrv);
return 0;
}
static void vrr_shutdown(struct platform_device *pdev)
{
struct aml_vrr_drv_s *vdrv = platform_get_drvdata(pdev);
if (vdrv->sw_timer_flag) {
vrr_timer_stop(vdrv);
vdrv->sw_timer_flag = 0;
VRRPR("[%d]: %s: stop sw_timer\n", vdrv->index, __func__);
}
vrr_work_disable(vdrv);
}
static struct platform_driver vrr_platform_driver = {
.probe = vrr_probe,
.remove = vrr_remove,
.suspend = vrr_suspend,
.resume = vrr_resume,
.shutdown = vrr_shutdown,
.driver = {
.name = "meson_vrr",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(vrr_dt_match_table),
#endif
},
};
int __init vrr_init(void)
{
if (platform_driver_register(&vrr_platform_driver)) {
VRRERR("failed to register lcd driver module\n");
return -ENODEV;
}
return 0;
}
void __exit vrr_exit(void)
{
platform_driver_unregister(&vrr_platform_driver);
}