ambilight: support ambilight for t3x [1/1]

PD#SWPL-128669

Problem:
need support ambilight driver

Solution:
add ambilight driver support for t3x

Verify:
bc302

Change-Id: Ic4b998006eaf09ec5f3fd9a9d5da736ca2ec385a
Signed-off-by: Evoke Zhang <evoke.zhang@amlogic.com>
This commit is contained in:
Evoke Zhang
2023-06-13 16:22:39 +08:00
committed by gerrit autosubmit
parent f38b40750a
commit 02750fb3ea
13 changed files with 1170 additions and 3 deletions
@@ -2251,6 +2251,14 @@
interrupt-names = "vsync";
};
ambilight: ambilight {
compatible = "amlogic, ambilight-t3x";
status = "disabled";
interrupts = <0 115 1>;
interrupt-names = "vsync";
zone_h_v = <32 20>;
};
ir: ir@8000 {
compatible = "amlogic, meson-ir";
reg = <0x0 0xfe084040 0x0 0x44>,
@@ -1861,6 +1861,11 @@
};
};
&ambilight {
status = "okay";
zone_h_v = <32 20>;
};
&amhdmitx {
status = "disabled";
pxp_mode = <1>; /* run on pxp */
@@ -1838,6 +1838,11 @@
};
};
&ambilight {
status = "okay";
zone_h_v = <32 20>;
};
&amhdmitx {
status = "disabled";
pxp_mode = <1>; /* run on pxp */
+1
View File
@@ -171,6 +171,7 @@ CONFIG_AMLOGIC_BL_EXTERN=y
CONFIG_AMLOGIC_BL_LDIM=y
CONFIG_AMLOGIC_BL_LDIM_IW7027=y
CONFIG_AMLOGIC_BL_LDIM_BLMCU=y
CONFIG_AMLOGIC_AMBILIGHT=y
CONFIG_AMLOGIC_CVBS_OUTPUT=y
CONFIG_AMLOGIC_WSS=y
CONFIG_AMLOGIC_VDAC=y
+1
View File
@@ -53,6 +53,7 @@ static int __init media_main_init(void)
call_sub_init(aml_bl_init);
call_sub_init(aml_bl_extern_init);
call_sub_init(ldim_dev_init);
call_sub_init(ambilight_init);
call_sub_init(peripheral_lcd_init);
call_sub_init(dummy_venc_init);
call_sub_init(vout_sys_serve_init);
+9
View File
@@ -517,6 +517,15 @@ static inline int aml_bl_init(void)
}
#endif
#ifdef CONFIG_AMLOGIC_AMBILIGHT
int ambilight_init(void);
#else
static inline int ambilight_init(void)
{
return 0;
}
#endif
#ifdef CONFIG_AMLOGIC_MEDIA_DEINTERLACE
int dil_init(void);
int di_module_init(void);
+1
View File
@@ -37,6 +37,7 @@ config AMLOGIC_LCD_TABLET
if AMLOGIC_LCD
source "$(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/backlight/Kconfig"
source "$(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_extern/Kconfig"
source "$(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/ambilight/Kconfig"
endif
endmenu
+5 -3
View File
@@ -19,10 +19,12 @@ include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_tv/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_tablet/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_clk/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_phy/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_extern/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/backlight/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_clk/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/lcd_phy/Makefile
include $(COMMON_DRIVERS_DIR)/drivers/media/vout/lcd/ambilight/Makefile
+15
View File
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
#
# Ambilight configuration
#
menu "Amlogic Ambilight Support"
config AMLOGIC_AMBILIGHT
bool "Amlogic ambilight support"
default n
help
Say Y here if you want to use the Ambilight.
Ambilight can provide rgb sum for each zone.
endmenu
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_AMBILIGHT) += vout/lcd/ambilight/ambilight_drv.o
$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_AMBILIGHT) += vout/lcd/ambilight/ambilight_debug.o
@@ -0,0 +1,145 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#ifndef _AMBILIGHT_H_
#define _AMBILIGHT_H_
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
//20230613, init version
#define AMBLT_DRV_VER "20230613"
#define AMBLTPR(fmt, args...) pr_info("ambilight: " fmt "", ## args)
#define AMBLTERR(fmt, args...) pr_err("error: ambilight: " fmt "", ## args)
#define AMBLT_CLASS_NAME "amblt"
#define AMBLT_DEVICE_NAME "amblt"
enum LUT_DMA_WR_ID_e {
LC_STTS_0_DMA_ID = 0,
LC_STTS_1_DMA_ID = 1,
VI_HIST_SPL_0_DMA_ID = 2,
VI_HIST_SPL_1_DMA_ID = 3,
CM2_HIST_0_DMA_ID = 4,
CM2_HIST_1_DMA_ID = 5,
VD1_HDR_0_DMA_ID = 6,
VD1_HDR_1_DMA_ID = 7,
VD2_HDR_DMA_ID = 8,
AMBLT_DMA_ID = 9,
LUT_WR_MAX_ID = 10
};
struct vpu_lut_dma_wr_s {
enum LUT_DMA_WR_ID_e dma_wr_id;
unsigned int wr_sel;
unsigned int offset;
unsigned int stride;
unsigned int baddr0;
unsigned int baddr1;
unsigned int addr_mode;
unsigned int rpt_num;
};
struct amblt_dma_mem_s {
unsigned char flag;
void *vaddr;
phys_addr_t paddr;
unsigned int size;
};
struct amblt_data_sum_s {
unsigned int sum_b;
unsigned int sum_g;
unsigned int sum_r;
unsigned short x;
unsigned short y;
};
struct amblt_data_s {
unsigned int sum_r;
unsigned int sum_g;
unsigned int sum_b;
unsigned short avg_r;
unsigned short avg_g;
unsigned short avg_b;
unsigned char err;
};
struct amblt_drv_data_s {
enum lcd_chip_e chip_type;
const char *chip_name;
unsigned int zone_h_max;
unsigned int zone_v_max;
unsigned int zone_size_max;
unsigned int zone_size_min;
};
#define AMBLT_STATE_EN BIT(0)
struct amblt_drv_s {
unsigned int state;
unsigned int en;
unsigned int zone_h;
unsigned int zone_v;
unsigned int zone_size;
unsigned int zone_pixel;
unsigned int frm_idex;
unsigned int dbg_level;
struct amblt_drv_data_s *drv_data;
struct amblt_dma_mem_s dma_mem;
struct vpu_lut_dma_wr_s lut_dma;
struct amblt_data_s *buf;
dev_t devno;
struct cdev cdev;
struct device *dev;
struct class *clsp;
struct resource *res_vs_irq;
spinlock_t isr_lock; //for vsync ir
struct mutex power_mutex; //for on/off sequence
};
extern unsigned int amblt_debug_print;
void amblt_zone_pixel_init(struct amblt_drv_s *amblt_drv);
int amblt_debug_file_add(struct amblt_drv_s *amblt_drv);
int amblt_debug_file_remove(struct amblt_drv_s *amblt_drv);
int amblt_function_enable(struct amblt_drv_s *amblt_drv);
int amblt_function_disable(struct amblt_drv_s *amblt_drv);
/********************ioctl************************/
#define AMBLT_IOC_TYPE 'B'
#define AMBLT_IOC_EN_CTRL 0x1
#define AMBLT_IOC_GET_ZONE_SIZE 0x2
#define AMBLT_IOC_GET_DATA 0x3
#define AMBLT_IOC_CMD_POWER_CTRL \
_IOW(AMBLT_IOC_TYPE, AMBLT_IOC_EN_CTRL, unsigned int)
#define AMBLT_IOC_CMD_GET_SS \
_IOR(AMBLT_IOC_TYPE, AMBLT_IOC_GET_ZONE_SIZE, unsigned int)
/********************regs************************/
#define LCD_OLED_SIZE 0x14ec
#define LDC_REG_INPUT_STAT_NUM 0x14ef
#define VPU_DMA_WRMIF_CTRL 0x27d5
#define VPU_DMA_WRMIF0_CTRL 0x27d6
#define VPU_DMA_WRMIF1_CTRL 0x27d7
#define VPU_DMA_WRMIF2_CTRL 0x27d8
#define VPU_DMA_WRMIF3_CTRL 0x27d9
#define VPU_DMA_WRMIF4_CTRL 0x27da
#define VPU_DMA_WRMIF0_BADR0 0x27de
#define VPU_DMA_WRMIF0_BADR1 0x27df
#define VPU_DMA_WRMIF1_BADR0 0x27e0
#define VPU_DMA_WRMIF1_BADR1 0x27e1
#define VPU_DMA_WRMIF2_BADR0 0x27e2
#define VPU_DMA_WRMIF2_BADR1 0x27e3
#define VPU_DMA_WRMIF3_BADR0 0x27e4
#define VPU_DMA_WRMIF3_BADR1 0x27e5
#define VPU_DMA_WRMIF4_BADR0 0x27e6
#define VPU_DMA_WRMIF4_BADR1 0x27e7
#define VPU_DMA_WRMIF_SEL 0x27ee
#endif
@@ -0,0 +1,517 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/clk.h>
#include <linux/of_device.h>
#include <linux/compat.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
#include <linux/of_reserved_mem.h>
#include <linux/cma.h>
#include <linux/dma-mapping.h>
#include <linux/sched/clock.h>
#include <linux/amlogic/pm.h>
#include "ambilight.h"
#include "../lcd_reg.h"
static void amblt_buffer_dump(struct amblt_drv_s *amblt_drv)
{
unsigned int *vaddr;
char *print_buf;
int i, j, n, len;
if (amblt_drv->dma_mem.flag == 0) {
AMBLTERR("dma_mem is null\n");
return;
}
vaddr = amblt_drv->dma_mem.vaddr;
print_buf = kzalloc(20 * 3 + 10, GFP_KERNEL);
if (!print_buf) {
AMBLTERR("print_buf is null\n");
return;
}
pr_info("ambilight buffer:\n");
for (i = 0; i < amblt_drv->zone_size; i++) {
len = 0;
n = i * 4;
for (j = 0; j < 4; j++)
len += sprintf(print_buf + len, " 0x%08x", vaddr[n + j]);
pr_info("[%d]:%s\n", i, print_buf);
}
pr_info("\n");
kfree(print_buf);
}
static void amblt_data_sum_dump(struct amblt_drv_s *amblt_drv)
{
struct amblt_data_s *data;
char *print_buf;
int x, y, n, len;
if (!amblt_drv->buf) {
AMBLTERR("data buf is null\n");
return;
}
print_buf = kzalloc(5 * 30 + 10, GFP_KERNEL);
if (!print_buf) {
AMBLTERR("print_buf is null\n");
return;
}
for (y = 0; y < amblt_drv->zone_v; y++) {
for (x = 0; x < amblt_drv->zone_h; x++) {
len = 0;
n = y * amblt_drv->zone_h + x;
data = amblt_drv->buf + n;
len += sprintf(print_buf + len, " %8d %8d %8d",
data->sum_r, data->sum_g, data->sum_b);
if (data->err)
len += sprintf(print_buf + len, " (x)");
pr_info("[x:%02d, y:%02d]: %s\n", x, y, print_buf);
}
}
kfree(print_buf);
}
static void amblt_data_avg_dump(struct amblt_drv_s *amblt_drv)
{
struct amblt_data_s *data;
char *print_buf;
int x, y, n, len;
if (!amblt_drv->buf) {
AMBLTERR("data buf is null\n");
return;
}
print_buf = kzalloc(5 * 20 + 10, GFP_KERNEL);
if (!print_buf) {
AMBLTERR("print_buf is null\n");
return;
}
for (y = 0; y < amblt_drv->zone_v; y++) {
for (x = 0; x < amblt_drv->zone_h; x++) {
len = 0;
n = y * amblt_drv->zone_h + x;
data = amblt_drv->buf + n;
len += sprintf(print_buf + len, " %3d %3d %3d",
data->avg_r, data->avg_g, data->avg_b);
if (data->err)
len += sprintf(print_buf + len, " (x)");
pr_info("[x:%02d, y:%02d]: %s\n", x, y, print_buf);
}
}
kfree(print_buf);
}
ssize_t amblt_status_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct amblt_drv_s *amblt_drv = dev_get_drvdata(dev);
ssize_t len = 0;
if (!amblt_drv)
return sprintf(buf, "amblt_drv is NULL\n");
len = sprintf(buf, "ambilight status:\n"
"en: %d\n"
"state: 0x%x\n"
"zone_h: %d\n"
"zone_v: %d\n"
"zone_size: %d\n"
"zone_size: %d\n"
"dbg_level: 0x%x\n\n",
amblt_drv->en,
amblt_drv->state,
amblt_drv->zone_h,
amblt_drv->zone_v,
amblt_drv->zone_size,
amblt_drv->zone_pixel,
amblt_drv->dbg_level);
len += sprintf(buf + len, "ambilight lut_dma:\n"
"dma_wr_id: %d\n"
"wr_sel: %d\n"
"offset: %d\n"
"stride: %d\n"
"rpt_num: %d\n"
"addr_mode: %d\n"
"baddr0: 0x%08x\n"
"baddr1: 0x%08x\n\n",
amblt_drv->lut_dma.dma_wr_id,
amblt_drv->lut_dma.wr_sel,
amblt_drv->lut_dma.offset,
amblt_drv->lut_dma.stride,
amblt_drv->lut_dma.rpt_num,
amblt_drv->lut_dma.addr_mode,
amblt_drv->lut_dma.baddr0,
amblt_drv->lut_dma.baddr1);
lcd_vcbus_write(VPU_DMA_WRMIF_SEL, (0xff0 + amblt_drv->lut_dma.wr_sel));
len += sprintf(buf + len, "ambilight regs:\n");
len += sprintf(buf + len, "LDC_REG_INPUT_STAT_NUM 0x%04x = 0x%08x\n",
LDC_REG_INPUT_STAT_NUM, lcd_vcbus_read(LDC_REG_INPUT_STAT_NUM));
len += sprintf(buf + len, "LCD_OLED_SIZE 0x%04x = 0x%08x\n",
LCD_OLED_SIZE, lcd_vcbus_read(LCD_OLED_SIZE));
len += sprintf(buf + len, "VPU_DMA_WRMIF_SEL 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF_SEL, lcd_vcbus_read(VPU_DMA_WRMIF_SEL));
len += sprintf(buf + len, "VPU_DMA_WRMIF_CTRL 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF_CTRL, lcd_vcbus_read(VPU_DMA_WRMIF_CTRL));
len += sprintf(buf + len, "VPU_DMA_WRMIF1_CTRL 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF1_CTRL, lcd_vcbus_read(VPU_DMA_WRMIF1_CTRL));
len += sprintf(buf + len, "VPU_DMA_WRMIF1_BADR0 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF1_BADR0, lcd_vcbus_read(VPU_DMA_WRMIF1_BADR0));
len += sprintf(buf + len, "VPU_DMA_WRMIF1_BADR1 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF1_BADR1, lcd_vcbus_read(VPU_DMA_WRMIF1_BADR1));
return len;
}
static const char *amblt_debug_usage_str = {
"Usage:\n"
" cat /sys/class/amblt/amblt/status\n"
" echo 0 >/sys/class/amblt/amblt/enable\n"
" echo 1 >/sys/class/amblt/amblt/enable\n"
" echo reg >/sys/class/amblt/amblt/debug\n"
" echo data >/sys/class/amblt/amblt/debug\n"
" echo buffer >/sys/class/amblt/amblt/debug\n"
" echo 1 >/sys/class/amblt/amblt/print\n"
};
static ssize_t amblt_debug_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", amblt_debug_usage_str);
}
static ssize_t amblt_debug_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct amblt_drv_s *amblt_drv = dev_get_drvdata(dev);
unsigned int data[5];
int ret;
if (!buf)
return count;
if (!amblt_drv)
return count;
switch (buf[0]) {
case 'z': //zone
ret = sscanf(buf, "zone %d %d", &data[0], &data[1]);
if (ret == 2) {
amblt_drv->zone_h = data[0];
amblt_drv->zone_v = data[1];
amblt_drv->zone_size = data[0] * data[1];
}
pr_info("zone_h=%d, zone_v=%d\n",
amblt_drv->zone_h, amblt_drv->zone_v);
break;
case 'r': //reg
pr_info("LDC_REG_INPUT_STAT_NUM 0x%04x = 0x%08x\n",
LDC_REG_INPUT_STAT_NUM, lcd_vcbus_read(LDC_REG_INPUT_STAT_NUM));
pr_info("LCD_OLED_SIZE 0x%04x = 0x%08x\n",
LCD_OLED_SIZE, lcd_vcbus_read(LCD_OLED_SIZE));
pr_info("VPU_DMA_WRMIF_CTRL 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF_CTRL, lcd_vcbus_read(VPU_DMA_WRMIF_CTRL));
pr_info("VPU_DMA_WRMIF_SEL 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF_SEL, lcd_vcbus_read(VPU_DMA_WRMIF_SEL));
pr_info("VPU_DMA_WRMIF1_CTRL 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF1_CTRL, lcd_vcbus_read(VPU_DMA_WRMIF1_CTRL));
pr_info("VPU_DMA_WRMIF1_BADR0 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF1_BADR0, lcd_vcbus_read(VPU_DMA_WRMIF1_BADR0));
pr_info("VPU_DMA_WRMIF1_BADR1 0x%04x = 0x%08x\n",
VPU_DMA_WRMIF1_BADR1, lcd_vcbus_read(VPU_DMA_WRMIF1_BADR1));
break;
case 'd': //dma
ret = sscanf(buf, "dma %d %d %d", &data[0], &data[1], &data[2]);
if (ret == 3) {
amblt_drv->lut_dma.stride = data[0];
amblt_drv->lut_dma.rpt_num = data[1];
amblt_drv->lut_dma.addr_mode = data[2];
}
pr_info("lut_dma: stride=%d, rpt_num=%d, addr_mode=%d\n",
amblt_drv->lut_dma.stride,
amblt_drv->lut_dma.rpt_num,
amblt_drv->lut_dma.addr_mode);
break;
case 'b': //buffer
amblt_buffer_dump(amblt_drv);
break;
case 's': //sum
amblt_data_sum_dump(amblt_drv);
break;
case 'a': //avg
amblt_data_avg_dump(amblt_drv);
break;
default:
pr_info("invalid cmd\n");
break;
}
return count;
}
static ssize_t amblt_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct amblt_drv_s *amblt_drv = dev_get_drvdata(dev);
unsigned int temp;
temp = (amblt_drv->state & AMBLT_STATE_EN) ? 1 : 0;
return sprintf(buf, "%d\n", temp);
}
static ssize_t amblt_enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct amblt_drv_s *amblt_drv = dev_get_drvdata(dev);
unsigned int temp, zone_h, zone_v;
int ret;
if (!buf)
return count;
ret = sscanf(buf, "%d %d %d", &temp, &zone_h, &zone_v);
if (ret == 3) {
if (temp) {
amblt_drv->zone_h = zone_h;
amblt_drv->zone_v = zone_v;
amblt_drv->zone_size = zone_h * zone_v;
amblt_zone_pixel_init(amblt_drv);
amblt_function_enable(amblt_drv);
} else {
amblt_function_disable(amblt_drv);
}
} else if (ret == 1) {
if (temp)
amblt_function_enable(amblt_drv);
else
amblt_function_disable(amblt_drv);
} else {
pr_info("invalid data\n");
return -EINVAL;
}
return count;
}
static ssize_t amblt_print_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", amblt_debug_print);
}
static ssize_t amblt_print_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int temp;
int ret;
if (!buf)
return count;
ret = kstrtouint(buf, 16, &temp);
if (ret) {
pr_info("invalid data\n");
return -EINVAL;
}
amblt_debug_print = (unsigned int)temp;
pr_info("set debug print flag: 0x%x\n", amblt_debug_print);
return count;
}
static struct device_attribute amblt_debug_attrs[] = {
__ATTR(status, 0444, amblt_status_show, NULL),
__ATTR(debug, 0644, amblt_debug_show, amblt_debug_store),
__ATTR(enable, 0644, amblt_enable_show, amblt_enable_store),
__ATTR(print, 0644, amblt_print_show, amblt_print_store)
};
/* ************************************************************* */
/* vout ioctl */
/* ************************************************************* */
static int amblt_io_open(struct inode *inode, struct file *file)
{
struct amblt_drv_s *amblt_drv;
if (amblt_debug_print)
AMBLTPR("amblt io_open\n");
amblt_drv = container_of(inode->i_cdev, struct amblt_drv_s, cdev);
file->private_data = amblt_drv;
return 0;
}
static int amblt_io_release(struct inode *inode, struct file *file)
{
if (amblt_debug_print)
AMBLTPR("amblt io_release\n");
file->private_data = NULL;
return 0;
}
static long amblt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct amblt_drv_s *amblt_drv;
void __user *argp;
int mcd_nr;
unsigned int temp;
int ret = 0;
amblt_drv = (struct amblt_drv_s *)file->private_data;
if (!amblt_drv)
return -1;
mcd_nr = _IOC_NR(cmd);
if (amblt_debug_print) {
AMBLTPR("%s: cmd: 0x%x, cmd_dir = 0x%x, cmd_nr = 0x%x\n",
__func__, cmd, _IOC_DIR(cmd), mcd_nr);
}
argp = (void __user *)arg;
switch (mcd_nr) {
case AMBLT_IOC_EN_CTRL:
if (copy_from_user(&temp, argp, sizeof(unsigned int)))
ret = -EFAULT;
if (amblt_debug_print)
AMBLTPR("%s: AMBLT_IOC_EN_CTRL: %d\n", __func__, temp);
if (temp)
amblt_function_enable(amblt_drv);
else
amblt_function_disable(amblt_drv);
break;
case AMBLT_IOC_GET_ZONE_SIZE:
if (amblt_debug_print) {
AMBLTPR("%s: AMBLT_IOC_GET_ZONE_SIZE: %d\n",
__func__, amblt_drv->zone_size);
}
temp = amblt_drv->zone_size;
if (copy_to_user(argp, &temp, sizeof(unsigned int)))
ret = -EFAULT;
break;
default:
AMBLTERR("%s: invalid mcd_nr 0x%x\n", __func__, mcd_nr);
ret = -EINVAL;
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long amblt_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = amblt_ioctl(file, cmd, arg);
return ret;
}
#endif
static const struct file_operations amblt_fops = {
.owner = THIS_MODULE,
.open = amblt_io_open,
.release = amblt_io_release,
.unlocked_ioctl = amblt_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amblt_compat_ioctl,
#endif
};
int amblt_debug_file_add(struct amblt_drv_s *amblt_drv)
{
int i, ret;
amblt_drv->clsp = class_create(THIS_MODULE, AMBLT_CLASS_NAME);
if (IS_ERR(amblt_drv->clsp)) {
AMBLTERR("failed to create class\n");
return -1;
}
ret = alloc_chrdev_region(&amblt_drv->devno, 0, 1, AMBLT_DEVICE_NAME);
if (ret < 0) {
AMBLTERR("failed to alloc major number\n");
goto err1;
}
amblt_drv->dev = device_create(amblt_drv->clsp, NULL,
amblt_drv->devno, (void *)amblt_drv, AMBLT_DEVICE_NAME);
if (IS_ERR(amblt_drv->dev)) {
AMBLTERR("failed to create device\n");
goto err2;
}
for (i = 0; i < ARRAY_SIZE(amblt_debug_attrs); i++) {
if (device_create_file(amblt_drv->dev, &amblt_debug_attrs[i])) {
AMBLTERR("tcon: create tcon debug attribute %s fail\n",
amblt_debug_attrs[i].attr.name);
goto err3;
}
}
/* connect the file operations with cdev */
cdev_init(&amblt_drv->cdev, &amblt_fops);
amblt_drv->cdev.owner = THIS_MODULE;
/* connect the major/minor number to the cdev */
ret = cdev_add(&amblt_drv->cdev, amblt_drv->devno, 1);
if (ret) {
AMBLTERR("failed to add device\n");
goto err4;
}
AMBLTPR("%s: OK\n", __func__);
return 0;
err4:
for (i = 0; i < ARRAY_SIZE(amblt_debug_attrs); i++)
device_remove_file(amblt_drv->dev, &amblt_debug_attrs[i]);
err3:
device_destroy(amblt_drv->clsp, amblt_drv->devno);
amblt_drv->dev = NULL;
err2:
unregister_chrdev_region(amblt_drv->devno, 1);
err1:
class_destroy(amblt_drv->clsp);
amblt_drv->clsp = NULL;
AMBLTERR("%s error\n", __func__);
return -1;
}
int amblt_debug_file_remove(struct amblt_drv_s *amblt_drv)
{
int i;
for (i = 0; i < ARRAY_SIZE(amblt_debug_attrs); i++)
device_remove_file(amblt_drv->dev, &amblt_debug_attrs[i]);
cdev_del(&amblt_drv->cdev);
device_destroy(amblt_drv->clsp, amblt_drv->devno);
amblt_drv->dev = NULL;
class_destroy(amblt_drv->clsp);
amblt_drv->clsp = NULL;
unregister_chrdev_region(amblt_drv->devno, 1);
return 0;
}
@@ -0,0 +1,454 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/clk.h>
#include <linux/of_device.h>
#include <linux/compat.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
#include <linux/of_reserved_mem.h>
#include <linux/cma.h>
#include <linux/dma-mapping.h>
#include <linux/sched/clock.h>
#include <linux/amlogic/pm.h>
#include "ambilight.h"
#include "../lcd_reg.h"
unsigned int amblt_debug_print;
static void vpu_lut_dma_clr_fcnt(struct vpu_lut_dma_wr_s *lut_dma)
{
lcd_vcbus_write(VPU_DMA_WRMIF_SEL, (0xff0 + lut_dma->wr_sel));
lcd_vcbus_setb(VPU_DMA_WRMIF0_CTRL + lut_dma->offset, 1, 30, 1);
}
static void set_vpu_lut_dma_mif_wr(struct vpu_lut_dma_wr_s *lut_dma, int flag)
{
unsigned int ofst = lut_dma->offset;
if (flag) {
//Bit 31 ro_lut_wr_hs_r // unsigned , RO , default = 0
//Bit 30:27 ro_lut_wr_id // unsigned , RO , default = 0
//Bit 26 pls_hold_clr // unsigned , pls , default = 0
//Bit 25 ro_hold_timeout // unsigned , RO , default = 0
//Bit 24:12 ro_wrmif_cnt // unsigned , RO , default = 0
//Bit 11:4 reg_hold_line // unsigned , RW, default = 8'hff
//Bit 3 ro_lut_wr_hs_s // unsigned , RO , default = 0
//Bit 2 wrmif_hw_reset // unsigned , RW , default = 0
//Bit 1 lut_wr_cnt_sel // unsigned , RW , default = 0
//Bit 0 lut_wr_reg_sel // unsigned , RW , default = 0,
// 0: sel lut0,1,2,...,7 1:sel lut8,9,...,15
lcd_vcbus_write(VPU_DMA_WRMIF_SEL, (0xff0 + lut_dma->wr_sel));
lcd_vcbus_write(VPU_DMA_WRMIF0_CTRL + ofst,
((lut_dma->addr_mode & 0x3) << 28) |
((lut_dma->rpt_num & 0xff) << 18) |
(1 << 16) | //chn_enable
(lut_dma->stride & 0x1fff));
lcd_vcbus_write(VPU_DMA_WRMIF0_BADR0 + (ofst << 1), lut_dma->baddr0);
lcd_vcbus_write(VPU_DMA_WRMIF0_BADR1 + (ofst << 1), lut_dma->baddr1);
} else {
lcd_vcbus_write(VPU_DMA_WRMIF_SEL, (0xff0 + lut_dma->wr_sel));
lcd_vcbus_setb(VPU_DMA_WRMIF0_CTRL + ofst, 0, 16, 1); //chn_disable
}
}
static void amblt_hw_ctrl(struct amblt_drv_s *amblt_drv)
{
if (amblt_drv->en) {
set_vpu_lut_dma_mif_wr(&amblt_drv->lut_dma, 1);
lcd_vcbus_setb(LDC_REG_INPUT_STAT_NUM, amblt_drv->zone_h, 0, 6); //x num
lcd_vcbus_setb(LDC_REG_INPUT_STAT_NUM, amblt_drv->zone_v, 8, 5); //y num
lcd_vcbus_setb(LDC_REG_INPUT_STAT_NUM, 1, 15, 1); //reverse vs pol
lcd_vcbus_setb(LDC_REG_INPUT_STAT_NUM, 1, 21, 1); //reverse vs pol
lcd_vcbus_setb(LDC_REG_INPUT_STAT_NUM, 1, 16, 1); //en
//AMBLTPR("LDC_REG_INPUT_STAT_NUM 0x%04x = 0x%08x\n",
// LDC_REG_INPUT_STAT_NUM, lcd_vcbus_read(LDC_REG_INPUT_STAT_NUM));
amblt_drv->state |= AMBLT_STATE_EN;
if (amblt_debug_print) {
AMBLTPR("ambilight enable: zone x:%d, y:%d\n",
amblt_drv->zone_h, amblt_drv->zone_v);
}
} else {
amblt_drv->state &= ~AMBLT_STATE_EN;
lcd_vcbus_setb(LDC_REG_INPUT_STAT_NUM, 0, 16, 1);
set_vpu_lut_dma_mif_wr(&amblt_drv->lut_dma, 0);
if (amblt_debug_print)
AMBLTPR("ambilight disable\n");
}
}
int amblt_function_enable(struct amblt_drv_s *amblt_drv)
{
struct amblt_dma_mem_s *dma_mem = &amblt_drv->dma_mem;
if (dma_mem->flag == 0) {
AMBLTERR("%s: dma memory failed\n", __func__);
return -1;
}
if (amblt_drv->state & AMBLT_STATE_EN) {
AMBLTPR("%s: already enabled\n", __func__);
return 0;
}
amblt_drv->lut_dma.rpt_num = (amblt_drv->zone_size + 3) / 4;
vpu_lut_dma_clr_fcnt(&amblt_drv->lut_dma);
AMBLTPR("ambilight mem paddr: 0x%x, vaddr: 0x%px, size: 0x%x, zone_h=%d, zone_v=%d\n",
(unsigned int)dma_mem->paddr, dma_mem->vaddr, dma_mem->size,
amblt_drv->zone_h, amblt_drv->zone_v);
memset(dma_mem->vaddr, 0, dma_mem->size);
amblt_drv->en = 1;
return 0;
}
int amblt_function_disable(struct amblt_drv_s *amblt_drv)
{
if ((amblt_drv->state & AMBLT_STATE_EN) == 0) {
AMBLTPR("%s: already disabled\n", __func__);
return 0;
}
amblt_drv->en = 0;
return 0;
}
static void amblt_data_refersh(struct amblt_drv_s *amblt_drv)
{
struct amblt_data_sum_s *sum, *p;
int x, y, n;
if (amblt_drv->dma_mem.flag == 0)
return;
if (!amblt_drv->buf)
return;
p = (struct amblt_data_sum_s *)amblt_drv->dma_mem.vaddr;
for (y = 0; y < amblt_drv->zone_v; y++) {
for (x = 0; x < amblt_drv->zone_h; x++) {
n = y * amblt_drv->zone_h + x;
sum = p + n;
amblt_drv->buf[n].sum_r = sum->sum_r;
amblt_drv->buf[n].sum_g = sum->sum_g;
amblt_drv->buf[n].sum_b = sum->sum_b;
amblt_drv->buf[n].avg_r = sum->sum_r / amblt_drv->zone_pixel;
amblt_drv->buf[n].avg_g = sum->sum_g / amblt_drv->zone_pixel;
amblt_drv->buf[n].avg_b = sum->sum_b / amblt_drv->zone_pixel;
if (x != sum->x || y != sum->y)
amblt_drv->buf[n].err = 1;
else
amblt_drv->buf[n].err = 0;
}
}
}
static irqreturn_t amblt_vsync_isr(int irq, void *data)
{
struct amblt_drv_s *amblt_drv = (struct amblt_drv_s *)data;
unsigned int state;
if (!amblt_drv)
return IRQ_HANDLED;
state = amblt_drv->state & AMBLT_STATE_EN;
if (amblt_drv->en != state) {
amblt_hw_ctrl(amblt_drv);
return IRQ_HANDLED;
}
if (state)
amblt_data_refersh(amblt_drv);
return IRQ_HANDLED;
}
static int amblt_vsync_irq_init(struct amblt_drv_s *amblt_drv)
{
if (!amblt_drv->res_vs_irq)
return -1;
if (request_irq(amblt_drv->res_vs_irq->start,
amblt_vsync_isr, IRQF_SHARED, "amblt_vs", (void *)amblt_drv)) {
AMBLTERR("can't request amblt_vsync_isr\n");
return -1;
}
return 0;
}
static void amblt_vsync_irq_remove(struct amblt_drv_s *amblt_drv)
{
if (amblt_drv->res_vs_irq)
free_irq(amblt_drv->res_vs_irq->start, (void *)amblt_drv);
}
static int amblt_get_config(struct amblt_drv_s *amblt_drv, struct platform_device *pdev)
{
struct device_node *of_node;
unsigned int para[2];
int ret;
of_node = pdev->dev.of_node;
ret = of_property_read_u32_array(of_node, "zone_h_v", &para[0], 2);
if (ret) {
AMBLTERR("%s: get zone_h_v error\n", __func__);
return -1;
}
amblt_drv->zone_h = para[0];
amblt_drv->zone_v = para[1];
amblt_drv->zone_size = para[0] * para[1];
AMBLTPR("%s: zone h=%d, v=%d, size=%d\n",
__func__, amblt_drv->zone_h, amblt_drv->zone_v, amblt_drv->zone_size);
amblt_zone_pixel_init(amblt_drv);
amblt_drv->res_vs_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
if (!amblt_drv->res_vs_irq) {
AMBLTERR("%s: can't get vsync irq\n", __func__);
return -1;
}
return 0;
}
void amblt_zone_pixel_init(struct amblt_drv_s *amblt_drv)
{
struct aml_lcd_drv_s *pdrv = aml_lcd_get_driver(0);
unsigned int h_active, v_active, h_pixel, v_pixel;
if (!pdrv)
return;
h_active = pdrv->config.basic.h_active;
v_active = pdrv->config.basic.v_active;
h_pixel = h_active / amblt_drv->zone_h;
v_pixel = v_active / amblt_drv->zone_v;
amblt_drv->zone_pixel = h_pixel * v_pixel;
AMBLTPR("%s: %d x %d = %d\n", __func__, h_pixel, v_pixel, amblt_drv->zone_pixel);
kfree(amblt_drv->buf);
amblt_drv->buf = kcalloc(amblt_drv->zone_size, sizeof(struct amblt_data_s), GFP_KERNEL);
if (!amblt_drv->buf)
AMBLTERR("%s: data buf is NULL\n", __func__);
}
static void amblt_lut_dma_init(struct amblt_drv_s *amblt_drv)
{
struct vpu_lut_dma_wr_s *lut_dma = &amblt_drv->lut_dma;
unsigned int rpt_num;
rpt_num = (amblt_drv->zone_size + 3) / 4;
lut_dma->dma_wr_id = AMBLT_DMA_ID;
lut_dma->wr_sel = 1;
lut_dma->offset = AMBLT_DMA_ID - 8;
lut_dma->stride = 4;//0x280; //8; //unit:128bit
lut_dma->baddr0 = amblt_drv->dma_mem.paddr >> 4;
lut_dma->baddr1 = amblt_drv->dma_mem.paddr >> 4;
lut_dma->addr_mode = 1;//0; //1;
lut_dma->rpt_num = rpt_num;
lcd_vcbus_setb(VPU_DMA_WRMIF_CTRL, 1, 13, 1);//wr dma enable
}
static int amblt_dma_malloc(struct amblt_drv_s *amblt_drv, struct device *dev)
{
struct amblt_dma_mem_s *dma_mem = &amblt_drv->dma_mem;
unsigned int mem_size;
mem_size = 32 * 20 * 16; //byte
dma_mem->size = mem_size;
dma_mem->vaddr = dma_alloc_coherent(dev, mem_size, &dma_mem->paddr, GFP_KERNEL);
if (!dma_mem->vaddr) {
AMBLTERR("%s: failed! dma_mem size: 0x%x\n", __func__, mem_size);
return -1;
}
dma_mem->flag = 1;
AMBLTPR("%s: dma paddr: 0x%lx, vaddr: 0x%px, size: 0x%x, flag: %d\n",
__func__, (unsigned long)dma_mem->paddr, dma_mem->vaddr,
dma_mem->size, dma_mem->flag);
return 0;
}
/* ************************************************************* */
/* amblt driver data */
/* ************************************************************* */
static struct amblt_drv_data_s amblt_data_t3x = {
.chip_type = LCD_CHIP_T3X,
.chip_name = "t3x",
.zone_h_max = 32,
.zone_v_max = 20,
.zone_size_max = 640,
.zone_size_min = 4,
};
static const struct of_device_id amblt_dt_match_table[] = {
{
.compatible = "amlogic, ambilight-t3x",
.data = &amblt_data_t3x,
},
{}
};
static int amblt_probe(struct platform_device *pdev)
{
struct amblt_drv_s *amblt_drv;
const struct of_device_id *match;
struct amblt_drv_data_s *amblt_data;
int ret = 0;
match = of_match_device(amblt_dt_match_table, &pdev->dev);
if (!match) {
AMBLTERR("%s: no match table\n", __func__);
goto amblt_probe_err_0;
}
amblt_data = (struct amblt_drv_data_s *)match->data;
AMBLTPR("driver version: %s(%d-%s)\n",
AMBLT_DRV_VER,
amblt_data->chip_type,
amblt_data->chip_name);
amblt_drv = kzalloc(sizeof(*amblt_drv), GFP_KERNEL);
if (!amblt_drv)
goto amblt_probe_err_0;
amblt_drv->dev = &pdev->dev;
amblt_drv->drv_data = amblt_data;
platform_set_drvdata(pdev, amblt_drv);
ret = amblt_get_config(amblt_drv, pdev);
if (ret)
goto amblt_probe_err_1;
ret = amblt_dma_malloc(amblt_drv, &pdev->dev);
if (ret)
goto amblt_probe_err_1;
amblt_lut_dma_init(amblt_drv);
spin_lock_init(&amblt_drv->isr_lock);
mutex_init(&amblt_drv->power_mutex);
amblt_debug_file_add(amblt_drv);
ret = amblt_vsync_irq_init(amblt_drv);
if (ret)
goto amblt_probe_err_2;
AMBLTPR("driver probe ok\n");
return 0;
amblt_probe_err_2:
amblt_debug_file_remove(amblt_drv);
amblt_probe_err_1:
/* free drvdata */
platform_set_drvdata(pdev, NULL);
/* free drv */
kfree(amblt_drv);
amblt_probe_err_0:
AMBLTERR("driver probe failed\n");
return -1;
}
static int amblt_remove(struct platform_device *pdev)
{
struct amblt_drv_s *amblt_drv = platform_get_drvdata(pdev);
if (!amblt_drv)
return 0;
amblt_vsync_irq_remove(amblt_drv);
amblt_debug_file_remove(amblt_drv);
/* free drvdata */
platform_set_drvdata(pdev, NULL);
kfree(amblt_drv);
return 0;
}
static int amblt_resume(struct platform_device *pdev)
{
struct amblt_drv_s *amblt_drv = platform_get_drvdata(pdev);
if (!amblt_drv)
return 0;
mutex_lock(&amblt_drv->power_mutex);
AMBLTPR("resume done\n");
mutex_unlock(&amblt_drv->power_mutex);
return 0;
}
static int amblt_suspend(struct platform_device *pdev, pm_message_t state)
{
struct amblt_drv_s *amblt_drv = platform_get_drvdata(pdev);
if (!amblt_drv)
return 0;
mutex_lock(&amblt_drv->power_mutex);
AMBLTPR("suspend done\n");
mutex_unlock(&amblt_drv->power_mutex);
return 0;
}
static void amblt_shutdown(struct platform_device *pdev)
{
struct amblt_drv_s *amblt_drv = platform_get_drvdata(pdev);
if (!amblt_drv)
return;
AMBLTPR("shutdown done\n");
}
static struct platform_driver ambilight_platform_driver = {
.probe = amblt_probe,
.remove = amblt_remove,
.suspend = amblt_suspend,
.resume = amblt_resume,
.shutdown = amblt_shutdown,
.driver = {
.name = "ambilight",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(amblt_dt_match_table),
#endif
},
};
int __init ambilight_init(void)
{
if (platform_driver_register(&ambilight_platform_driver)) {
AMBLTERR("failed to register ambilight driver module\n");
return -ENODEV;
}
return 0;
}
void __exit ambilight_exit(void)
{
platform_driver_unregister(&ambilight_platform_driver);
}