You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
6.5 KiB
274 lines
6.5 KiB
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2019 Rockchip Electronics Co., Ltd
|
|
*/
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <clk.h>
|
|
#include <dm.h>
|
|
#include <linux/bitops.h>
|
|
#include <misc.h>
|
|
#include <irq-generic.h>
|
|
#include <reset.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#define DECOM_CTRL 0x0
|
|
#define DECOM_ENR 0x4
|
|
#define DECOM_RADDR 0x8
|
|
#define DECOM_WADDR 0xc
|
|
#define DECOM_UDDSL 0x10
|
|
#define DECOM_UDDSH 0x14
|
|
#define DECOM_TXTHR 0x18
|
|
#define DECOM_RXTHR 0x1c
|
|
#define DECOM_SLEN 0x20
|
|
#define DECOM_STAT 0x24
|
|
#define DECOM_ISR 0x28
|
|
#define DECOM_IEN 0x2c
|
|
#define DECOM_AXI_STAT 0x30
|
|
#define DECOM_TSIZEL 0x34
|
|
#define DECOM_TSIZEH 0x38
|
|
#define DECOM_MGNUM 0x3c
|
|
#define DECOM_FRAME 0x40
|
|
#define DECOM_DICTID 0x44
|
|
#define DECOM_CSL 0x48
|
|
#define DECOM_CSH 0x4c
|
|
#define DECOM_LMTSL 0x50
|
|
#define DECOM_LMTSH 0x54
|
|
|
|
#define LZ4_HEAD_CSUM_CHECK_EN BIT(1)
|
|
#define LZ4_BLOCK_CSUM_CHECK_EN BIT(2)
|
|
#define LZ4_CONT_CSUM_CHECK_EN BIT(3)
|
|
|
|
#define DSOLIEN BIT(19)
|
|
#define ZDICTEIEN BIT(18)
|
|
#define GCMEIEN BIT(17)
|
|
#define GIDEIEN BIT(16)
|
|
#define CCCEIEN BIT(15)
|
|
#define BCCEIEN BIT(14)
|
|
#define HCCEIEN BIT(13)
|
|
#define CSEIEN BIT(12)
|
|
#define DICTEIEN BIT(11)
|
|
#define VNEIEN BIT(10)
|
|
#define WNEIEN BIT(9)
|
|
#define RDCEIEN BIT(8)
|
|
#define WRCEIEN BIT(7)
|
|
#define DISEIEN BIT(6)
|
|
#define LENEIEN BIT(5)
|
|
#define LITEIEN BIT(4)
|
|
#define SQMEIEN BIT(3)
|
|
#define SLCIEN BIT(2)
|
|
#define HDEIEN BIT(1)
|
|
#define DSIEN BIT(0)
|
|
|
|
#define DECOM_STOP BIT(0)
|
|
#define DECOM_COMPLETE BIT(0)
|
|
#define DECOM_GZIP_MODE BIT(4)
|
|
#define DECOM_ZLIB_MODE BIT(5)
|
|
#define DECOM_DEFLATE_MODE BIT(0)
|
|
#define DECOM_AXI_IDLE BIT(4)
|
|
#define DECOM_LZ4_MODE 0
|
|
|
|
#define DECOM_ENABLE 0x1
|
|
#define DECOM_DISABLE 0x0
|
|
|
|
#define DECOM_IRQ 0xffff /* fixme */
|
|
|
|
#define DECOM_INT_MASK \
|
|
(DSOLIEN | ZDICTEIEN | GCMEIEN | GIDEIEN | \
|
|
CCCEIEN | BCCEIEN | HCCEIEN | CSEIEN | \
|
|
DICTEIEN | VNEIEN | WNEIEN | RDCEIEN | WRCEIEN | \
|
|
DISEIEN | LENEIEN | LITEIEN | SQMEIEN | SLCIEN | \
|
|
HDEIEN | DSIEN)
|
|
|
|
#define DCLK_DECOM 400 * 1000 * 1000
|
|
|
|
struct rockchip_decom_priv {
|
|
struct reset_ctl rst;
|
|
void __iomem *base;
|
|
bool idle_check_once;
|
|
bool done;
|
|
struct clk dclk;
|
|
int cached; /* 1: access the data through dcache; 0: no dcache */
|
|
};
|
|
|
|
static int rockchip_decom_start(struct udevice *dev, void *buf)
|
|
{
|
|
struct rockchip_decom_priv *priv = dev_get_priv(dev);
|
|
struct decom_param *param = (struct decom_param *)buf;
|
|
unsigned int limit_lo = param->size_dst & 0xffffffff;
|
|
unsigned int limit_hi = param->size_dst >> 32;
|
|
|
|
#if CONFIG_IS_ENABLED(DM_RESET)
|
|
reset_assert(&priv->rst);
|
|
udelay(10);
|
|
reset_deassert(&priv->rst);
|
|
#endif
|
|
/*
|
|
* Purpose:
|
|
* src: clean dcache to get the real compressed data from ddr.
|
|
* dst: invalidate dcache.
|
|
*
|
|
* flush_dcache_all() operating on set/way is faster than
|
|
* flush_cache() and invalidate_dcache_range() operating
|
|
* on virtual address.
|
|
*/
|
|
if (!priv->cached)
|
|
flush_dcache_all();
|
|
|
|
priv->done = false;
|
|
|
|
if (param->mode == DECOM_LZ4)
|
|
writel(LZ4_CONT_CSUM_CHECK_EN |
|
|
LZ4_HEAD_CSUM_CHECK_EN |
|
|
LZ4_BLOCK_CSUM_CHECK_EN |
|
|
DECOM_LZ4_MODE,
|
|
priv->base + DECOM_CTRL);
|
|
else if (param->mode == DECOM_GZIP)
|
|
writel(DECOM_DEFLATE_MODE | DECOM_GZIP_MODE,
|
|
priv->base + DECOM_CTRL);
|
|
else if (param->mode == DECOM_ZLIB)
|
|
writel(DECOM_DEFLATE_MODE | DECOM_ZLIB_MODE,
|
|
priv->base + DECOM_CTRL);
|
|
|
|
writel(param->addr_src, priv->base + DECOM_RADDR);
|
|
writel(param->addr_dst, priv->base + DECOM_WADDR);
|
|
|
|
writel(limit_lo, priv->base + DECOM_LMTSL);
|
|
writel(limit_hi, priv->base + DECOM_LMTSH);
|
|
|
|
writel(DECOM_ENABLE, priv->base + DECOM_ENR);
|
|
|
|
priv->idle_check_once = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_decom_stop(struct udevice *dev)
|
|
{
|
|
struct rockchip_decom_priv *priv = dev_get_priv(dev);
|
|
|
|
writel(DECOM_DISABLE, priv->base + DECOM_ENR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Caller must call this function to check if decompress done */
|
|
static int rockchip_decom_done_poll(struct udevice *dev)
|
|
{
|
|
struct rockchip_decom_priv *priv = dev_get_priv(dev);
|
|
|
|
/*
|
|
* Test the decom is idle first time.
|
|
*/
|
|
if (!priv->idle_check_once)
|
|
return !(readl(priv->base + DECOM_AXI_STAT) & DECOM_AXI_IDLE);
|
|
|
|
return !(readl(priv->base + DECOM_STAT) & DECOM_COMPLETE);
|
|
}
|
|
|
|
static int rockchip_decom_capability(u32 *buf)
|
|
{
|
|
*buf = DECOM_GZIP | DECOM_LZ4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_decom_data_size(struct udevice *dev, u64 *buf)
|
|
{
|
|
struct rockchip_decom_priv *priv = dev_get_priv(dev);
|
|
struct decom_param *param = (struct decom_param *)buf;
|
|
u32 sizel, sizeh;
|
|
|
|
sizel = readl(priv->base + DECOM_TSIZEL);
|
|
sizeh = readl(priv->base + DECOM_TSIZEH);
|
|
param->size_dst = sizel | ((u64)sizeh << 32);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Caller must fill in param @buf which represent struct decom_param */
|
|
static int rockchip_decom_ioctl(struct udevice *dev, unsigned long request,
|
|
void *buf)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
switch (request) {
|
|
case IOCTL_REQ_START:
|
|
ret = rockchip_decom_start(dev, buf);
|
|
break;
|
|
case IOCTL_REQ_POLL:
|
|
ret = rockchip_decom_done_poll(dev);
|
|
break;
|
|
case IOCTL_REQ_STOP:
|
|
ret = rockchip_decom_stop(dev);
|
|
break;
|
|
case IOCTL_REQ_CAPABILITY:
|
|
ret = rockchip_decom_capability(buf);
|
|
break;
|
|
case IOCTL_REQ_DATA_SIZE:
|
|
ret = rockchip_decom_data_size(dev, buf);
|
|
break;
|
|
default:
|
|
printf("Unsupported ioctl: %ld\n", (ulong)request);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct misc_ops rockchip_decom_ops = {
|
|
.ioctl = rockchip_decom_ioctl,
|
|
};
|
|
|
|
static int rockchip_decom_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct rockchip_decom_priv *priv = dev_get_priv(dev);
|
|
|
|
priv->base = dev_read_addr_ptr(dev);
|
|
if (!priv->base)
|
|
return -ENOENT;
|
|
|
|
priv->cached = dev_read_u32_default(dev, "data-cached", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_decom_probe(struct udevice *dev)
|
|
{
|
|
struct rockchip_decom_priv *priv = dev_get_priv(dev);
|
|
int ret;
|
|
|
|
#if CONFIG_IS_ENABLED(DM_RESET)
|
|
ret = reset_get_by_name(dev, "dresetn", &priv->rst);
|
|
if (ret) {
|
|
debug("reset_get_by_name() failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
ret = clk_get_by_index(dev, 1, &priv->dclk);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = clk_set_rate(&priv->dclk, DCLK_DECOM);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id rockchip_decom_ids[] = {
|
|
{ .compatible = "rockchip,hw-decompress" },
|
|
{}
|
|
};
|
|
|
|
U_BOOT_DRIVER(rockchip_hw_decompress) = {
|
|
.name = "rockchip_hw_decompress",
|
|
.id = UCLASS_MISC,
|
|
.of_match = rockchip_decom_ids,
|
|
.probe = rockchip_decom_probe,
|
|
.ofdata_to_platdata = rockchip_decom_ofdata_to_platdata,
|
|
.priv_auto_alloc_size = sizeof(struct rockchip_decom_priv),
|
|
.ops = &rockchip_decom_ops,
|
|
};
|
|
|