diff --git a/arch/arm64/boot/dts/amlogic/mesont3x.dtsi b/arch/arm64/boot/dts/amlogic/mesont3x.dtsi index 3049ee610..0ebdf382b 100644 --- a/arch/arm64/boot/dts/amlogic/mesont3x.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesont3x.dtsi @@ -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>, diff --git a/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302.dts b/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302.dts index aee4c6b70..cd76f98d9 100644 --- a/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302.dts +++ b/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302.dts @@ -1861,6 +1861,11 @@ }; }; +&ambilight { + status = "okay"; + zone_h_v = <32 20>; +}; + &amhdmitx { status = "disabled"; pxp_mode = <1>; /* run on pxp */ diff --git a/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302_linux.dts b/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302_linux.dts index 7953b47cd..51b218737 100644 --- a/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302_linux.dts +++ b/arch/arm64/boot/dts/amlogic/t3x_t968d4_bc302_linux.dts @@ -1838,6 +1838,11 @@ }; }; +&ambilight { + status = "okay"; + zone_h_v = <32 20>; +}; + &amhdmitx { status = "disabled"; pxp_mode = <1>; /* run on pxp */ diff --git a/arch/arm64/configs/amlogic_gki.fragment b/arch/arm64/configs/amlogic_gki.fragment index fe67e9367..259092161 100644 --- a/arch/arm64/configs/amlogic_gki.fragment +++ b/arch/arm64/configs/amlogic_gki.fragment @@ -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 diff --git a/drivers/media/media_main.c b/drivers/media/media_main.c index afc19bd8c..2a347d65a 100644 --- a/drivers/media/media_main.c +++ b/drivers/media/media_main.c @@ -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); diff --git a/drivers/media/media_main.h b/drivers/media/media_main.h index 365431a5c..37b40ceee 100644 --- a/drivers/media/media_main.h +++ b/drivers/media/media_main.h @@ -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); diff --git a/drivers/media/vout/lcd/Kconfig b/drivers/media/vout/lcd/Kconfig index 767e18359..5e3bc5c1f 100644 --- a/drivers/media/vout/lcd/Kconfig +++ b/drivers/media/vout/lcd/Kconfig @@ -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 diff --git a/drivers/media/vout/lcd/Makefile b/drivers/media/vout/lcd/Makefile index 3020495f0..9b3077bcd 100644 --- a/drivers/media/vout/lcd/Makefile +++ b/drivers/media/vout/lcd/Makefile @@ -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 diff --git a/drivers/media/vout/lcd/ambilight/Kconfig b/drivers/media/vout/lcd/ambilight/Kconfig new file mode 100644 index 000000000..fb1c160b5 --- /dev/null +++ b/drivers/media/vout/lcd/ambilight/Kconfig @@ -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 diff --git a/drivers/media/vout/lcd/ambilight/Makefile b/drivers/media/vout/lcd/ambilight/Makefile new file mode 100644 index 000000000..74bfdaa19 --- /dev/null +++ b/drivers/media/vout/lcd/ambilight/Makefile @@ -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 diff --git a/drivers/media/vout/lcd/ambilight/ambilight.h b/drivers/media/vout/lcd/ambilight/ambilight.h new file mode 100644 index 000000000..dfef73166 --- /dev/null +++ b/drivers/media/vout/lcd/ambilight/ambilight.h @@ -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 + +//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 diff --git a/drivers/media/vout/lcd/ambilight/ambilight_debug.c b/drivers/media/vout/lcd/ambilight/ambilight_debug.c new file mode 100644 index 000000000..c072fe49e --- /dev/null +++ b/drivers/media/vout/lcd/ambilight/ambilight_debug.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/drivers/media/vout/lcd/ambilight/ambilight_drv.c b/drivers/media/vout/lcd/ambilight/ambilight_drv.c new file mode 100644 index 000000000..f56ae18cb --- /dev/null +++ b/drivers/media/vout/lcd/ambilight/ambilight_drv.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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", ¶[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); +}