270 lines
7.0 KiB
C
270 lines
7.0 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2022 Rockchip Electronics Co., Ltd.
|
|
* Author: Sandy Huang <hjc@rock-chips.com>
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/module.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <drm/drm_atomic_uapi.h>
|
|
|
|
#include "rockchip_drm_drv.h"
|
|
#include "rockchip_drm_direct_show.h"
|
|
#include "rockchip_drm_display_pattern.h"
|
|
|
|
#include "kernel_logo_img.h"
|
|
|
|
#define USE_BUFFER_NUM 2
|
|
#define BUFFER_WIDTH 652
|
|
#define BUFFER_HEIGHT 268
|
|
#define BUFFER_FORMAT DRM_FORMAT_RGB565 /* DRM_FORMAT_RGB565/DRM_FORMAT_XRGB8888/DRM_FORMAT_NV12 */
|
|
|
|
struct rockchip_drm_self_test {
|
|
struct drm_device *dev;
|
|
struct work_struct commit_work;
|
|
struct workqueue_struct *workqueue;
|
|
|
|
struct drm_crtc *crtc;
|
|
struct drm_plane *plane;
|
|
|
|
struct rockchip_drm_direct_show_buffer *drm_buffer[USE_BUFFER_NUM];
|
|
};
|
|
|
|
static struct rockchip_drm_self_test rockchip_drm_st;
|
|
|
|
static void __maybe_unused
|
|
rockchip_drm_draw_white(struct rockchip_drm_direct_show_buffer *buffer)
|
|
{
|
|
if (buffer && buffer->vir_addr[0])
|
|
memset(buffer->vir_addr[0], 0xff, buffer->pitch[0] * buffer->height);
|
|
}
|
|
|
|
static void __maybe_unused
|
|
rockchip_drm_draw_gray128(struct rockchip_drm_direct_show_buffer *buffer)
|
|
{
|
|
if (buffer && buffer->vir_addr[0])
|
|
memset(buffer->vir_addr[0], 0x80, buffer->pitch[0] * buffer->height);
|
|
}
|
|
|
|
static void __maybe_unused
|
|
rockchip_drm_copy_bmp_file(struct rockchip_drm_direct_show_buffer *buffer)
|
|
{
|
|
int i = 0;
|
|
void *src, *dst;
|
|
|
|
if (!buffer || !buffer->vir_addr[0]) {
|
|
pr_info("%s[%d] buffer or buffer->vir_addr[0] is NULL\n", __func__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
src = (void *)bmp_file;
|
|
dst = (void *)buffer->vir_addr[0];
|
|
for (i = 0; i < buffer->height; i++) {
|
|
memcpy(dst, src, buffer->pitch[0]);
|
|
src += BUFFER_WIDTH * buffer->bpp >> 3;
|
|
dst += buffer->pitch[0];
|
|
}
|
|
}
|
|
|
|
static void __maybe_unused
|
|
rockchip_drm_draw_color_bar(struct rockchip_drm_direct_show_buffer *buffer)
|
|
{
|
|
if (buffer && buffer->vir_addr[0])
|
|
rockchip_drm_fill_color_bar(buffer->pixel_format,
|
|
buffer->vir_addr,
|
|
buffer->width,
|
|
buffer->height,
|
|
buffer->pitch[0]);
|
|
}
|
|
|
|
static int rockchip_drm_self_test_alloc_buffer(struct rockchip_drm_self_test *self_test)
|
|
{
|
|
int ret = 0, i = 0;
|
|
struct rockchip_drm_direct_show_buffer *buffer;
|
|
|
|
for (i = 0; i < USE_BUFFER_NUM; i++) {
|
|
buffer = kmalloc(sizeof(struct rockchip_drm_direct_show_buffer), GFP_KERNEL);
|
|
if (!buffer)
|
|
return -ENOMEM;
|
|
buffer->width = BUFFER_WIDTH;
|
|
buffer->height = BUFFER_HEIGHT;
|
|
buffer->pixel_format = BUFFER_FORMAT;
|
|
buffer->flag = ROCKCHIP_BO_CONTIG;
|
|
ret = rockchip_drm_direct_show_alloc_buffer(self_test->dev, buffer);
|
|
if (ret)
|
|
pr_info("failed to alloc drm buffer\n");
|
|
self_test->drm_buffer[i] = buffer;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_drm_self_test_free_buffer(struct rockchip_drm_self_test *self_test)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < USE_BUFFER_NUM; i++)
|
|
rockchip_drm_direct_show_free_buffer(self_test->dev, self_test->drm_buffer[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rockchip_drm_self_test_commit(struct work_struct *work)
|
|
{
|
|
struct rockchip_drm_self_test *self_test =
|
|
container_of(work, struct rockchip_drm_self_test, commit_work);
|
|
struct rockchip_drm_direct_show_commit_info commit_info;
|
|
int ret = 0;
|
|
|
|
if (!self_test->dev)
|
|
self_test->dev = rockchip_drm_get_dev();
|
|
|
|
/* drm is unready */
|
|
if (!self_test->dev) {
|
|
pr_info("%s[%d], drm is unready\n", __func__, __LINE__);
|
|
msleep(100);
|
|
queue_work(self_test->workqueue, &self_test->commit_work);
|
|
|
|
return;
|
|
}
|
|
|
|
/* alloc buffer */
|
|
if (!self_test->drm_buffer[0]) {
|
|
ret = rockchip_drm_self_test_alloc_buffer(self_test);
|
|
if (ret)
|
|
pr_info("error: drm self test alloc buffer error\n");
|
|
}
|
|
|
|
/* draw buffer */
|
|
rockchip_drm_copy_bmp_file(self_test->drm_buffer[0]);
|
|
/* rockchip_drm_draw_gray128(self_test->drm_buffer[1]); */
|
|
rockchip_drm_draw_color_bar(self_test->drm_buffer[1]);
|
|
|
|
/* get crtc and plane */
|
|
self_test->crtc = rockchip_drm_direct_show_get_crtc(self_test->dev, NULL);
|
|
if (self_test->crtc == NULL) {
|
|
pr_info("error: failed to get crtc\n");
|
|
goto free_buffer;
|
|
}
|
|
|
|
self_test->plane = rockchip_drm_direct_show_get_plane(self_test->dev, "Esmart0-win0");
|
|
if (self_test->plane == NULL) {
|
|
pr_info("error: failed to get plane\n");
|
|
goto free_buffer;
|
|
}
|
|
|
|
#if 1 /* for self test pattern */
|
|
/* commit to display */
|
|
do {
|
|
u32 i = 0;
|
|
|
|
commit_info.crtc = self_test->crtc;
|
|
commit_info.plane = self_test->plane;
|
|
|
|
commit_info.src_x = 0;
|
|
commit_info.src_y = 0;
|
|
commit_info.src_w = BUFFER_WIDTH;
|
|
commit_info.src_h = BUFFER_HEIGHT;
|
|
|
|
commit_info.dst_x = 0;
|
|
commit_info.dst_y = 0;
|
|
commit_info.dst_w = commit_info.src_w;
|
|
commit_info.dst_h = commit_info.src_h;
|
|
|
|
commit_info.top_zpos = true;
|
|
|
|
for (i = 0; i < 1000; i++) {
|
|
commit_info.buffer = self_test->drm_buffer[i % 2];/* two buffer ping pong */
|
|
rockchip_drm_direct_show_commit(self_test->dev, &commit_info);
|
|
mdelay(1000);
|
|
}
|
|
/* disable plane */
|
|
rockchip_drm_direct_show_disable_plane(self_test->dev, commit_info.plane);
|
|
/* free buffer */
|
|
rockchip_drm_self_test_free_buffer(self_test);
|
|
} while (0);
|
|
#else
|
|
/* for kernel logo display */
|
|
do {
|
|
int hdisplay = self_test->crtc->state->adjusted_mode.hdisplay;
|
|
int vdisplay = self_test->crtc->state->adjusted_mode.vdisplay;
|
|
|
|
commit_info.crtc = self_test->crtc;
|
|
commit_info.plane = self_test->plane;
|
|
|
|
commit_info.src_x = 0;
|
|
commit_info.src_y = 0;
|
|
commit_info.src_w = self_test->drm_buffer[0]->width;
|
|
commit_info.src_h = self_test->drm_buffer[0]->height;
|
|
|
|
if (1) {/* center display */
|
|
commit_info.dst_x = (hdisplay - BUFFER_WIDTH) / 2;
|
|
commit_info.dst_y = (vdisplay - BUFFER_HEIGHT) / 2;
|
|
commit_info.dst_w = commit_info.src_w;
|
|
commit_info.dst_h = commit_info.src_h;
|
|
|
|
} else {/* full screen display */
|
|
commit_info.dst_x = 0;
|
|
commit_info.dst_y = 0;
|
|
commit_info.dst_w = hdisplay;
|
|
commit_info.dst_h = vdisplay;
|
|
}
|
|
|
|
commit_info.buffer = self_test->drm_buffer[0];
|
|
rockchip_drm_direct_show_commit(self_test->dev, &commit_info);
|
|
} while (0);
|
|
#endif
|
|
return;
|
|
|
|
free_buffer:
|
|
/* free buffer */
|
|
rockchip_drm_self_test_free_buffer(self_test);
|
|
}
|
|
|
|
static int rockchip_drm_self_test_create_worker(struct rockchip_drm_self_test *slef_test)
|
|
{
|
|
struct workqueue_struct *wq = NULL;
|
|
|
|
wq = create_singlethread_workqueue("rockchip_drm_self_test");
|
|
if (!wq) {
|
|
pr_info("Failed to create rockchip_drm_self_test workqueue\n");
|
|
return -ENODEV;
|
|
}
|
|
slef_test->workqueue = wq;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused rockchip_drm_self_test_destory_worker(struct rockchip_drm_self_test *slef_test)
|
|
{
|
|
if (!slef_test)
|
|
return -ENODEV;
|
|
|
|
if (slef_test->workqueue)
|
|
destroy_workqueue(slef_test->workqueue);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_drm_self_test_main(void *arg)
|
|
{
|
|
rockchip_drm_self_test_create_worker(&rockchip_drm_st);
|
|
INIT_WORK(&rockchip_drm_st.commit_work, rockchip_drm_self_test_commit);
|
|
queue_work(rockchip_drm_st.workqueue, &rockchip_drm_st.commit_work);
|
|
|
|
return 0;
|
|
};
|
|
|
|
static int __init rockchip_drm_self_test(void)
|
|
{
|
|
kthread_run(rockchip_drm_self_test_main, NULL, "rockchip drm self test");
|
|
|
|
return 0;
|
|
}
|
|
|
|
subsys_initcall_sync(rockchip_drm_self_test);
|