| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/types.h> |
| #include <linux/delay.h> |
| #include <linux/kernel.h> |
| #include <linux/interrupt.h> |
| #include <linux/fs.h> |
| #include <linux/init.h> |
| #include <linux/device.h> |
| #include <linux/mm.h> |
| #include <linux/major.h> |
| #include <linux/platform_device.h> |
| #include <linux/mutex.h> |
| #include <linux/cdev.h> |
| #include <linux/io.h> |
| #include <linux/of_address.h> |
| #include <linux/arm-smccc.h> |
| |
| #include <linux/amlogic/media/vout/hdmi_tx21/hdmi_tx_module.h> |
| |
| #include "common.h" |
| |
| // TODO |
| #define HDMITX_TOP_OFFSET 0xfe300000 |
| #define HDMITX_COR_OFFSET 0xfe380000 |
| |
| static int hdmi_dbg; |
| |
| struct reg_map reg21_maps[REG_IDX_END] = {0}; |
| int hdmitx21_init_reg_map(struct platform_device *pdev) |
| { |
| int i = 0; |
| struct resource res; |
| struct device_node *np = pdev->dev.of_node; |
| |
| for (i = 0; i < REG_IDX_END; i++) { |
| if (of_address_to_resource(np, i, &res)) { |
| pr_err("not get regbase index %d\n", i); |
| return 0; |
| } |
| |
| reg21_maps[i].phy_addr = res.start; |
| reg21_maps[i].size = resource_size(&res); |
| reg21_maps[i].p = devm_ioremap(&pdev->dev, res.start, |
| resource_size(&res)); |
| if (IS_ERR(reg21_maps[i].p)) |
| return -ENOMEM; |
| |
| pr_debug("Mapped PHY: 0x%x\n", reg21_maps[i].phy_addr); |
| } |
| |
| return 0; |
| } |
| |
| u32 get21_hdcp22_base(void) |
| { |
| return 0; |
| } |
| |
| static void sec_wr(u32 addr, u32 data) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(0x82000019, (unsigned long)addr, data, 32, 0, 0, 0, 0, &res); |
| if (hdmi_dbg) |
| pr_info("sec_wr32[0x%08x] 0x%08x\n", addr, data); |
| } |
| |
| static void sec_wr8(u32 addr, u8 data) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(0x82000019, (unsigned long)addr, data & 0xff, 8, 0, 0, 0, 0, &res); |
| if (hdmi_dbg) |
| pr_info("[0x%08x] 0x%02x\n", addr, data); |
| } |
| |
| static u32 sec_rd(u32 addr) |
| { |
| u32 data; |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(0x82000018, (unsigned long)addr, 32, 0, 0, 0, 0, 0, &res); |
| data = (unsigned int)((res.a0) & 0xffffffff); |
| |
| if (hdmi_dbg) |
| pr_info("[0x%08x] 0x%08x\n", addr, data); |
| return data; |
| } |
| |
| static u8 sec_rd8(u32 addr) |
| { |
| u32 data; |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(0x82000018, (unsigned long)addr, 8, 0, 0, 0, 0, 0, &res); |
| data = (unsigned int)((res.a0) & 0xffffffff); |
| |
| if (hdmi_dbg) |
| pr_info("[0x%08x] 0x%02x\n", addr, data); |
| return data; |
| } |
| |
| u32 TO21_PHY_ADDR(u32 addr) |
| { |
| u32 index; |
| u32 offset; |
| |
| index = addr >> BASE_REG_OFFSET; |
| offset = addr & (((1 << BASE_REG_OFFSET) - 1)); |
| |
| return (reg21_maps[index].phy_addr + offset); |
| } |
| |
| static void __iomem *TO_PMAP_ADDR(u32 addr) |
| { |
| u32 index; |
| u32 offset; |
| |
| index = addr >> BASE_REG_OFFSET; |
| offset = addr & (((1 << BASE_REG_OFFSET) - 1)); |
| |
| return (void __iomem *)(reg21_maps[index].p + offset); |
| } |
| |
| static u32 get_enc_paddr(unsigned int addr) |
| { |
| struct hdmitx_dev *hdev = get_hdmitx21_device(); |
| unsigned int idx = addr >> BASE_REG_OFFSET; |
| unsigned int offset = (addr & 0xffff) >> 2; |
| |
| if (hdev->enc_idx == 2 && idx == VPUCTRL_REG_IDX) { |
| if (offset >= 0x1b00 && offset < 0x1d00) |
| return addr + (0x800 << 2); |
| } |
| return addr; |
| } |
| |
| u32 hd21_read_reg(u32 vaddr) |
| { |
| u32 val; |
| u32 paddr = get_enc_paddr(vaddr); |
| |
| val = readl(TO_PMAP_ADDR(paddr)); |
| if (hdmi_dbg) |
| pr_info("Rd32[0x%08x] 0x%08x\n", TO21_PHY_ADDR(paddr), val); |
| return val; |
| } |
| EXPORT_SYMBOL(hd21_read_reg); |
| |
| void hd21_write_reg(u32 vaddr, u32 val) |
| { |
| u32 rval; |
| u32 paddr = get_enc_paddr(vaddr); |
| |
| writel(val, TO_PMAP_ADDR(paddr)); |
| rval = readl(TO_PMAP_ADDR(paddr)); |
| if (!hdmi_dbg) |
| return; |
| if (val != rval) |
| pr_info("Wr32[0x%08x] 0x%08x != Rd32 0x%08x\n", |
| TO21_PHY_ADDR(paddr), val, rval); |
| else |
| pr_info("Wr32[0x%08x] 0x%08x\n", |
| TO21_PHY_ADDR(paddr), val); |
| } |
| EXPORT_SYMBOL(hd21_write_reg); |
| |
| void hd21_set_reg_bits(u32 addr, u32 value, |
| u32 offset, u32 len) |
| { |
| u32 data32 = 0; |
| |
| data32 = hd21_read_reg(addr); |
| data32 &= ~(((1 << len) - 1) << offset); |
| data32 |= (value & ((1 << len) - 1)) << offset; |
| hd21_write_reg(addr, data32); |
| } |
| EXPORT_SYMBOL(hd21_set_reg_bits); |
| |
| static u32 hdmitx_rd_top(u32 addr) |
| { |
| u32 base_offset; |
| u32 data; |
| |
| base_offset = HDMITX_TOP_OFFSET; |
| |
| data = sec_rd(base_offset + addr); |
| return data; |
| } /* hdmitx_rd_top */ |
| |
| static u8 hdmitx_rd_cor(u32 addr) |
| { |
| u32 base_offset; |
| u8 data; |
| |
| base_offset = HDMITX_COR_OFFSET; |
| data = sec_rd8(base_offset + addr); |
| return data; |
| } /* hdmitx_rd_cor */ |
| |
| static void hdmitx_wr_top(u32 addr, u32 data) |
| { |
| u32 base_offset; |
| |
| base_offset = HDMITX_TOP_OFFSET; |
| sec_wr(base_offset + addr, data); |
| } /* hdmitx_wr_top */ |
| |
| static void hdmitx_wr_cor(u32 addr, u8 data) |
| { |
| u32 base_offset; |
| |
| base_offset = HDMITX_COR_OFFSET; |
| sec_wr8(base_offset + addr, data); |
| } /* hdmitx_wr_cor */ |
| |
| u32 hdmitx21_rd_reg(u32 addr) |
| { |
| u32 offset = (addr & TOP_OFFSET_MASK) >> 24; |
| u32 data; |
| |
| addr = addr & 0xffff; |
| if (offset) |
| data = hdmitx_rd_top(addr); |
| else |
| data = hdmitx_rd_cor(addr); |
| |
| return data; |
| } |
| |
| void hdmitx21_wr_reg(u32 addr, u32 val) |
| { |
| u32 offset = (addr & TOP_OFFSET_MASK) >> 24; |
| |
| addr = addr & 0xffff; |
| if (offset) |
| hdmitx_wr_top(addr, val); |
| else |
| hdmitx_wr_cor(addr, val); |
| } |
| |
| bool hdmitx21_get_bit(u32 addr, u32 bit_nr) |
| { |
| return (hdmitx21_rd_reg(addr) & (1 << bit_nr)) == (1 << bit_nr); |
| } |
| |
| void hdmitx21_set_reg_bits(u32 addr, u32 value, |
| u32 offset, u32 len) |
| { |
| u32 data32 = 0; |
| |
| data32 = hdmitx21_rd_reg(addr); |
| data32 &= ~(((1 << len) - 1) << offset); |
| data32 |= (value & ((1 << len) - 1)) << offset; |
| hdmitx21_wr_reg(addr, data32); |
| } |
| EXPORT_SYMBOL(hdmitx21_set_reg_bits); |
| |
| void hdmitx21_set_bit(u32 addr, u32 bit_val, bool st) |
| { |
| u32 data32 = 0; |
| |
| data32 = hdmitx21_rd_reg(addr); |
| data32 = st ? (data32 | bit_val) : (data32 & ~bit_val); |
| hdmitx21_wr_reg(addr, data32); |
| } |
| EXPORT_SYMBOL(hdmitx21_set_bit); |
| |
| void hdmitx21_poll_reg(u32 addr, u8 exp_data, u8 mask, ulong timeout) |
| { |
| u8 rd_data; |
| u8 done = 0; |
| ulong time = 0; |
| |
| time = jiffies; |
| |
| rd_data = hdmitx21_rd_reg(addr); |
| while (time_before(jiffies, time + timeout)) { |
| if ((rd_data | mask) == (exp_data | mask)) { |
| done = 1; |
| break; |
| } |
| rd_data = hdmitx21_rd_reg(addr); |
| usleep_range(10, 20); |
| } |
| if (done == 0) |
| pr_info("%s 0x%x poll time-out!\n", __func__, addr); |
| } /* hdmitx21_poll_reg */ |
| EXPORT_SYMBOL(hdmitx21_poll_reg); |
| |
| u32 hdmitx21_rd_check_reg(u32 addr, u32 exp_data, |
| u32 mask) |
| { |
| unsigned long rd_data; |
| |
| rd_data = hdmitx21_rd_reg(addr); |
| if ((rd_data | mask) != (exp_data | mask)) { |
| pr_info(REG "HDMITX-DWC addr=0x%04x rd_data=0x%02x\n", |
| (unsigned int)addr, (unsigned int)rd_data); |
| pr_info(REG "HDMITX-DWC exp_data=0x%02x mask=0x%02x\n", |
| (unsigned int)exp_data, (unsigned int)mask); |
| return 1; |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(hdmitx21_rd_check_reg); |
| |
| struct mask_reg_val_t { |
| u16 reg; |
| u8 val; |
| }; |
| |
| static struct mask_reg_val_t addrs[] = { |
| {INTR1_IVCTX, 0x00,}, |
| {TPI_INTR_ST0_IVCTX, 0x00,}, |
| {CP2TX_INTR0_IVCTX, 0x00,}, |
| {CP2TX_INTR1_IVCTX, 0x00,}, |
| {CP2TX_INTR2_IVCTX, 0x00,}, |
| {CP2TX_INTR3_IVCTX, 0x00,}, |
| {CP2TX_INTR3_IVCTX, 0x01,}, |
| {DDC_STATUS_IVCTX, 0x04,}, |
| {DDC_STATUS_IVCTX, 0x14,}, |
| {DDC_STATUS_IVCTX, 0x15,}, |
| {DDC_STATUS_IVCTX, 0x00,}, |
| {TPI_KSV_FIFO_STAT_IVCTX, 0x00}, |
| {0xffff, 0xff}, |
| }; |
| |
| static bool mask_printf(u16 add, u8 val) |
| { |
| int i; |
| |
| for (i = 0; addrs[i].reg != 0xffff; i++) |
| if (add == addrs[i].reg && val == addrs[i].val) |
| return 1; |
| return 0; |
| } |
| |
| void hdmitx21_seq_rd_reg(u16 offset, u8 *buf, u16 cnt) |
| { |
| int i = 0; |
| |
| while (cnt--) { |
| buf[i] = hdmitx21_rd_reg(offset + i); |
| if (!mask_printf(offset + i, buf[i])) |
| pr_info("rd[0x%04x] 0x%02x\n", offset + i, buf[i]); |
| i++; |
| } |
| } |
| EXPORT_SYMBOL(hdmitx21_seq_rd_reg); |
| |
| MODULE_PARM_DESC(hdmi_dbg, "\n hdmi_dbg\n"); |
| module_param(hdmi_dbg, int, 0644); |