blob: cb11709954fe120b7d01ae780efcadc1742180ea [file]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dma-fence.h>
#include <linux/dma-heap.h>
#include <linux/dma-buf.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#include <linux/sync_file.h>
#include <uapi/linux/dma-heap.h>
#include "mtk-mml-drm-adaptor.h"
#include "mtk-mml-color.h"
#include "mtk-mml-core.h"
/* HOWTO:
* 1. echo $id > sys/module/mtk_mml_test/patameters/mml_case
* 2. run userspace UnitTest bin
*
* UnitTest flow
* 1. open file "/sys/kernel/debug/mml/mml-test"
* 2. read to struct mml_test_case, store cfg_ members.
* 3. write struct mml_test_case with [in] members
*
*/
/* test case struct use by ut bin in dpframework */
struct mml_test_case {
/* [out] config next ut */
uint32_t cfg_src_format;
uint32_t cfg_src_w;
uint32_t cfg_src_h;
uint32_t cfg_src1_format;
uint32_t cfg_src1_w;
uint32_t cfg_src1_h;
uint32_t cfg_dest_format;
uint32_t cfg_dest_w;
uint32_t cfg_dest_h;
uint32_t cfg_dest1_format;
uint32_t cfg_dest1_w;
uint32_t cfg_dest1_h;
/* [in] */
int32_t fd_in;
uint32_t size_in;
int32_t fd_in1;
uint32_t size_in1;
int32_t fd_out;
uint32_t size_out;
int32_t fd_out1;
uint32_t size_out1;
};
/* kernel level test case struct */
struct mml_ut {
/* [out] config next ut */
uint32_t cfg_src_format;
uint32_t cfg_src_w;
uint32_t cfg_src_h;
uint32_t cfg_src1_format;
uint32_t cfg_src1_w;
uint32_t cfg_src1_h;
uint32_t cfg_dest_format;
uint32_t cfg_dest_w;
uint32_t cfg_dest_h;
uint32_t cfg_dest1_format;
uint32_t cfg_dest1_w;
uint32_t cfg_dest1_h;
union {
struct {
/* [in] */
int32_t fd_in;
uint32_t size_in;
int32_t fd_in1;
uint32_t size_in1;
int32_t fd_out;
uint32_t size_out;
int32_t fd_out1;
uint32_t size_out1;
};
struct {
/* [in] */
void *buf_src[2];
uint32_t dma_size_in[2];
void *buf_dest[2];
uint32_t dma_size_out[2];
bool use_dma;
};
};
};
static struct mml_ut the_case;
unsigned int mml_case;
module_param(mml_case, uint, 0644);
/* how many submit for each ut */
int mml_test_round = 1;
module_param(mml_test_round, int, 0644);
/* interval for each test run, in ms */
int mml_test_interval = 16;
module_param(mml_test_interval, int, 0644);
int mml_test_w = 1920;
module_param(mml_test_w, int, 0644);
int mml_test_h = 1088;
module_param(mml_test_h, int, 0644);
int mml_test_out_w = 1920;
module_param(mml_test_out_w, int, 0644);
int mml_test_out_h = 1088;
module_param(mml_test_out_h, int, 0644);
int mml_test_crop_left;
module_param(mml_test_crop_left, int, 0644);
int mml_test_crop_top;
module_param(mml_test_crop_top, int, 0644);
int mml_test_crop_width = 1920;
module_param(mml_test_crop_width, int, 0644);
int mml_test_crop_height = 1088;
module_param(mml_test_crop_height, int, 0644);
int mml_test_rot;
module_param(mml_test_rot, int, 0644);
int mml_test_flip;
module_param(mml_test_flip, int, 0644);
int mml_test_pq;
module_param(mml_test_pq, int, 0644);
int mml_test_in_fmt = MML_FMT_RGB888;
module_param(mml_test_in_fmt, int, 0644);
int mml_test_out_fmt = MML_FMT_RGB888;
module_param(mml_test_out_fmt, int, 0644);
struct mml_test {
struct platform_device *pdev;
struct device *dev;
struct platform_device *mml_plat_dev;
struct mml_drm_ctx *drm_ctx;
struct dentry *fs;
struct dentry *fs_inst;
struct dentry *fs_frame_in;
struct dentry *fs_frame_out;
struct dentry *fs_dump;
struct dentry *fs_dump_dest;
};
struct test_case_op {
void (*config)(void);
void (*run)(struct mml_test *test, struct mml_ut *cur);
};
static void check_fence(int32_t fd, const char *func)
{
#ifndef MML_FPGA
struct dma_fence *fence = sync_file_get_fence(fd);
long fret;
if (unlikely(!fence)) {
mml_err("sync_file_get_fence return null");
return;
}
fret = dma_fence_wait(fence, true);
mml_log("[test]%s success fence %d %p ret %ld",
func, fd, fence, fret);
dma_fence_put(fence);
put_unused_fd(fd);
#endif
}
#define mml_afbc_align(p) (((p + 31) >> 5) << 5)
static void fillin_info_data(u32 format, u32 width, u32 height,
struct mml_frame_data *data)
{
data->format = format;
data->width = width;
data->height = height;
data->plane_cnt = MML_FMT_PLANE(data->format);
data->y_stride = mml_color_get_min_y_stride(data->format, data->width);
if (data->plane_cnt >= 2)
data->uv_stride = mml_color_get_min_uv_stride(
data->format, data->width);
data->vert_stride = mml_afbc_align(height);
data->profile = 0;
data->secure = false;
}
static void fillin_buf(struct mml_frame_data *data, s32 fd, u32 fd_size,
struct mml_buffer *buf)
{
buf->cnt = MML_FMT_PLANE(data->format);
buf->fd[0] = fd;
buf->size[0] = fd_size;
if (buf->cnt >= 2) {
buf->size[0] = mml_color_get_min_y_size(
data->format, data->width, data->height);
buf->fd[1] = fd;
buf->size[1] = mml_color_get_min_uv_size(
data->format, data->width, data->height);
}
if (buf->cnt >= 3) {
buf->fd[2] = fd;
buf->size[2] = mml_color_get_min_uv_size(
data->format, data->width, data->height);
}
}
static void fillin_buf_dma(struct mml_frame_data *data, void *dmabuf, u32 size,
struct mml_buffer *buf)
{
buf->cnt = MML_FMT_PLANE(data->format);
buf->dmabuf[0] = dmabuf;
buf->size[0] = size;
if (buf->cnt >= 2) {
buf->size[0] = mml_color_get_min_y_size(
data->format, data->width, data->height);
buf->dmabuf[1] = dmabuf;
buf->size[1] = mml_color_get_min_uv_size(
data->format, data->width, data->height);
}
if (buf->cnt >= 3) {
buf->dmabuf[2] = dmabuf;
buf->size[2] = mml_color_get_min_uv_size(
data->format, data->width, data->height);
}
buf->use_dma = true;
}
static void case_general_submit(struct mml_test *test,
struct mml_ut *cur,
void (*setup)(struct mml_submit *task, struct mml_ut *cur))
{
struct platform_device *mml_pdev;
struct mml_drm_ctx *mml_ctx;
struct mml_job job = {};
struct mml_pq_param pq_param = {};
struct mml_submit task = {.job = &job};
u32 run_cnt = mml_test_round <= 0 ? 1 : (u32)mml_test_round;
struct mml_drm_param disp = {
.vdo_mode = true,
};
const u32 max_running = 10;
int *fences;
u32 i;
s32 ret;
int8_t mode;
mml_log("[test]%s begin case %d", __func__, mml_case);
mml_pdev = mml_get_plat_device(test->pdev);
if (!mml_pdev) {
mml_err("[test]get mml device failed");
return;
}
mml_ctx = mml_drm_get_context(mml_pdev, &disp);
if (IS_ERR_OR_NULL(mml_ctx)) {
mml_err("[test]get mml context failed %d", PTR_ERR(mml_ctx));
return;
}
/* srouce info and buffer */
fillin_info_data(cur->cfg_src_format, cur->cfg_src_w, cur->cfg_src_h,
&task.info.src);
if (cur->use_dma)
fillin_buf_dma(&task.info.src, cur->buf_src[0],
cur->dma_size_in[0], &task.buffer.src);
else
fillin_buf(&task.info.src, cur->fd_in, cur->size_in, &task.buffer.src);
/* destination info and buffer */
fillin_info_data(cur->cfg_dest_format, cur->cfg_dest_w, cur->cfg_dest_h,
&task.info.dest[0].data);
if (cur->use_dma)
fillin_buf_dma(&task.info.dest[0].data, cur->buf_dest[0], cur->dma_size_out[0],
&task.buffer.dest[0]);
else
fillin_buf(&task.info.dest[0].data, cur->fd_out, cur->size_out,
&task.buffer.dest[0]);
task.info.dest_cnt = 1;
task.info.mode = MML_MODE_MML_DECOUPLE;
task.info.layer_id = 0;
task.buffer.dest_cnt = 1;
/* trigger all invalid/flush */
task.buffer.src.flush = true;
task.buffer.src.invalid = true;
task.buffer.src.fence = -1;
task.buffer.dest[0].flush = true;
task.buffer.dest[0].invalid = true;
task.buffer.dest[0].fence = -1;
task.buffer.dest[1].flush = true;
task.buffer.dest[1].invalid = true;
task.buffer.dest[1].fence = -1;
if (mml_test_pq == 1) {
pq_param.enable = 1;
pq_param.scenario = MML_PQ_MEDIA_VIDEO;
pq_param.src_hdr_video_mode = MML_PQ_HDR10;
pq_param.video_param.video_id = 0x546;
task.pq_param[0] = &pq_param;
task.info.dest[0].pq_config.en = 1;
task.info.dest[0].pq_config.en_dre = 1;
task.info.dest[0].pq_config.en_hdr = 1;
task.info.dest[0].pq_config.en_color = 1;
task.info.dest[0].pq_config.en_sharp = 1;
mml_log("[test]%s open PQ", __func__);
}
if (setup)
setup(&task, cur);
mode = task.info.mode;
mml_drm_try_frame(mml_ctx, &task.info);
if (mml_drm_query_cap(mml_ctx, &task.info) == MML_MODE_NOT_SUPPORT) {
mml_err("[test]%s not support", __func__);
return;
}
/* for ut do not fall to inline rotate unless force use */
if (mode == MML_MODE_RACING || mode == MML_MODE_SRAM_READ || mode == MML_MODE_APUDC)
task.info.mode = mode;
else
task.info.mode = MML_MODE_MML_DECOUPLE;
if (mode == MML_MODE_RACING) {
struct mml_submit nouse_submit = {0};
mml_drm_split_info(&task, &nouse_submit);
}
fences = kcalloc(run_cnt, sizeof(*fences), GFP_KERNEL);
for (i = 0; i < run_cnt; i++) {
ktime_get_real_ts64((struct timespec64 *)&task.end);
timespec64_add_ns((struct timespec64 *)&task.end,
mml_test_interval * 1000000);
ret = mml_drm_submit(mml_ctx, &task, NULL);
if (ret) {
mml_err("[test]%s submit failed: %d round: %u",
__func__, ret, i);
fences[i] = -1;
} else {
fences[i] = task.job->fence;
}
msleep_interruptible(mml_test_interval);
if (i > max_running && fences[i-max_running] >= 0) {
check_fence(fences[i-max_running], __func__);
fences[i-max_running] = -1;
}
if (mml_racing_ut == 2 || mml_racing_ut == 3)
mml_drm_stop(mml_ctx, &task, false);
}
for (i = 0; i < run_cnt; i++) {
if (fences[i] >= 0)
check_fence(fences[i], __func__);
}
kfree(fences);
for (i = 0; i < 5 && !mml_drm_ctx_idle(mml_ctx); i++) {
mml_log("[test]wait for ctx idle...");
msleep_interruptible(1000); /* make sure mml stops */
}
if (mml_drm_ctx_idle(mml_ctx))
mml_drm_put_context(mml_ctx);
else
mml_err("[test]fail to put ctx");
mml_log("[test]%s end", __func__);
}
/* case_config_rgb/case_run_general
* most simple test case
*
* format in: RGB888
* format out: RGB888
*/
static void case_config_rgb(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void case_run_general(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, NULL);
}
/* case_config_rgb_rot/setup_rot/case_run_rgb_rot
* test rbg image with rotate
*
* format in: RGB888
* format out: RGB888
* rotate: 90
*/
static void case_config_rgb_rot(void)
{
case_config_rgb();
swap(the_case.cfg_dest_w, the_case.cfg_dest_h);
}
static void setup_rot(struct mml_submit *task, struct mml_ut *cur)
{
task->info.dest[0].rotate = MML_ROT_90;
}
static void case_run_rgb_rot(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_rot);
}
/* case_config_rgb_compose/setup_rgb_compose/case_run_rgb_compose
* compose feature test
*
* format in: RGB888
* format out: RGB888
* compose: (50, 16)
*/
#define CASE_RGB_LEFT 50
#define CASE_RGB_TOP 16
static void case_config_rgb_compose(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w + CASE_RGB_LEFT;
the_case.cfg_dest_h = mml_test_h + CASE_RGB_TOP;
}
static void setup_rgb_compose(struct mml_submit *task,
struct mml_ut *cur)
{
task->info.dest[0].data.width = mml_test_w;
task->info.dest[0].data.height = mml_test_h;
task->info.dest[0].crop.r.width = mml_test_w;
task->info.dest[0].crop.r.height = mml_test_h;
task->info.dest[0].compose.left = CASE_RGB_LEFT;
task->info.dest[0].compose.top = CASE_RGB_TOP;
task->info.dest[0].compose.width = the_case.cfg_dest_w;
task->info.dest[0].compose.height = the_case.cfg_dest_h;
task->info.dest[0].flip = true;
}
static void case_run_rgb_compose(struct mml_test *test,
struct mml_ut *cur)
{
case_general_submit(test, cur, setup_rgb_compose);
}
/* setup_relay/case_run_relay - enable pq engine to test relay mode
*
* format in: RGB888
* format out: RGB888
*/
static void case_config_relay_crop(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void setup_relay(struct mml_submit *task, struct mml_ut *cur)
{
task->info.dest[0].pq_config.en = true;
task->info.dest[0].crop.r.left = 0;
task->info.dest[0].crop.r.top = 0;
task->info.dest[0].crop.r.width = the_case.cfg_dest_w;
task->info.dest[0].crop.r.height = the_case.cfg_dest_h;
}
static void case_run_relay(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_relay);
}
/* case_config_rsz_up2
* resize to up scale x2
*
* format in: RGB888
* format out: RGB888
* scale: 2
*/
static void case_config_rsz_up2(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w * 2;
the_case.cfg_dest_h = mml_test_h * 2;
}
/* case_config_nv12 / setup_nv12 / setup_nv12
* test format nv12
*
* format in: NV12 (YUV420/2 plane)
* format out: NV12 (YUV420/2 plane)
*/
static void case_config_nv12(void)
{
the_case.cfg_src_format = MML_FMT_NV12;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_NV12;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void setup_nv12(struct mml_submit *task, struct mml_ut *cur)
{
/* check src with 2 plane size */
if (task->buffer.src.size[0] + task->buffer.src.size[1] !=
cur->size_in)
mml_err("[test]%s case %d src size total %u plane %u %u",
__func__, mml_case, cur->size_in,
task->buffer.src.size[0], task->buffer.src.size[1]);
/* check dest 0 with 2 plane size */
if (task->buffer.dest[0].size[0] + task->buffer.dest[0].size[1] !=
cur->size_out)
mml_err("[test]%s case %d dest size total %u plane %u %u",
__func__, mml_case, cur->size_out,
task->buffer.dest[0].size[0], task->buffer.dest[0].size[1]);
}
static void case_run_nv12(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_nv12);
}
/* case_config_yuyv_down2
* test format yuyv (1 plane yuv422)
*
* format in: RGB888
* format out: YUYV
*/
static void case_config_yuyv_down2(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_YUYV;
the_case.cfg_dest_w = mml_test_w / 2;
the_case.cfg_dest_h = mml_test_h / 2;
}
/* case_config_block_to_nv12
* test format block to nv12
*
* format in: block 420
* format out: NV12
*/
static void case_config_block_to_nv12(void)
{
the_case.cfg_src_format = MML_FMT_BLK;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_NV12;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void setup_block_to_nv12(struct mml_submit *task,
struct mml_ut *cur)
{
/* check dest 0 with 2 plane size */
if (task->buffer.dest[0].size[0] + task->buffer.dest[0].size[1] !=
cur->size_out)
mml_err("[test]%s case %d dest size total %u plane %u %u",
__func__, mml_case, cur->size_out,
task->buffer.dest[0].size[0], task->buffer.dest[0].size[1]);
}
static void case_run_block_to_nv12(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_block_to_nv12);
}
/* case_config_afbc / setup_afbc / case_run_afbc
* rgb to afbc. setup compose since afbc format size 32x32 block
*
* format in: RGB888
* format out: AFBC_RGBA8888
*/
static void case_config_afbc(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGBA8888_AFBC;
the_case.cfg_dest_w = mml_afbc_align(mml_test_w);
the_case.cfg_dest_h = mml_afbc_align(mml_test_h);
}
static void setup_afbc(struct mml_submit *task, struct mml_ut *cur)
{
task->info.dest[0].compose.left = 0;
task->info.dest[0].compose.top = 0;
task->info.dest[0].compose.width = mml_test_w;
task->info.dest[0].compose.height = mml_test_h;
task->info.dest[0].data.width = mml_test_w;
task->info.dest[0].data.height = mml_test_h;
task->info.dest[0].data.vert_stride =
mml_afbc_align(task->info.dest[0].data.height);
}
static void case_run_afbc(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_afbc);
}
/* case_config_afbc_to_rgb
* afbc (from last case) to rgb. Source size align 32x32 and
* this case crop content roi, which is height 1080.
*
* format in: AFBC_RGBA8888
* format out: RGB888
*/
static void case_config_afbc_to_rgb(void)
{
the_case.cfg_src_format = MML_FMT_RGBA8888_AFBC;
the_case.cfg_src_w = mml_afbc_align(mml_test_w);
the_case.cfg_src_h = mml_afbc_align(mml_test_h);
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void setup_afbc_to_rgb(struct mml_submit *task,
struct mml_ut *cur)
{
task->info.dest[0].crop.r.left = 0;
task->info.dest[0].crop.r.top = 0;
task->info.dest[0].crop.r.width = mml_test_w;
task->info.dest[0].crop.r.height = mml_test_h;
}
static void case_run_afbc_to_rgb(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_afbc_to_rgb);
}
/* case_config_yuv_afbc_to_rgb
* yuv afbc (from last case) to rgb. Source size align 16x16 and
* this case crop content roi, which is height 640.
*
* format in: NV12_AFBC_RGBA8888
* format out: RGB888
*/
#define mml_yuv_afbc_align(p) (((p + 15) >> 4) << 4)
static void case_config_yuv_afbc_to_rgb(void)
{
the_case.cfg_src_format = MML_FMT_YUV420_AFBC;
the_case.cfg_src_w = mml_yuv_afbc_align(mml_test_w);
the_case.cfg_src_h = mml_yuv_afbc_align(mml_test_h);
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void case_config_yuv_afbc_10_to_rgb(void)
{
the_case.cfg_src_format = MML_FMT_YUV420_10P_AFBC;
the_case.cfg_src_w = mml_yuv_afbc_align(mml_test_w);
the_case.cfg_src_h = mml_yuv_afbc_align(mml_test_h);
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void setup_yuv_afbc_to_rgb(struct mml_submit *task,
struct mml_ut *cur)
{
task->info.dest[0].crop.r.left = 0;
task->info.dest[0].crop.r.top = 0;
task->info.dest[0].crop.r.width = mml_test_w;
task->info.dest[0].crop.r.height = mml_test_h;
}
static void case_run_yuv_afbc_to_rgb(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_yuv_afbc_to_rgb);
}
/* case_config_2out
* 1 in 2 out + resize
*
* format in: RGB888
* format out0: NV12
* format out1: RGB resize 256x256
*/
static void case_config_2out(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_NV12;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
the_case.cfg_dest1_format = MML_FMT_RGB888;
the_case.cfg_dest1_w = 256;
the_case.cfg_dest1_h = 256;
}
static void setup_2out(struct mml_submit *task, struct mml_ut *cur)
{
/* config dest[1] info data and buf */
task->info.dest_cnt = 2;
task->buffer.dest_cnt = 2;
fillin_info_data(the_case.cfg_dest1_format,
the_case.cfg_dest1_w, the_case.cfg_dest1_h,
&task->info.dest[1].data);
if (cur->use_dma)
fillin_buf_dma(&task->info.dest[1].data, cur->buf_dest[1], cur->dma_size_out[1],
&task->buffer.dest[1]);
else
fillin_buf(&task->info.dest[1].data, cur->fd_out1, cur->size_out1,
&task->buffer.dest[1]);
}
static void case_run_2out(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_2out);
}
/* case_config_2in_2out
* 2 in 2 out + resize
*
* format in0: RGB888
* format in1: Y8
* format out0: NV12
* format out1: RGB resize 256x256
*/
static void case_config_2in_2out(void)
{
the_case.cfg_src_format = mml_test_in_fmt;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_src1_format = MML_FMT_GREY;
the_case.cfg_src1_w = 256;
the_case.cfg_src1_h = 256;
the_case.cfg_dest_format = mml_test_out_fmt;
the_case.cfg_dest_w = mml_test_out_w;
the_case.cfg_dest_h = mml_test_out_h;
the_case.cfg_dest1_format = MML_FMT_RGB888;
the_case.cfg_dest1_w = 256;
the_case.cfg_dest1_h = 256;
}
static void setup_2in_2out(struct mml_submit *task, struct mml_ut *cur)
{
task->info.dest[0].crop.r.left = mml_test_crop_left;
task->info.dest[0].crop.r.top = mml_test_crop_top;
task->info.dest[0].crop.r.width = mml_test_crop_width;
task->info.dest[0].crop.r.height = mml_test_crop_height;
task->info.dest[0].rotate = mml_test_rot;
task->info.dest[0].flip = mml_test_flip;
fillin_info_data(the_case.cfg_src1_format,
the_case.cfg_src1_w, the_case.cfg_src1_h,
&task->info.seg_map);
fillin_buf(&task->info.seg_map, cur->fd_in1, cur->size_in1,
&task->buffer.seg_map);
/* config dest[1] info data and buf */
task->info.dest_cnt = 2;
task->buffer.dest_cnt = 2;
task->info.dest[0].pq_config.en_region_pq = true;
fillin_info_data(the_case.cfg_dest1_format,
the_case.cfg_dest1_w, the_case.cfg_dest1_h,
&task->info.dest[1].data);
if (cur->use_dma)
fillin_buf_dma(&task->info.dest[1].data, cur->buf_dest[1], cur->dma_size_out[1],
&task->buffer.dest[1]);
else
fillin_buf(&task->info.dest[1].data, cur->fd_out1, cur->size_out1,
&task->buffer.dest[1]);
}
static void case_run_2in_2out(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_2in_2out);
}
/* case_config_2out_crop
* 1 in 2 out + resize
*
* format in: RGB888
* format out0: NV12
* format out1: RGB resize 256x256 + crop
*/
static void case_config_2out_crop(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_NV12;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
the_case.cfg_dest1_format = MML_FMT_RGB888;
the_case.cfg_dest1_w = mml_test_w / 2;
the_case.cfg_dest1_h = mml_test_h / 2;
}
static void setup_2out_crop(struct mml_submit *task, struct mml_ut *cur)
{
setup_2out(task, cur);
/* config dest[1] crop */
task->info.dest[1].crop.r.left = mml_test_w / 4;
task->info.dest[1].crop.r.top = mml_test_w / 4;
task->info.dest[1].crop.r.width = task->info.dest[1].data.width;
task->info.dest[1].crop.r.height = task->info.dest[1].data.height;
}
static void case_run_2out_crop(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_2out_crop);
}
/* case_config_2out_crop_compose
* 1 in 2 out + resize + compose
*
* format in: RGB888
* format out0: NV12
* format out1: RGB resize 256x256 + crop + compose
*/
#define CASE_CROP_W 256
#define CASE_CROP_H 256
static void case_config_2out_crop_compose(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_NV12;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
the_case.cfg_dest1_format = MML_FMT_RGB888;
/* still use full size cause compose */
the_case.cfg_dest1_w = mml_test_w;
the_case.cfg_dest1_h = mml_test_h;
}
#define central(out, in) (out / 2 - in / 2)
static void setup_2out_crop_compose(struct mml_submit *task,
struct mml_ut *cur)
{
setup_2out(task, cur);
/* config dest[1] crop */
task->info.dest[1].crop.r.left = central(mml_test_w, CASE_CROP_W);
task->info.dest[1].crop.r.top = central(mml_test_h, CASE_CROP_H);
task->info.dest[1].crop.r.width = task->info.dest[1].data.width;
task->info.dest[1].crop.r.height = task->info.dest[1].data.height;
task->info.dest[1].compose.left = central(mml_test_w, CASE_CROP_W);
task->info.dest[1].compose.top = central(mml_test_h, CASE_CROP_H);
task->info.dest[1].compose.width = mml_test_w;
task->info.dest[1].compose.height = mml_test_h;
}
static void case_run_2out_crop_compose(struct mml_test *test,
struct mml_ut *cur)
{
case_general_submit(test, cur, setup_2out_crop_compose);
}
/* case_config_crop_offset / setup_crop / case_run_crop
*
* format in: RGB888
* format out0: RGB888 crop
*/
#define RGB_CROP_OFF_W (mml_test_w / 2)
#define RGB_CROP_OFF_H (mml_test_h / 2)
static void case_config_crop(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = RGB_CROP_OFF_W;
the_case.cfg_dest_h = RGB_CROP_OFF_H;
}
static void setup_crop(struct mml_submit *task, struct mml_ut *cur)
{
task->info.dest[0].crop.r.left = central(mml_test_w, RGB_CROP_OFF_W);
task->info.dest[0].crop.r.top = central(mml_test_h, RGB_CROP_OFF_H);
task->info.dest[0].crop.r.width = RGB_CROP_OFF_W;
task->info.dest[0].crop.r.height = RGB_CROP_OFF_H;
}
static void case_run_crop(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_crop);
}
/* case_config_yv12_yuyv/setup_nv12_yuyv/case_run_nv12_yuyv
*
* format in: MML_FMT_YV12
* format out: MML_FMT_YUYV
*/
static void case_config_yv12_yuyv(void)
{
the_case.cfg_src_format = MML_FMT_YV12;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_YUYV;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_h;
}
static void setup_yv12_yuyv(struct mml_submit *task, struct mml_ut *cur)
{
if (task->buffer.src.size[0] + task->buffer.src.size[1]
+ task->buffer.src.size[2] !=
cur->size_in)
mml_err("[test]%s case %d src size total %u plane %u %u",
__func__, mml_case, cur->size_in,
task->buffer.src.size[0], task->buffer.src.size[1]);
}
static void case_run_yv12_yuyv(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_yv12_yuyv);
}
/* setup_write_sram/case_run_write_sram
*
* format in: MML_FMT_RGB888
* format out: MML_FMT_RGB888
*/
static void setup_write_sram(struct mml_submit *task, struct mml_ut *cur)
{
task->info.mode = MML_MODE_RACING;
task->info.dest[0].crop.r.left = 0;
task->info.dest[0].crop.r.top = 0;
task->info.dest[0].crop.r.width = task->info.src.width;
task->info.dest[0].crop.r.height = task->info.src.height;
task->buffer.dest[0].flush = false;
task->buffer.dest[0].invalid = false;
task->buffer.dest[0].fd[0] = -1;
task->info.dest[0].rotate = mml_test_rot;
task->info.dest[0].flip = mml_test_flip;
}
static void case_run_write_sram(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_write_sram);
}
/* case_config_read_sram / setup_read_sram
*
* format in: MML_FMT_RGB888
* format out: MML_FMT_RGB888
*/
#define SRAM_HEIGHT 64
#define SRAM_SIZE (512 * 1024)
static void case_config_read_sram(void)
{
the_case.cfg_src_format = mml_test_in_fmt;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = mml_test_out_fmt;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_out_h;
}
static void setup_read_sram_bufa(struct mml_submit *task, struct mml_ut *cur)
{
task->info.mode = MML_MODE_SRAM_READ;
task->buffer.src.flush = false;
task->buffer.src.invalid = false;
task->buffer.src.fd[0] = -1;
task->info.src.height = SRAM_HEIGHT;
task->info.dest[0].data.height = SRAM_HEIGHT;
task->buffer.src.size[0] /= 2;
task->buffer.src.size[1] /= 2;
task->buffer.src.size[2] /= 2;
task->info.dest[0].crop.r.width = cur->cfg_src_w;
task->info.dest[0].crop.r.height = SRAM_HEIGHT;
}
static void setup_read_sram_bufb(struct mml_submit *task, struct mml_ut *cur)
{
u32 src_offset = SRAM_SIZE, offset;
mml_log("[test]%s read buf", __func__);
task->info.mode = MML_MODE_SRAM_READ;
task->buffer.src.flush = false;
task->buffer.src.invalid = false;
task->buffer.src.fd[0] = -1;
task->info.src.height = SRAM_HEIGHT;
task->info.dest[0].data.height = SRAM_HEIGHT;
task->buffer.src.size[0] /= 2;
task->buffer.src.size[1] /= 2;
task->buffer.src.size[2] /= 2;
task->info.dest[0].crop.r.width = cur->cfg_src_w;
task->info.dest[0].crop.r.height = SRAM_HEIGHT;
task->info.src.plane_offset[0] = src_offset;
task->info.src.plane_offset[1] = src_offset;
task->info.src.plane_offset[2] = src_offset;
offset = mml_color_get_min_y_size(task->info.dest[0].data.format,
task->info.dest[0].data.width, task->info.dest[0].data.height);
mml_log("[test]%s dest plane offset %u", __func__, offset);
task->info.dest[0].data.plane_offset[0] = offset;
task->info.dest[0].data.plane_offset[1] = offset;
task->info.dest[0].data.plane_offset[2] = offset;
}
static void case_run_read_sram(struct mml_test *test, struct mml_ut *cur)
{
struct platform_device *mml_pdev;
struct device *dev;
struct mml_drm_ctx *mml_ctx;
struct mml_drm_param disp = {.vdo_mode = true};
void *mml;
/* create context */
mml_pdev = mml_get_plat_device(test->pdev);
mml_ctx = mml_drm_get_context(mml_pdev, &disp);
/* hold sram, for wrot out and rdma in */
dev = &mml_pdev->dev;
if (unlikely(!dev)) {
mml_err("%s dev = null", __func__);
return;
}
mml = dev_get_drvdata(dev);
mml_sram_get(mml);
msleep_interruptible(mml_test_interval);
/* correct the format in sram */
the_case.cfg_src_format = the_case.cfg_dest_format;
the_case.cfg_dest_h = SRAM_HEIGHT;
/* sram -> dram */
cur->fd_in = -1;
case_general_submit(test, cur, setup_read_sram_bufa);
case_general_submit(test, cur, setup_read_sram_bufb);
/* release */
mml_sram_put(mml);
mml_drm_put_context(mml_ctx);
}
/* case_config_wr_sram / case_run_wr_sram
*
* format in: MML_FMT_RGB888
* format out: MML_FMT_RGB888
*/
static void case_config_wr_sram(void)
{
the_case.cfg_src_format = mml_test_in_fmt;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = mml_test_out_fmt;
the_case.cfg_dest_w = mml_test_out_w;
the_case.cfg_dest_h = SRAM_HEIGHT * 2;
}
static void setup_write_sram_crop(struct mml_submit *task, struct mml_ut *cur)
{
task->info.mode = MML_MODE_RACING;
task->info.dest[0].crop.r.left = 0;
task->info.dest[0].crop.r.top = 0;
task->info.dest[0].crop.r.width = mml_test_out_w;
task->info.dest[0].crop.r.height = mml_test_out_h;
if (mml_test_rot == 1 || mml_test_rot == 3)
swap(task->info.dest[0].crop.r.width, task->info.dest[0].crop.r.height);
task->buffer.dest[0].flush = false;
task->buffer.dest[0].invalid = false;
task->buffer.dest[0].fd[0] = -1;
task->info.dest[0].rotate = mml_test_rot;
task->info.dest[0].flip = mml_test_flip;
}
static void case_run_wr_sram(struct mml_test *test, struct mml_ut *cur)
{
struct platform_device *mml_pdev;
struct device *dev;
struct mml_drm_ctx *mml_ctx;
struct mml_drm_param disp = {.vdo_mode = true};
void *mml;
int32_t fd = -1;
/* create context */
if (unlikely(!test)) {
mml_err("%s test = null", __func__);
return;
}
mml_pdev = mml_get_plat_device(test->pdev);
if (unlikely(!mml_pdev)) {
mml_err("%s mml_pdev = null", __func__);
return;
}
mml_ctx = mml_drm_get_context(mml_pdev, &disp);
if (unlikely(!mml_ctx)) {
mml_err("%s mml_ctx = null", __func__);
return;
}
/* hold sram, for wrot out and rdma in */
dev = &mml_pdev->dev;
if (unlikely(!dev)) {
mml_drm_put_context(mml_ctx);
mml_err("%s dev = null", __func__);
return;
}
mml = dev_get_drvdata(dev);
mml_sram_get(mml);
/* dram -> sram */
swap(fd, cur->fd_out);
case_general_submit(test, cur, setup_write_sram_crop);
/* correct the format in sram */
cur->cfg_src_format = cur->cfg_dest_format;
cur->cfg_src_w = mml_test_out_w;
cur->cfg_dest_h = SRAM_HEIGHT;
/* sram -> dram */
cur->fd_out = fd;
cur->fd_in = -1;
case_general_submit(test, cur, setup_read_sram_bufa);
case_general_submit(test, cur, setup_read_sram_bufb);
/* release */
mml_sram_put(mml);
mml_drm_put_context(mml_ctx);
}
/* case_config_rgb_up1_5
* test format rgb888 scale up 1.5
*
* format in: RGB888
* format out: RGB888
*/
static void case_config_rgb_up1_5(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w * 3 / 2;
the_case.cfg_dest_h = mml_test_h * 3 / 2;
}
/* case_config_rgb_down2
* test format rgb888 scale down 2
*
* format in: RGB888
* format out: RGB888
*/
static void case_config_rgb_down2(void)
{
the_case.cfg_src_format = MML_FMT_RGB888;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = MML_FMT_RGB888;
the_case.cfg_dest_w = mml_test_w / 2;
the_case.cfg_dest_h = mml_test_h / 2;
}
/* case_config_blk_manual
* test format blk scale manual
*
* format in: blk
* format out: YUYV crop
*/
static void case_config_crop_manual(void)
{
the_case.cfg_src_format = mml_test_in_fmt;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = mml_test_out_fmt;
the_case.cfg_dest_w = mml_test_out_w;
the_case.cfg_dest_h = mml_test_out_h;
}
static void setup_crop_manual(struct mml_submit *task, struct mml_ut *cur)
{
task->info.dest[0].crop.r.left = mml_test_crop_left;
task->info.dest[0].crop.r.top = mml_test_crop_top;
task->info.dest[0].crop.r.width = mml_test_crop_width;
task->info.dest[0].crop.r.height = mml_test_crop_height;
task->info.dest[0].rotate = mml_test_rot;
task->info.dest[0].flip = mml_test_flip;
/* check dest 0 with 2 plane size */
if (task->buffer.dest[0].size[0] + task->buffer.dest[0].size[1] !=
cur->size_out)
mml_err("[test]%s case %d dest size total %u plane %u %u",
__func__, mml_case, cur->size_out,
task->buffer.dest[0].size[0], task->buffer.dest[0].size[1]);
}
static void case_run_crop_manual(struct mml_test *test, struct mml_ut *cur)
{
case_general_submit(test, cur, setup_crop_manual);
}
/* case_run_sram_frame / setup_read_sram_frame
*
* format in: MML_FMT_RGB888
* format out: MML_FMT_RGB888
*/
static void case_config_sram_frame(void)
{
the_case.cfg_src_format = mml_test_in_fmt;
the_case.cfg_src_w = mml_test_w;
the_case.cfg_src_h = mml_test_h;
the_case.cfg_dest_format = mml_test_out_fmt;
the_case.cfg_dest_w = mml_test_w;
the_case.cfg_dest_h = mml_test_out_h;
}
static void setup_read_sram_frame(struct mml_submit *task, struct mml_ut *cur)
{
task->info.mode = MML_MODE_APUDC;
task->buffer.src.flush = false;
task->buffer.src.invalid = false;
task->buffer.src.fd[0] = -1;
task->info.src.height = SRAM_HEIGHT;
task->info.dest[0].data.height = SRAM_HEIGHT;
task->buffer.src.size[0] /= 2;
task->buffer.src.size[1] /= 2;
task->buffer.src.size[2] /= 2;
}
static void case_run_sram_frame(struct mml_test *test, struct mml_ut *cur)
{
struct platform_device *mml_pdev;
struct device *dev;
struct mml_drm_ctx *mml_ctx;
struct mml_drm_param disp = {.vdo_mode = true};
void *mml;
/* create context */
mml_pdev = mml_get_plat_device(test->pdev);
mml_ctx = mml_drm_get_context(mml_pdev, &disp);
/* hold sram, for wrot out and rdma in */
dev = &mml_pdev->dev;
if (unlikely(!dev)) {
mml_err("%s dev = null", __func__);
return;
}
mml = dev_get_drvdata(dev);
mml_sram_get(mml);
msleep_interruptible(mml_test_interval);
/* correct the format in sram */
the_case.cfg_src_format = the_case.cfg_dest_format;
the_case.cfg_dest_h = SRAM_HEIGHT;
/* sram -> dram */
cur->fd_in = -1;
case_general_submit(test, cur, setup_read_sram_frame);
/* release */
mml_sram_put(mml);
mml_drm_put_context(mml_ctx);
}
enum mml_ut_case {
MML_UT_RGB, /* 0 */
MML_UT_RGB_ROTATE, /* 1 */
MML_UT_COMPOSE_FLIP, /* 2 */
MML_UT_RESIZE_RELAY, /* 3 */
MML_UT_RESIZE_UP2, /* 4 */
MML_UT_NV12, /* 5 */
MML_UT_YUYV_DOWN2, /* 6 */
MML_UT_BLOCK_TO_NV12, /* 7 */
MML_UT_AFBC, /* 8 */
MML_UT_AFBC_TO_RGB, /* 9 */
MML_UT_2OUT, /* 10 */
MML_UT_2OUT_CROP, /* 11 */
MML_UT_2OUT_RCC, /* 12 */
MML_UT_CROP, /* 13 */
MML_UT_YV12_YUYV, /* 14 */
MML_UT_WRITE_SRAM, /* 15 */
MML_UT_READ_SRAM, /* 16 */
MML_UT_WR_SRAM, /* 17 */
MML_UT_RESIZE_UP1_5, /* 18 */
MML_UT_RGB_DOWN2, /* 19 */
MML_UT_BLK_MANUAL, /* 20 */
MML_UT_YUV_AFBC_TO_RGB, /* 21 */
MML_UT_YUV_AFBC_10_TO_RGB, /* 22 */
MML_UT_2IN_2OUT, /* 23 */
MML_UT_SRAM_FRAME, /* 24 */
MML_UT_TOTAL
};
static struct test_case_op cases[MML_UT_TOTAL] = {
[MML_UT_RGB] = {
.config = case_config_rgb,
.run = case_run_general,
},
[MML_UT_RGB_ROTATE] = {
.config = case_config_rgb_rot,
.run = case_run_rgb_rot,
},
[MML_UT_COMPOSE_FLIP] = {
.config = case_config_rgb_compose,
.run = case_run_rgb_compose,
},
[MML_UT_RESIZE_RELAY] = {
.config = case_config_relay_crop,
.run = case_run_relay,
},
[MML_UT_RESIZE_UP2] = {
.config = case_config_rsz_up2,
.run = case_run_general,
},
[MML_UT_NV12] = {
.config = case_config_nv12,
.run = case_run_nv12,
},
[MML_UT_YUYV_DOWN2] = {
.config = case_config_yuyv_down2,
.run = case_run_general,
},
[MML_UT_BLOCK_TO_NV12] = {
.config = case_config_block_to_nv12,
.run = case_run_block_to_nv12,
},
[MML_UT_AFBC] = {
.config = case_config_afbc,
.run = case_run_afbc,
},
[MML_UT_AFBC_TO_RGB] = {
.config = case_config_afbc_to_rgb,
.run = case_run_afbc_to_rgb,
},
[MML_UT_2OUT] = {
.config = case_config_2out,
.run = case_run_2out,
},
[MML_UT_2OUT_CROP] = {
.config = case_config_2out_crop,
.run = case_run_2out_crop,
},
[MML_UT_2OUT_RCC] = {
.config = case_config_2out_crop_compose,
.run = case_run_2out_crop_compose,
},
[MML_UT_CROP] = {
.config = case_config_crop,
.run = case_run_crop,
},
[MML_UT_YV12_YUYV] = {
.config = case_config_yv12_yuyv,
.run = case_run_yv12_yuyv,
},
[MML_UT_WRITE_SRAM] = {
.config = case_config_rgb,
.run = case_run_write_sram,
},
[MML_UT_READ_SRAM] = {
.config = case_config_read_sram,
.run = case_run_read_sram,
},
[MML_UT_WR_SRAM] = {
.config = case_config_wr_sram,
.run = case_run_wr_sram,
},
[MML_UT_RESIZE_UP1_5] = {
.config = case_config_rgb_up1_5,
.run = case_run_general,
},
[MML_UT_RGB_DOWN2] = {
.config = case_config_rgb_down2,
.run = case_run_general,
},
[MML_UT_BLK_MANUAL] = {
.config = case_config_crop_manual,
.run = case_run_crop_manual,
},
[MML_UT_YUV_AFBC_TO_RGB] = {
.config = case_config_yuv_afbc_to_rgb,
.run = case_run_yuv_afbc_to_rgb,
},
[MML_UT_YUV_AFBC_10_TO_RGB] = {
.config = case_config_yuv_afbc_10_to_rgb,
.run = case_run_yuv_afbc_to_rgb,
},
[MML_UT_2IN_2OUT] = {
.config = case_config_2in_2out,
.run = case_run_2in_2out,
},
[MML_UT_SRAM_FRAME] = {
.config = case_config_sram_frame,
.run = case_run_sram_frame,
},
};
static ssize_t test_read(struct file *filep, char __user *buf, size_t size,
loff_t *offset)
{
struct mml_test_case user_case = {0};
u32 len = sizeof(user_case);
int ret;
if (size < sizeof(user_case)) {
mml_err("[test] buf size not match %zu %u",
sizeof(user_case), len);
return -EFAULT;
}
if (mml_case < ARRAY_SIZE(cases) && cases[mml_case].config)
cases[mml_case].config();
else
mml_err("[test]no such case %d", mml_case);
user_case.cfg_src_format = the_case.cfg_src_format;
user_case.cfg_src_w = the_case.cfg_src_w;
user_case.cfg_src_h = the_case.cfg_src_h;
user_case.cfg_dest_format = the_case.cfg_dest_format;
user_case.cfg_dest_w = the_case.cfg_dest_w;
user_case.cfg_dest_h = the_case.cfg_dest_h;
user_case.cfg_dest1_format = the_case.cfg_dest1_format;
user_case.cfg_dest1_w = the_case.cfg_dest1_w;
user_case.cfg_dest1_h = the_case.cfg_dest1_h;
user_case.fd_in = the_case.fd_in;
user_case.size_in = the_case.size_in;
user_case.fd_out = the_case.fd_out;
user_case.size_out = the_case.size_out;
user_case.fd_out1 = the_case.fd_out1;
user_case.size_out1 = the_case.size_out1;
ret = copy_to_user(buf, &user_case, len);
if (ret) {
mml_err("[test]%s copy case fail %d", __func__, ret);
return -EFAULT;
}
*offset += len;
mml_log("[test]%s format src %#010x dest %#010x",
__func__, user_case.cfg_src_format, user_case.cfg_dest_format);
return 0;
}
static ssize_t test_write(struct file *filp, const char *buf, size_t count,
loff_t *offp)
{
struct mml_test *test = (struct mml_test *)filp->f_inode->i_private;
struct mml_test_case user_case = {0};
struct mml_ut cur = {0};
if (count > sizeof(user_case)) {
mml_err("[test]buf count not match %zu %zu", count, sizeof(user_case));
return -EFAULT;
}
if (copy_from_user(&user_case, buf, count)) {
mml_err("[test]copy_from_user failed len:%zu", count);
return -EFAULT;
}
cur.cfg_src_format = user_case.cfg_src_format;
cur.cfg_src_w = user_case.cfg_src_w;
cur.cfg_src_h = user_case.cfg_src_h;
cur.cfg_dest_format = user_case.cfg_dest_format;
cur.cfg_dest_w = user_case.cfg_dest_w;
cur.cfg_dest_h = user_case.cfg_dest_h;
cur.cfg_dest1_format = user_case.cfg_dest1_format;
cur.cfg_dest1_w = user_case.cfg_dest1_w;
cur.cfg_dest1_h = user_case.cfg_dest1_h;
cur.fd_in = user_case.fd_in;
cur.size_in = user_case.size_in;
cur.fd_out = user_case.fd_out;
cur.size_out = user_case.size_out;
cur.fd_out1 = user_case.fd_out1;
cur.size_out1 = user_case.size_out1;
if (mml_case < ARRAY_SIZE(cases) && cases[mml_case].run)
cases[mml_case].run(test, &cur);
return count;
}
static const struct file_operations test_fops = {
.read = test_read,
.write = test_write,
};
static void mml_test_fill_frame_rgb888(u8 *va, u32 width, u32 height)
{
u32 x, y;
const u32 step = 0xff;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
u32 r = step * x / width;
u32 g = step * y / height;
u32 b = (r + g) / 2;
u32 idx = (y * width + x) * 3;
va[idx] = r;
va[idx + 1] = g;
va[idx + 2] = b;
}
}
}
static void mml_test_fill_frame_rgba8888(u8 *va, u32 width, u32 height)
{
u32 x, y;
const u32 step = 0xff;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
u32 r = step * x / width;
u32 g = step * y / height;
u32 b = (r + g) / 2;
u32 idx = (y * width + x) * 4;
va[idx] = r;
va[idx + 1] = g;
va[idx + 2] = b;
va[idx + 3] = x * y * step / width / height;
}
}
}
static struct dma_buf *mml_test_create_buf(struct dma_heap *heap, u32 size)
{
struct dma_buf *frame_buf;
/* fd flags align API dmabuf_heap_alloc in libdmabufheap.so */
frame_buf = dma_heap_buffer_alloc(heap, size,
O_RDWR | O_CLOEXEC, DMA_HEAP_VALID_HEAP_FLAGS);
if (IS_ERR_OR_NULL(frame_buf)) {
mml_err("[test]buffer alloc fail %d heap %p size %u",
PTR_ERR(frame_buf), heap, size);
return frame_buf;
}
return frame_buf;
}
static int mml_test_alloc_frame(struct dma_heap *heap, struct mml_buffer *buf,
u32 format, u32 width, u32 height)
{
u32 plane, buf_size;
struct dma_buf *dmabuf;
mml_log("[test]%s format %#x res %u %u", __func__, format, width, height);
if (MML_FMT_AFBC(format)) {
mml_err("[test]not support format %#x", format);
return -EINVAL;
}
plane = MML_FMT_PLANE(format);
buf_size = mml_color_get_min_y_size(format, width, height);
if (plane == 2)
buf_size += mml_color_get_min_uv_size(format, width, height);
else if (plane >= 3)
buf_size += mml_color_get_min_uv_size(format, width, height) * 2;
dmabuf = mml_test_create_buf(heap, buf_size);
if (IS_ERR_OR_NULL(dmabuf))
return PTR_ERR(dmabuf);
buf->dmabuf[0] = dmabuf;
buf->size[0] = buf_size;
buf->cnt = plane;
buf->use_dma = true;
return 0;
}
static void mml_test_free_frame(struct mml_buffer *buf)
{
if (!buf->dmabuf[0])
return;
dma_heap_buffer_free(buf->dmabuf[0]);
}
static int mml_test_create_src(struct dma_heap *heap, struct mml_ut *cur_case,
struct mml_buffer *buf)
{
void *va;
struct dma_buf_map map = {0};
int ret;
if (mml_test_alloc_frame(heap, buf, cur_case->cfg_src_format,
cur_case->cfg_src_w, cur_case->cfg_src_h) < 0)
return -EINVAL;
/* retrieve va to fill in raw data */
ret = dma_buf_vmap(buf->dmabuf[0], &map);
if (ret) {
mml_err("[test]fail to vmap");
return -ENOMEM;
}
va = map.vaddr;
switch (cur_case->cfg_src_format) {
case MML_FMT_RGB888:
case MML_FMT_BGR888:
mml_test_fill_frame_rgb888(va, cur_case->cfg_src_w, cur_case->cfg_src_h);
break;
case MML_FMT_RGBA8888:
case MML_FMT_BGRA8888:
mml_test_fill_frame_rgba8888(va, cur_case->cfg_src_w, cur_case->cfg_src_h);
break;
default:
mml_err("[test]not support src format %#x", cur_case->cfg_src_format);
break;
}
buf->flush = true;
dma_buf_vunmap(buf->dmabuf[0], &map);
return 0;
}
static int mml_test_create_dest(struct dma_heap *heap, struct mml_ut *cur_case,
struct mml_buffer *buf)
{
u64 *va;
struct dma_buf_map map = {0};
u32 i;
int ret;
if (mml_test_alloc_frame(heap, buf, cur_case->cfg_dest_format,
cur_case->cfg_dest_w, cur_case->cfg_dest_h) < 0)
return -EINVAL;
/* retrieve va to fill in raw data */
ret = dma_buf_vmap(buf->dmabuf[0], &map);
if (ret) {
mml_err("[test]%s fail to vmap", __func__);
return -ENOMEM;
}
va = map.vaddr;
for (i = 0; i < buf->size[0] / 8; i++)
va[i] = 0xdeadbeef + i;
buf->flush = true;
dma_buf_vunmap(buf->dmabuf[0], &map);
return 0;
}
static struct mml_test *main_test;
static void mml_test_krun(u32 case_num)
{
struct mml_buffer src_buf = {0}, dest_buf = {0};
struct mml_ut cur = {0};
struct dma_heap *heap = NULL;
mml_log("[test]%s run case %u", __func__, case_num);
if (!main_test) {
mml_err("[test]test drv not probe");
goto end;
}
cur.cfg_src_format = mml_test_in_fmt;
cur.cfg_src_w = mml_test_w;
cur.cfg_src_h = mml_test_h;
cur.cfg_dest_format = mml_test_out_fmt;
cur.cfg_dest_w = mml_test_out_w;
cur.cfg_dest_h = mml_test_out_h;
cur.buf_src[0] = src_buf.dmabuf[0];
cur.dma_size_in[0] = src_buf.size[0];
cur.buf_dest[0] = dest_buf.dmabuf[0];
cur.dma_size_out[0] = dest_buf.size[0];
cur.use_dma = true;
heap = dma_heap_find("mtk_mm-uncached");
if (!heap) {
mml_err("[test]heap find fail");
goto end;
}
if (mml_test_create_src(heap, &cur, &src_buf) < 0)
goto free_heap;
if (mml_test_create_dest(heap, &cur, &dest_buf) < 0)
goto free_heap;
cur.buf_src[0] = src_buf.dmabuf[0];
cur.dma_size_in[0] = src_buf.size[0];
cur.buf_src[1] = src_buf.dmabuf[1];
cur.dma_size_in[1] = src_buf.size[1];
cur.buf_dest[0] = dest_buf.dmabuf[0];
cur.dma_size_out[0] = dest_buf.size[0];
cur.buf_dest[1] = dest_buf.dmabuf[1];
cur.dma_size_out[1] = dest_buf.size[1];
if (case_num < ARRAY_SIZE(cases) && cases[case_num].run)
cases[case_num].run(main_test, &cur);
free_heap:
/* put heap struct after use it done.
* put times must same with get pass times, otherwise heap will disappear.
*/
dma_heap_put(heap);
end:
mml_test_free_frame(&src_buf);
mml_test_free_frame(&dest_buf);
}
static int mml_test_krun_set(const char *val, const struct kernel_param *kp)
{
int result;
u32 case_num;
result = kstrtoint(val, 0, &case_num);
if (result) {
mml_err("[test]case num fail %d", result);
return result;
}
mml_test_krun(case_num);
return 0;
}
static struct kernel_param_ops krun_ops = {
.set = mml_test_krun_set,
};
module_param_cb(mml_test_ut, &krun_ops, NULL, 0644);
static int mml_test_inst_print(struct seq_file *seq, void *data)
{
u32 size;
char *insts = mml_core_get_dump_inst(&size);
mml_log("[test]%s dump inst buf size %u", __func__, size);
seq_printf(seq, "%s\n", insts);
return 0;
}
static int mml_test_inst_open(struct inode *inode, struct file *file)
{
return single_open(file, mml_test_inst_print, inode->i_private);
}
static const struct file_operations mml_inst_dump_fops = {
.owner = THIS_MODULE,
.open = mml_test_inst_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mml_test_frame_in(struct seq_file *seq, void *data)
{
struct mml_frm_dump_data *frm = mml_core_get_frame_in();
if (!frm->size) {
mml_log("[test]%s no data to dump", __func__);
return 0;
}
mml_log("[test]%s dump frame %s size %u", __func__, frm->name, frm->size);
seq_write(seq, frm->frame, frm->size);
return 0;
}
static int mml_test_frame_in_open(struct inode *inode, struct file *file)
{
return single_open(file, mml_test_frame_in, inode->i_private);
}
static const struct file_operations mml_frame_in_fops = {
.owner = THIS_MODULE,
.open = mml_test_frame_in_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mml_test_frame_out(struct seq_file *seq, void *data)
{
struct mml_frm_dump_data *frm = mml_core_get_frame_out();
if (!frm->size) {
mml_log("[test]%s no data to dump", __func__);
return 0;
}
mml_log("[test]%s dump frame %s size %u", __func__, frm->name, frm->size);
seq_write(seq, frm->frame, frm->size);
return 0;
}
static int mml_test_frame_out_open(struct inode *inode, struct file *file)
{
return single_open(file, mml_test_frame_out, inode->i_private);
}
static const struct file_operations mml_frame_out_fops = {
.owner = THIS_MODULE,
.open = mml_test_frame_out_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/* Following code implement mml dump service node */
int mml_dump_srv;
module_param(mml_dump_srv, int, 0644);
int mml_dump_srv_opt = DUMPOPT_ALL;
module_param(mml_dump_srv_opt, int, 0644);
#define MML_DUMP_NAME_MAX 128
enum MML_DUMPSRV_ACTION {
DUMPSRV_STOP,
DUMPSRV_CONTINUE,
DUMPSRV_WRITE_FILE,
};
/* must sync struct with mdp_ut */
struct mml_dump_job {
uint32_t action;
int32_t fd;
uint32_t size;
char name[MML_DUMP_NAME_MAX];
};
struct mml_dump_context {
wait_queue_head_t dump_queue;
struct completion write_done;
struct mutex job_mutex;
u64 stamp;
struct dma_buf *frame_dma_buf;
u32 size;
char name[MML_DUMP_NAME_MAX];
enum mml_dump_buf_t buf_type;
bool ready;
bool async;
};
static struct mml_dump_context dump_ctx[MMLDUMPT_CNT] = {
[MMLDUMPT_DEST] = {.buf_type = MMLDUMPT_DEST},
};
void mml_dump_buf(struct mml_task *task, struct mml_frame_data *data,
u32 width, u32 height, const char *prefix,
char *fmt, struct mml_file_buf *buf, enum mml_dump_buf_t buf_type, bool async)
{
u32 i;
u64 stamp;
int ret;
if (buf_type >= ARRAY_SIZE(dump_ctx)) {
mml_err("[dumpsrv]%s wrong type %d", __func__, buf_type);
return;
}
mutex_lock(&dump_ctx[buf_type].job_mutex);
if (!dump_ctx[buf_type].ready) {
mml_err("[dumpsrv]%s type %d skip job %u cause not ready",
__func__, buf_type, task->job.jobid);
goto exit;
}
dump_ctx[buf_type].stamp = sched_clock();
stamp = div_u64(dump_ctx[buf_type].stamp, 1000000);
dump_ctx[buf_type].async = async;
dump_ctx[buf_type].size = 0;
for (i = 0; i < MML_MAX_PLANES; i++)
dump_ctx[buf_type].size += buf->size[i];
ret = snprintf(dump_ctx[buf_type].name, sizeof(dump_ctx[buf_type].name),
"mml_%llu_%u_%s_f%s_%u_%u_%u.bin",
stamp, task->job.jobid, prefix, fmt,
width, height, data->y_stride);
if (ret < 0)
dump_ctx[buf_type].name[0] = 0;
dump_ctx[buf_type].frame_dma_buf = buf->dma[0].dmabuf;
dump_ctx[buf_type].ready = false;
task->dump_queued[buf_type] = true;
if (!async)
init_completion(&dump_ctx[buf_type].write_done);
wake_up(&dump_ctx[buf_type].dump_queue);
exit:
mutex_unlock(&dump_ctx[buf_type].job_mutex);
}
void mml_dump_wait(struct mml_task *task, enum mml_dump_buf_t buf_type)
{
if (!dump_ctx[buf_type].async)
wait_for_completion(&dump_ctx[buf_type].write_done);
mutex_lock(&dump_ctx[buf_type].job_mutex);
task->dump_queued[buf_type] = false;
dump_ctx[buf_type].ready = true;
mutex_unlock(&dump_ctx[buf_type].job_mutex);
}
static ssize_t dumpsrv_read(struct file *filp, char __user *buf, size_t size,
loff_t *offset)
{
struct mml_dump_context *dctx = (struct mml_dump_context *)filp->private_data;
struct mml_dump_job job;
int ret;
if (size != sizeof(struct mml_dump_job)) {
mml_err("[dumpsrv]size not match %u %zu",
size, sizeof(struct mml_dump_job));
return -EFAULT;
}
if (copy_from_user(&job, buf, sizeof(struct mml_dump_job))) {
mml_err("[dumpsrv]copy_from_user failed len:%zu",
sizeof(struct mml_dump_job));
return -EFAULT;
}
ret = wait_event_timeout(dctx->dump_queue, dctx->frame_dma_buf != NULL,
msecs_to_jiffies(1000));
if (!ret || !dctx->frame_dma_buf) {
if (mml_dump_srv == DUMPCTRL_DISABLE)
job.action = DUMPSRV_STOP;
else
job.action = DUMPSRV_CONTINUE;
goto exit;
}
mutex_lock(&dctx->job_mutex);
job.action = DUMPSRV_WRITE_FILE;
get_dma_buf(dctx->frame_dma_buf);
job.fd = dma_buf_fd(dctx->frame_dma_buf, O_RDWR | O_CLOEXEC);
job.size = dctx->size;
memcpy(job.name, dctx->name, sizeof(job.name));
mml_log("[dumpsrv]%s action %u fd %d size %u name %s buft %d opt %#x",
__func__, job.action, job.fd, job.size, job.name, dctx->buf_type,
mml_dump_srv_opt);
dctx->frame_dma_buf = NULL;
dctx->size = 0;
mutex_unlock(&dctx->job_mutex);
exit:
ret = copy_to_user(buf, &job, sizeof(job));
if (ret)
mml_err("[dumpsrv]%s fail to copy job %d", __func__, ret);
*offset += sizeof(job);
return sizeof(job);
}
static ssize_t dumpsrv_write(struct file *filp, const char __user *buf, size_t len,
loff_t *offset)
{
struct mml_dump_context *dctx = (struct mml_dump_context *)filp->private_data;
u64 stamp;
mutex_lock(&dctx->job_mutex);
if (dctx->async)
dctx->ready = true;
else
complete(&dctx->write_done);
stamp = div_u64(sched_clock() - dctx->stamp, 1000);
mml_log("[dumpsrv]%s done cost %lluus async %s",
dctx->name, stamp, dctx->async ? "true" : "false");
mutex_unlock(&dctx->job_mutex);
return 0;
}
static int dumpsrv_open(struct inode *node, struct file *filp)
{
mml_log("[dumpsrv]%s %p open", __func__, filp);
filp->private_data = &dump_ctx[MMLDUMPT_SRC];
dump_ctx[MMLDUMPT_SRC].ready = true;
return 0;
}
static int dumpsrv_dest_open(struct inode *node, struct file *filp)
{
mml_log("[dumpsrv]%s %p open", __func__, filp);
filp->private_data = &dump_ctx[MMLDUMPT_DEST];
dump_ctx[MMLDUMPT_DEST].ready = true;
return 0;
}
static int dumpsrv_release(struct inode *node, struct file *filp)
{
struct mml_dump_context *dctx = (struct mml_dump_context *)filp->private_data;
dctx->ready = false;
filp->private_data = NULL;
mml_log("[dumpsrv]%p released", filp);
return 0;
}
static const struct file_operations dumpsrv_fops = {
.open = dumpsrv_open,
.release = dumpsrv_release,
.read = dumpsrv_read,
.write = dumpsrv_write,
};
static const struct file_operations dumpsrv_dest_fops = {
.open = dumpsrv_dest_open,
.release = dumpsrv_release,
.read = dumpsrv_read,
.write = dumpsrv_write,
};
static int probe(struct platform_device *pdev)
{
struct mml_test *test;
struct dentry *dir;
bool exists = false;
u32 i;
mml_log("[test]mml-test %s begin", __func__);
test = devm_kzalloc(&pdev->dev, sizeof(*test), GFP_KERNEL);
if (!test)
return -ENOMEM;
test->pdev = pdev;
test->dev = &pdev->dev;
dir = debugfs_lookup("mml", NULL);
if (!dir) {
dir = debugfs_create_dir("mml", NULL);
if (IS_ERR(dir) && PTR_ERR(dir) != -EEXIST) {
mml_err("[test]debugfs_create_dir mml failed:%ld", PTR_ERR(dir));
return PTR_ERR(dir);
}
} else
exists = true;
test->fs = debugfs_create_file(
"mml-test", 0444, dir, test, &test_fops);
if (IS_ERR(test->fs)) {
mml_err("[test]debugfs_create_file mml-test failed:%ld",
PTR_ERR(test->fs));
return PTR_ERR(test->fs);
}
test->fs_inst = debugfs_create_file(
"mml-inst-dump", 0444, dir, test, &mml_inst_dump_fops);
if (IS_ERR(test->fs_inst))
mml_err("[test]debugfs_create_file mml-inst-dump failed:%ld",
PTR_ERR(test->fs_inst));
test->fs_frame_in = debugfs_create_file(
"mml-frame-dump-in", 0444, dir, test, &mml_frame_in_fops);
if (IS_ERR(test->fs_frame_in))
mml_err("[test]debugfs_create_file mml-frame-dump-in failed:%ld",
PTR_ERR(test->fs_frame_in));
test->fs_frame_out = debugfs_create_file(
"mml-frame-dump-out", 0444, dir, test, &mml_frame_out_fops);
if (IS_ERR(test->fs_frame_out))
mml_err("[test]debugfs_create_file mml-frame-dump-out failed:%ld",
PTR_ERR(test->fs_frame_out));
test->fs_dump = debugfs_create_file(
"mml-dumpsrv", 0444, dir, test, &dumpsrv_fops);
if (IS_ERR(test->fs_dump)) {
mml_err("debugfs_create_file mml-dumpsrv failed:%pe", test->fs_dump);
return PTR_ERR(test->fs_dump);
}
test->fs_dump_dest = debugfs_create_file(
"mml-dumpsrv-dest", 0444, dir, test, &dumpsrv_dest_fops);
if (IS_ERR(test->fs_dump_dest)) {
mml_err("debugfs_create_file mml-dumpsrv-dest failed:%pe", test->fs_dump_dest);
return PTR_ERR(test->fs_dump_dest);
}
if (exists)
dput(dir);
platform_set_drvdata(pdev, test);
mml_log("[test]debugfs_create_file mml-test success");
main_test = test;
for (i = 0; i < ARRAY_SIZE(dump_ctx); i++) {
mutex_init(&dump_ctx[i].job_mutex);
init_waitqueue_head(&dump_ctx[i].dump_queue);
}
return 0;
}
static int remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id test_of_ids[] = {
{
.compatible = "mediatek,mml-test",
},
{}
};
MODULE_DEVICE_TABLE(of, test_of_ids);
struct platform_driver mtk_mml_test_drv = {
.probe = probe,
.remove = remove,
.driver = {
.name = "mml_test",
.of_match_table = test_of_ids,
},
};