166 lines
4.0 KiB
C
166 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "common.h"
|
|
#include "dev.h"
|
|
#include "regs.h"
|
|
|
|
|
|
void rkvpss_idx_write(struct rkvpss_device *dev, u32 reg, u32 val, int idx)
|
|
{
|
|
u32 *mem, *flag, offset = idx * RKVPSS_SW_REG_SIZE_MAX;
|
|
|
|
if (!dev->unite_mode)
|
|
offset = 0;
|
|
|
|
mem = dev->sw_base_addr + reg + offset;
|
|
flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE + offset;
|
|
*mem = val;
|
|
*flag = RKVPSS_REG_CACHE;
|
|
|
|
if (dev->hw_dev->is_single && !dev->unite_mode)
|
|
rkvpss_hw_write(dev->hw_dev, reg, val);
|
|
}
|
|
|
|
void rkvpss_unite_write(struct rkvpss_device *dev, u32 reg, u32 val)
|
|
{
|
|
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT);
|
|
if (dev->unite_mode)
|
|
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
|
|
}
|
|
|
|
u32 rkvpss_idx_read(struct rkvpss_device *dev, u32 reg, int idx)
|
|
{
|
|
u32 val, offset = idx * RKVPSS_SW_REG_SIZE_MAX;
|
|
|
|
if (!dev->unite_mode)
|
|
offset = 0;
|
|
|
|
if (dev->hw_dev->is_single && !dev->unite_mode)
|
|
val = rkvpss_hw_read(dev->hw_dev, reg);
|
|
else
|
|
val = *(u32 *)(dev->sw_base_addr + reg + offset);
|
|
return val;
|
|
}
|
|
|
|
void rkvpss_idx_set_bits(struct rkvpss_device *dev, u32 reg, u32 mask, u32 val, int idx)
|
|
{
|
|
u32 tmp = rkvpss_idx_read(dev, reg, idx) & ~mask;
|
|
|
|
rkvpss_idx_write(dev, reg, val | tmp, idx);
|
|
}
|
|
|
|
void rkvpss_unite_set_bits(struct rkvpss_device *dev, u32 reg, u32 mask, u32 val)
|
|
{
|
|
rkvpss_idx_set_bits(dev, reg, mask, val, VPSS_UNITE_LEFT);
|
|
if (dev->unite_mode)
|
|
rkvpss_idx_set_bits(dev, reg, mask, val, VPSS_UNITE_RIGHT);
|
|
}
|
|
|
|
void rkvpss_idx_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask, int idx)
|
|
{
|
|
u32 tmp = rkvpss_idx_read(dev, reg, idx);
|
|
|
|
rkvpss_idx_write(dev, reg, tmp & ~mask, idx);
|
|
}
|
|
|
|
void rkvpss_unite_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask)
|
|
{
|
|
rkvpss_idx_clear_bits(dev, reg, mask, VPSS_UNITE_LEFT);
|
|
if (dev->unite_mode)
|
|
rkvpss_idx_clear_bits(dev, reg, mask, VPSS_UNITE_RIGHT);
|
|
}
|
|
|
|
void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end)
|
|
{
|
|
struct rkvpss_hw_dev *hw = dev->hw_dev;
|
|
void __iomem *base = hw->base_addr;
|
|
unsigned long lock_flags = 0;
|
|
u32 i, j;
|
|
|
|
if (end > RKVPSS_SW_REG_SIZE - 4) {
|
|
dev_err(dev->dev, "%s out of range\n", __func__);
|
|
return;
|
|
}
|
|
for (i = start; i <= end; i += 4) {
|
|
u32 *val = dev->sw_base_addr + i;
|
|
u32 *flag = dev->sw_base_addr + i + RKVPSS_SW_REG_SIZE;
|
|
|
|
if (dev->unite_mode &&
|
|
((!dev->mir_en && dev->unite_index == VPSS_UNITE_RIGHT) ||
|
|
(dev->mir_en && dev->unite_index == VPSS_UNITE_LEFT))) {
|
|
val += (RKVPSS_SW_REG_SIZE_MAX / 4);
|
|
flag += (RKVPSS_SW_REG_SIZE_MAX / 4);
|
|
}
|
|
|
|
if (*flag != RKVPSS_REG_CACHE)
|
|
continue;
|
|
|
|
if (IS_SYNC_REG(i)) {
|
|
spin_lock_irqsave(&hw->reg_lock, lock_flags);
|
|
if (i == RKVPSS_VPSS_ONLINE) {
|
|
u32 mask = 0;
|
|
|
|
for (j = 0; j < RKVPSS_OUTPUT_MAX; j++) {
|
|
if (!hw->is_ofl_ch[j])
|
|
continue;
|
|
mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << j * 2);
|
|
}
|
|
*val |= (readl(base + i) & mask);
|
|
} else {
|
|
*val |= readl(base + i);
|
|
}
|
|
}
|
|
writel(*val, base + i);
|
|
if (i == RKVPSS_MI_WR_INIT)
|
|
writel(*val, base + i);
|
|
if (IS_SYNC_REG(i))
|
|
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
|
|
}
|
|
}
|
|
|
|
int rkvpss_attach_hw(struct rkvpss_device *vpss)
|
|
{
|
|
struct device_node *np;
|
|
struct platform_device *pdev;
|
|
struct rkvpss_hw_dev *hw;
|
|
|
|
np = of_parse_phandle(vpss->dev->of_node, "rockchip,hw", 0);
|
|
if (!np || !of_device_is_available(np)) {
|
|
dev_err(vpss->dev, "failed to get vpss hw node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pdev = of_find_device_by_node(np);
|
|
of_node_put(np);
|
|
if (!pdev) {
|
|
dev_err(vpss->dev, "failed to get vpss hw from node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
hw = platform_get_drvdata(pdev);
|
|
if (!hw) {
|
|
dev_err(vpss->dev, "failed attach vpss hw\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (hw->dev_num >= VPSS_MAX_DEV) {
|
|
dev_err(vpss->dev, "failed attach vpss hw, max dev:%d\n", VPSS_MAX_DEV);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (hw->dev_num)
|
|
hw->is_single = false;
|
|
vpss->dev_id = hw->dev_num;
|
|
hw->vpss[hw->dev_num] = vpss;
|
|
hw->dev_num++;
|
|
vpss->hw_dev = hw;
|
|
vpss->vpss_ver = hw->vpss_ver;
|
|
|
|
return 0;
|
|
}
|