#include <linux/io.h>
#include <linux/kernel.h>
#include "camss-tpg.h"
#include "camss.h"
#define TPG_HW_VERSION 0x0
# define HW_VERSION_STEPPING GENMASK(15, 0)
# define HW_VERSION_REVISION GENMASK(27, 16)
# define HW_VERSION_GENERATION GENMASK(31, 28)
#define TPG_HW_VER(gen, rev, step) \
(((u32)(gen) << 28) | ((u32)(rev) << 16) | (u32)(step))
#define TPG_HW_VER_2_0_0 TPG_HW_VER(2, 0, 0)
#define TPG_HW_VER_2_1_0 TPG_HW_VER(2, 1, 0)
#define TPG_HW_STATUS 0x4
#define TPG_CTRL 0x64
# define TPG_CTRL_TEST_EN BIT(0)
# define TPG_CTRL_PHY_SEL BIT(3)
# define TPG_CTRL_NUM_ACTIVE_LANES GENMASK(5, 4)
# define TPG_CTRL_VC_DT_PATTERN_ID GENMASK(8, 6)
# define TPG_CTRL_OVERLAP_SHDR_EN BIT(10)
# define TPG_CTRL_NUM_ACTIVE_VC GENMASK(31, 30)
#define TPG_CLEAR 0x1F4
#define TPG_VC_n_GAIN_CFG(n) (0x60 + (n) * 0x60)
#define TPG_VC_n_CFG0(n) (0x68 + (n) * 0x60)
# define TPG_VC_n_CFG0_VC_NUM GENMASK(4, 0)
# define TPG_VC_n_CFG0_NUM_ACTIVE_DT GENMASK(9, 8)
# define TPG_VC_n_CFG0_NUM_BATCH GENMASK(15, 12)
# define TPG_VC_n_CFG0_NUM_FRAMES GENMASK(31, 16)
#define TPG_VC_n_LSFR_SEED(n) (0x6C + (n) * 0x60)
#define TPG_VC_n_HBI_CFG(n) (0x70 + (n) * 0x60)
#define TPG_VC_n_VBI_CFG(n) (0x74 + (n) * 0x60)
#define TPG_VC_n_COLOR_BARS_CFG(n) (0x78 + (n) * 0x60)
# define TPG_VC_n_COLOR_BARS_CFG_PIX_PATTERN GENMASK(2, 0)
# define TPG_VC_n_COLOR_BARS_CFG_QCFA_EN BIT(3)
# define TPG_VC_n_COLOR_BARS_CFG_SPLIT_EN BIT(4)
# define TPG_VC_n_COLOR_BARS_CFG_NOISE_EN BIT(5)
# define TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD GENMASK(13, 8)
# define TPG_VC_n_COLOR_BARS_CFG_XCFA_EN BIT(16)
# define TPG_VC_n_COLOR_BARS_CFG_SIZE_X GENMASK(26, 24)
# define TPG_VC_n_COLOR_BARS_CFG_SIZE_Y GENMASK(30, 28)
#define TPG_VC_m_DT_n_CFG_0(m, n) (0x7C + (m) * 0x60 + (n) * 0xC)
# define TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT GENMASK(15, 0)
# define TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH GENMASK(31, 16)
#define TPG_VC_m_DT_n_CFG_1(m, n) (0x80 + (m) * 0x60 + (n) * 0xC)
# define TPG_VC_m_DT_n_CFG_1_DATA_TYPE GENMASK(5, 0)
# define TPG_VC_m_DT_n_CFG_1_ECC_XOR_MASK GENMASK(13, 8)
# define TPG_VC_m_DT_n_CFG_1_CRC_XOR_MASK GENMASK(31, 16)
#define TPG_VC_m_DT_n_CFG_2(m, n) (0x84 + (m) * 0x60 + (n) * 0xC)
# define TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE GENMASK(3, 0)
# define TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(19, 4)
# define TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(23, 20)
# define TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(27, 4)
# define TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(31, 28)
#define TPG_HBI_PCT_DEFAULT 545
#define TPG_VBI_PCT_DEFAULT 10
#define PERCENT_BASE 100
#define TPG_USER_SPECIFIED_PAYLOAD_DEFAULT 0xBE
#define TPG_LFSR_SEED_DEFAULT 0x12345678
#define TPG_COLOR_BARS_CFG_STANDARD \
FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA)
static const char * const testgen_payload_modes[] = {
[TPG_PAYLOAD_MODE_DISABLED] = "Disabled",
[TPG_PAYLOAD_MODE_INCREMENTING] = "Incrementing",
[TPG_PAYLOAD_MODE_ALTERNATING_55_AA] = "Alternating 0x55/0xAA",
[TPG_PAYLOAD_MODE_RANDOM] = "Pseudo-random Data",
[TPG_PAYLOAD_MODE_USER_SPECIFIED] = "User Specified",
[TPG_PAYLOAD_MODE_COLOR_BARS] = "Color bars",
};
static int tpg_stream_on(struct tpg_device *tpg)
{
struct tpg_testgen_config *tg = &tpg->testgen;
struct v4l2_mbus_framefmt *input_format;
const struct tpg_format_info *format;
u8 payload_mode = (tg->mode > TPG_PAYLOAD_MODE_DISABLED) ?
tg->mode - 1 : 0;
u8 lane_cnt = tpg->res->lane_cnt;
u8 vc, dt, last_vc = 0;
u32 val;
for (vc = 0; vc <= MSM_TPG_ACTIVE_VC; vc++) {
last_vc = vc;
input_format = &tpg->fmt;
format = tpg_get_fmt_entry(tpg->res->formats->formats,
tpg->res->formats->nformats,
input_format->code);
if (IS_ERR(format))
return -EINVAL;
val = FIELD_PREP(TPG_VC_n_CFG0_NUM_ACTIVE_DT, MSM_TPG_ACTIVE_DT) |
FIELD_PREP(TPG_VC_n_CFG0_NUM_FRAMES, 0);
writel(val, tpg->base + TPG_VC_n_CFG0(vc));
writel(TPG_LFSR_SEED_DEFAULT, tpg->base + TPG_VC_n_LSFR_SEED(vc));
val = DIV_ROUND_UP(input_format->width * format->bpp * TPG_HBI_PCT_DEFAULT,
BITS_PER_BYTE * lane_cnt * PERCENT_BASE);
writel(val, tpg->base + TPG_VC_n_HBI_CFG(vc));
val = input_format->height * TPG_VBI_PCT_DEFAULT / PERCENT_BASE;
writel(val, tpg->base + TPG_VC_n_VBI_CFG(vc));
writel(TPG_COLOR_BARS_CFG_STANDARD, tpg->base + TPG_VC_n_COLOR_BARS_CFG(vc));
for (dt = 0; dt <= MSM_TPG_ACTIVE_DT; dt++) {
val = FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT,
input_format->height & 0xffff) |
FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH,
input_format->width & 0xffff);
writel(val, tpg->base + TPG_VC_m_DT_n_CFG_0(vc, dt));
val = FIELD_PREP(TPG_VC_m_DT_n_CFG_1_DATA_TYPE, format->data_type);
writel(val, tpg->base + TPG_VC_m_DT_n_CFG_1(vc, dt));
if (tpg->hw_version == TPG_HW_VER_2_0_0) {
val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) |
FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD,
TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) |
FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT,
format->encode_format);
} else if (tpg->hw_version >= TPG_HW_VER_2_1_0) {
val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) |
FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD,
TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) |
FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT,
format->encode_format);
}
writel(val, tpg->base + TPG_VC_m_DT_n_CFG_2(vc, dt));
}
}
val = FIELD_PREP(TPG_CTRL_TEST_EN, 1) |
FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) |
FIELD_PREP(TPG_CTRL_NUM_ACTIVE_VC, last_vc);
writel(val, tpg->base + TPG_CTRL);
return 0;
}
static int tpg_reset(struct tpg_device *tpg)
{
writel(0, tpg->base + TPG_CTRL);
writel(1, tpg->base + TPG_CLEAR);
return 0;
}
static void tpg_stream_off(struct tpg_device *tpg)
{
tpg_reset(tpg);
}
static int tpg_configure_stream(struct tpg_device *tpg, u8 enable)
{
if (enable)
return tpg_stream_on(tpg);
tpg_stream_off(tpg);
return 0;
}
static int tpg_configure_testgen_pattern(struct tpg_device *tpg, s32 val)
{
if (val >= 0 && val <= TPG_PAYLOAD_MODE_COLOR_BARS)
tpg->testgen.mode = val;
return 0;
}
static u32 tpg_hw_version(struct tpg_device *tpg)
{
u32 hw_version = readl(tpg->base + TPG_HW_VERSION);
tpg->hw_version = hw_version;
dev_dbg(tpg->camss->dev, "tpg HW Version = %u.%u.%u\n",
(u32)FIELD_GET(HW_VERSION_GENERATION, hw_version),
(u32)FIELD_GET(HW_VERSION_REVISION, hw_version),
(u32)FIELD_GET(HW_VERSION_STEPPING, hw_version));
return hw_version;
}
static void tpg_subdev_init(struct tpg_device *tpg)
{
tpg->testgen.modes = testgen_payload_modes;
tpg->testgen.nmodes = TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1;
}
const struct tpg_hw_ops tpg_ops_gen1 = {
.configure_stream = tpg_configure_stream,
.configure_testgen_pattern = tpg_configure_testgen_pattern,
.hw_version = tpg_hw_version,
.reset = tpg_reset,
.subdev_init = tpg_subdev_init,
};