oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi_car_reverse/preview.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

406 lines
12 KiB
C

/*
* Fast car reverse image preview module
*
* Copyright (C) 2015-2018 AllwinnerTech, Inc.
*
* Contacts:
* Zeng.Yajian <zengyajian@allwinnertech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/jiffies.h>
#include <linux/sunxi_tr.h>
#include "include.h"
#include "car_reverse.h"
#include "../../../video/sunxi/disp2/disp/dev_disp.h"
#undef _TRANSFORM_DEBUG
#define BUFFER_CNT (4)
#define BUFFER_MASK (0x03)
#define AUXLAYER_WIDTH (480)
#define AUXLAYER_HEIGHT (854)
#define AUXLAYER_SIZE (AUXLAYER_WIDTH * AUXLAYER_HEIGHT * 4)
struct rect {
int x, y;
int w, h;
};
struct preview_private_data {
struct device *dev;
struct rect src;
struct rect frame;
struct rect screen;
struct disp_layer_config config[2];
int layer_cnt;
int format;
int rotation;
unsigned long tr_handler;
tr_info transform;
struct buffer_pool *disp_buffer;
struct buffer_node *fb[BUFFER_CNT];
#ifdef CONFIG_SUPPORT_AUXILIARY_LINE
struct buffer_pool *auxiliary_line;
struct buffer_node *auxlayer;
#endif
};
/* for auxiliary line */
#ifdef CONFIG_SUPPORT_AUXILIARY_LINE
extern int draw_auxiliary_line(void *base, int width, int height);
int init_auxiliary_line(void);
#endif
/* function from sunxi transform driver */
extern unsigned long sunxi_tr_request(void);
extern int sunxi_tr_release(unsigned long hdl);
extern int sunxi_tr_commit(unsigned long hdl, tr_info *info);
extern int sunxi_tr_set_timeout(unsigned long hdl, unsigned long timeout);
extern int sunxi_tr_query(unsigned long hdl);
/* function from display driver */
extern struct disp_manager *disp_get_layer_manager(u32 disp);
static struct preview_private_data preview;
int preview_output_start(struct preview_params *params)
{
int i;
struct rect perfect;
struct disp_manager *mgr = disp_get_layer_manager(0);
if (!mgr || !mgr->force_set_layer_config) {
logerror("preview init error\n");
return -1;
}
memset(&preview, 0, sizeof(preview));
preview.dev = params->dev;
preview.src.w = params->src_width;
preview.src.h = params->src_height;
preview.format = params->format;
preview.layer_cnt = 1;
if (mgr->device && mgr->device->get_resolution) {
mgr->device->get_resolution(mgr->device, &preview.screen.w, &preview.screen.h);
logdebug("screen size: %dx%d\n", preview.screen.w, preview.screen.h);
} else {
preview.screen.w = 400;
preview.screen.h = 400;
logerror("can't get screen size, use default: %dx%d\n",
preview.screen.w, preview.screen.h);
}
if (params->rotation) {
perfect.w = params->screen_height;
perfect.h = params->screen_width;
} else {
perfect.w = params->screen_width;
perfect.h = params->screen_height;
}
preview.frame.w = (perfect.w > preview.screen.w) ? preview.screen.w : perfect.w;
preview.frame.h = (perfect.h > preview.screen.h) ? preview.screen.h : perfect.h;
preview.frame.x = (preview.screen.w - preview.frame.w) / 2;
preview.frame.y = (preview.screen.h - preview.frame.h) / 2;
if (params->rotation) {
preview.rotation = params->rotation;
preview.tr_handler = sunxi_tr_request();
if (!preview.tr_handler) {
logerror("request transform channel failed\n");
return -1;
}
preview.disp_buffer = alloc_buffer_pool(preview.dev, BUFFER_CNT,
preview.src.w * preview.src.h * 2);
if (!preview.disp_buffer) {
sunxi_tr_release(preview.tr_handler);
logerror("request display buffer failed\n");
return -1;
}
for (i = 0; i < BUFFER_CNT; i++)
preview.fb[i] = preview.disp_buffer->dequeue_buffer(preview.disp_buffer);
}
#ifdef CONFIG_SUPPORT_AUXILIARY_LINE
init_auxiliary_line();
#endif
return 0;
}
#ifdef CONFIG_SUPPORT_AUXILIARY_LINE
int init_auxiliary_line(void)
{
struct disp_layer_config *config;
void *start;
void *end;
preview.auxiliary_line = alloc_buffer_pool(preview.dev, 1, AUXLAYER_SIZE);
if (!preview.auxiliary_line) {
logerror("request auxiliary line buffer failed\n");
return -1;
}
preview.auxlayer = preview.auxiliary_line->dequeue_buffer(preview.auxiliary_line);
if (!preview.auxlayer) {
logerror("no buffer in buffer pool\n");
return -1;
}
memset(preview.auxlayer->vir_address, 0, AUXLAYER_SIZE);
draw_auxiliary_line(preview.auxlayer->vir_address, AUXLAYER_WIDTH, AUXLAYER_HEIGHT);
start = preview.auxlayer->vir_address;
end = (void *)((unsigned long)start + preview.auxlayer->size);
dmac_flush_range(start, end);
config = &preview.config[1];
memset(config, 0, sizeof(struct disp_layer_config));
config->channel = 1;
config->enable = 1;
config->layer_id = 0;
config->info.fb.addr[0] = (unsigned int)preview.auxlayer->phy_address;
config->info.fb.format = DISP_FORMAT_ARGB_8888;
config->info.fb.size[0].width = AUXLAYER_WIDTH;
config->info.fb.size[0].height = AUXLAYER_HEIGHT;
config->info.fb.size[1].width = AUXLAYER_WIDTH;
config->info.fb.size[1].height = AUXLAYER_HEIGHT;
config->info.fb.size[2].width = AUXLAYER_WIDTH;
config->info.fb.size[2].height = AUXLAYER_HEIGHT;
config->info.mode = LAYER_MODE_BUFFER;
config->info.zorder = 1;
config->info.fb.crop.width = (unsigned long long)AUXLAYER_WIDTH << 32;
config->info.fb.crop.height = (unsigned long long)AUXLAYER_HEIGHT << 32;
config->info.alpha_mode = 0; /* pixel alpha */
config->info.alpha_value = 0;
config->info.screen_win.x = preview.frame.x;
config->info.screen_win.y = preview.frame.y;
config->info.screen_win.width = preview.frame.w;
config->info.screen_win.height = preview.frame.h;
preview.layer_cnt++;
return 0;
}
void deinit_auxiliary_line(void)
{
if (preview.auxlayer)
preview.auxiliary_line->queue_buffer(preview.auxiliary_line, preview.auxlayer);
free_buffer_pool(preview.dev, preview.auxiliary_line);
}
#endif
int preview_output_stop(void)
{
int i;
struct disp_manager *mgr = disp_get_layer_manager(0);
if (!mgr || !mgr->force_set_layer_config_exit) {
logerror("preview stop error\n");
return -1;
}
mgr->force_set_layer_config_exit(mgr);
msleep(100);
if (preview.tr_handler)
sunxi_tr_release(preview.tr_handler);
if (preview.disp_buffer) {
for (i = 0; i < BUFFER_CNT; i++) {
if (!preview.fb[i])
continue;
preview.disp_buffer->queue_buffer(preview.disp_buffer, preview.fb[i]);
}
free_buffer_pool(preview.dev, preview.disp_buffer);
}
#ifdef CONFIG_SUPPORT_AUXILIARY_LINE
deinit_auxiliary_line();
#endif
return 0;
}
int image_rotate(struct buffer_node *frame, struct buffer_node **rotate)
{
static int active;
int retval;
struct buffer_node *node;
tr_info *info = &preview.transform;
#ifdef _TRANSFORM_DEBUG
unsigned long start_jiffies;
unsigned long end_jiffies;
unsigned long time;
#endif
active++;
node = preview.fb[active & BUFFER_MASK];
if (!node || !frame) {
logerror("%s, alloc buffer failed\n", __func__);
return -1;
}
info->mode = TR_ROT_90;
info->src_frame.fmt = TR_FORMAT_YUV420_SP_UVUV;
info->src_frame.laddr[0] = (unsigned int)frame->phy_address;
info->src_frame.laddr[1] = (unsigned int)frame->phy_address + preview.src.w * preview.src.h;
info->src_frame.laddr[2] = 0;
info->src_frame.pitch[0] = preview.src.w;
info->src_frame.pitch[1] = preview.src.w / 2;
info->src_frame.pitch[2] = 0;
info->src_frame.height[0] = preview.src.h;
info->src_frame.height[1] = preview.src.h / 2;
info->src_frame.height[2] = 0;
info->src_rect.x = 0;
info->src_rect.y = 0;
info->src_rect.w = preview.src.w;
info->src_rect.h = preview.src.h;
info->dst_frame.fmt = TR_FORMAT_YUV420_P;
info->dst_frame.laddr[0] = (unsigned long)node->phy_address;
info->dst_frame.laddr[1] = (unsigned long)node->phy_address +
preview.src.w * preview.src.h;
info->dst_frame.laddr[2] = (unsigned long)node->phy_address +
preview.src.w * preview.src.h +
preview.src.w * preview.src.h / 4;
info->dst_frame.pitch[0] = preview.src.h;
info->dst_frame.pitch[1] = preview.src.h / 2;
info->dst_frame.pitch[2] = preview.src.h / 2;
info->dst_frame.height[0] = preview.src.w;
info->dst_frame.height[1] = preview.src.w / 2;
info->dst_frame.height[2] = preview.src.w / 2;
info->dst_rect.x = 0;
info->dst_rect.y = 0;
info->dst_rect.w = preview.src.h;
info->dst_rect.h = preview.src.w;
#ifdef _TRANSFORM_DEBUG
start_jiffies = jiffies;
#endif
if (sunxi_tr_commit(preview.tr_handler, info) < 0) {
logerror("transform commit error!\n");
return -1;
}
while (1) {
retval = sunxi_tr_query(preview.tr_handler);
if (retval == 0 || retval == -1)
break;
msleep(1);
}
*rotate = node;
#ifdef _TRANSFORM_DEBUG
end_jiffies = jiffies;
time = jiffies_to_msecs(end_jiffies - start_jiffies);
logerror("TR:%ld ms\n", time);
#endif
return retval;
}
void preview_update(struct buffer_node *frame)
{
struct disp_layer_config *config = &preview.config[0];
struct buffer_node *rotate = NULL;
int buffer_format = preview.format;
int width = preview.src.w;
int height = preview.src.h;
struct disp_manager *mgr = disp_get_layer_manager(0);
if (!mgr || !mgr->force_set_layer_config) {
logerror("preview update error\n");
return;
}
if (preview.rotation && image_rotate(frame, &rotate) == 0) {
if (!rotate) {
logerror("image rotate error\n");
return;
}
buffer_format = V4L2_PIX_FMT_YVU420;
width = preview.src.h;
height = preview.src.w;
}
switch (buffer_format) {
case V4L2_PIX_FMT_NV21:
config->info.fb.format = DISP_FORMAT_YUV420_SP_VUVU;
config->info.fb.addr[0] = (unsigned int)frame->phy_address;
config->info.fb.addr[1] = (unsigned int)frame->phy_address + (width * height);
config->info.fb.addr[2] = 0;
config->info.fb.size[0].width = width;
config->info.fb.size[0].height = height;
config->info.fb.size[1].width = width / 2;
config->info.fb.size[1].height = height / 2;
config->info.fb.size[2].width = 0;
config->info.fb.size[2].height = 0;
config->info.fb.align[1] = 0;
config->info.fb.align[2] = 0;
config->info.fb.crop.width = (unsigned long long)width << 32;
config->info.fb.crop.height = (unsigned long long)height << 32;
config->info.screen_win.x = preview.frame.x;
config->info.screen_win.y = preview.frame.y;
config->info.screen_win.width = preview.frame.w;
config->info.screen_win.height = preview.frame.h;
config->channel = 0;
config->layer_id = 0;
config->enable = 1;
config->info.mode = LAYER_MODE_BUFFER;
config->info.zorder = 12;
config->info.alpha_mode = 1;
config->info.alpha_value = 0xff;
break;
case V4L2_PIX_FMT_YVU420:
config->info.fb.format = DISP_FORMAT_YUV420_P;
config->info.fb.addr[0] = (unsigned int)rotate->phy_address;
config->info.fb.addr[1] = (unsigned int)rotate->phy_address + (width * height) + (width * height / 4);
config->info.fb.addr[2] = (unsigned int)rotate->phy_address + (width * height);
config->info.fb.size[0].width = width;
config->info.fb.size[0].height = height;
config->info.fb.size[1].width = width / 2;
config->info.fb.size[1].height = height / 2;
config->info.fb.size[2].width = width / 2;
config->info.fb.size[2].height = height / 2;
config->info.fb.align[1] = 0;
config->info.fb.align[2] = 0;
config->info.fb.crop.width = (unsigned long long)width << 32;
config->info.fb.crop.height = (unsigned long long)height << 32;
config->info.screen_win.x = preview.frame.x;
config->info.screen_win.y = preview.frame.y;
config->info.screen_win.width = preview.frame.w; /* FIXME */
config->info.screen_win.height = preview.frame.h;
config->channel = 0;
config->layer_id = 0;
config->enable = 1;
config->info.mode = LAYER_MODE_BUFFER;
config->info.zorder = 0;
config->info.alpha_mode = 1;
config->info.alpha_value = 0xff;
break;
default:
logerror("%s: unknown pixel format, skip\n", __func__);
break;
}
mgr->force_set_layer_config(mgr, &preview.config[0], preview.layer_cnt);
}