Files
luban-lite/bsp/artinchip/drv/display/drv_fb_helper.c
刘可亮 3e10f578d3 v1.2.2
2025-10-21 13:59:50 +08:00

765 lines
20 KiB
C

/*
* Copyright (C) 2025, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: huahui.mai@artinchip.com
*
* ArtInChip framebuffer helper functions
*/
#include "drv_fb_helper.h"
void aicfb_enable_clk(struct aicfb_info *fbi, u32 on)
{
struct platform_driver *de = fbi->de;
struct platform_driver *di = fbi->di;
if (on == AICFB_ON)
{
de->de_funcs->clk_enable();
di->di_funcs->clk_enable();
}
else
{
di->di_funcs->clk_disable();
de->de_funcs->clk_disable();
}
}
void aicfb_update_alpha(struct aicfb_info *fbi)
{
struct platform_driver *de = fbi->de;
struct aicfb_alpha_config alpha = {0};
alpha.layer_id = AICFB_LAYER_TYPE_UI;
de->de_funcs->get_alpha_config(&alpha);
de->de_funcs->update_alpha_config(&alpha);
}
void aicfb_update_layer(struct aicfb_info *fbi)
{
struct platform_driver *de = fbi->de;
struct aicfb_layer_data layer = {0};
layer.layer_id = AICFB_LAYER_TYPE_UI;
layer.rect_id = 0;
de->de_funcs->get_layer_config(&layer);
layer.enable = 1;
switch (fbi->fb_rotate)
{
case 0:
layer.buf.size.width = fbi->width;
layer.buf.size.height = fbi->height;
layer.buf.stride[0] = fbi->stride;
layer.buf.phy_addr[0] = (uintptr_t)fbi->fb_start;
break;
#ifdef AIC_FB_ROTATE_EN
case 90:
case 270:
{
unsigned int stride = ALIGN_8B(fbi->height * fbi->bits_per_pixel / 8);
layer.buf.phy_addr[0] = (uintptr_t)fbi->fb_start + fbi->fb_size * AIC_FB_DRAW_BUF_NUM;
layer.buf.size.width = fbi->height;
layer.buf.size.height = fbi->width;
layer.buf.stride[0] = stride;
break;
}
case 180:
layer.buf.phy_addr[0] = (uintptr_t)fbi->fb_start + fbi->fb_size * AIC_FB_DRAW_BUF_NUM;
layer.buf.size.width = fbi->width;
layer.buf.size.height = fbi->height;
layer.buf.stride[0] = fbi->stride;
break;
#endif
default:
pr_err("Invalid rotation degree\n");
return;
}
layer.buf.crop_en = 0;
layer.buf.format = AICFB_FORMAT;
layer.buf.flags = 0;
de->de_funcs->update_layer_config(&layer);
}
static void aicfb_puts_panel_info(struct aicfb_info *fbi)
{
const char *com_type[] = { "DE", "RGB", "LVDS", "DSI", "DBI" };
struct display_timing *timing;
u32 vtotal, htotal;
timing = fbi->desc ? fbi->desc->timings : fbi->panel->timings;
vtotal = timing->vactive + timing->vback_porch + timing->vfront_porch + timing->vsync_len;
htotal = timing->hactive + timing->hback_porch + timing->hfront_porch + timing->hsync_len;
printf("%s type: %s pclk: %d Mhz h: %d v: %d fps: %d\n",
fbi->panel->name,
com_type[fbi->panel->connector_type],
timing->pixelclock / 1000000,
timing->hactive,
timing->vactive,
timing->pixelclock / (vtotal * htotal));
}
void aicfb_enable_panel(struct aicfb_info *fbi, u32 on)
{
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
struct aic_panel_funcs *funcs;
funcs = desc ? desc->funcs : panel->funcs;
if (on == AICFB_ON)
{
funcs->prepare();
funcs->enable(panel);
aicfb_puts_panel_info(fbi);
}
else
{
funcs->disable(panel);
funcs->unprepare();
}
}
void aicfb_get_panel_info(struct aicfb_info *fbi)
{
struct platform_driver *de = fbi->de;
struct platform_driver *di = fbi->di;
struct aic_panel *panel = fbi->panel;
u32 bpp = 24, dither_en = 1;
struct panel_desc *desc = NULL;
unsigned int id;
if (panel->desc && panel->match_num) {
id = panel->match_id;
desc = &panel->desc[id];
fbi->desc = desc;
}
if(di->di_funcs->attach_panel)
di->di_funcs->attach_panel(panel, desc);
if(di->di_funcs->get_output_bpp)
bpp = di->di_funcs->get_output_bpp();
#ifdef AIC_DISABLE_DITHER
dither_en = 0;
#endif
if(de->de_funcs->set_mode)
de->de_funcs->set_mode(panel, desc, bpp, dither_en);
}
static void aicfb_get_hv_active(struct aicfb_info *fbi, u32 *active_w, u32 *active_h)
{
#ifdef AIC_SCREEN_CROP
*active_w = AIC_SCREEN_CROP_WIDTH;
*active_h = AIC_SCREEN_CROP_HEIGHT;
#else
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
if (desc) {
*active_w = desc->timings->hactive;
*active_h = desc->timings->vactive;
} else {
*active_w = panel->timings->hactive;
*active_h = panel->timings->vactive;
}
#endif
}
static inline int aicfb_format_bpp(enum mpp_pixel_format format)
{
switch (format) {
case MPP_FMT_ARGB_8888:
case MPP_FMT_ABGR_8888:
case MPP_FMT_RGBA_8888:
case MPP_FMT_BGRA_8888:
case MPP_FMT_XRGB_8888:
case MPP_FMT_XBGR_8888:
case MPP_FMT_RGBX_8888:
case MPP_FMT_BGRX_8888:
return 32;
case MPP_FMT_RGB_888:
case MPP_FMT_BGR_888:
return 24;
case MPP_FMT_ARGB_1555:
case MPP_FMT_ABGR_1555:
case MPP_FMT_RGBA_5551:
case MPP_FMT_BGRA_5551:
case MPP_FMT_RGB_565:
case MPP_FMT_BGR_565:
case MPP_FMT_ARGB_4444:
case MPP_FMT_ABGR_4444:
case MPP_FMT_RGBA_4444:
case MPP_FMT_BGRA_4444:
return 16;
default:
break;
}
return 32;
}
void aicfb_fb_info_setup(struct aicfb_info *fbi)
{
u32 stride, bpp;
u32 active_w;
u32 active_h;
size_t fb_size;
bpp = aicfb_format_bpp(AICFB_FORMAT);
#ifdef AIC_FB_ROTATE_EN
fbi->fb_rotate = AIC_FB_ROTATE_DEGREE;
#else
fbi->fb_rotate = 0;
#endif
aicfb_get_hv_active(fbi, &active_w, &active_h);
if (fbi->fb_rotate == 90 || fbi->fb_rotate == 270)
{
u32 tmp = active_w;
active_w = active_h;
active_h = tmp;
}
stride = ALIGN_8B(active_w * bpp / 8);
fb_size = active_h * stride;
fbi->width = active_w;
fbi->height = active_h;
fbi->bits_per_pixel = bpp;
fbi->stride = stride;
fbi->fb_size = fb_size;
}
void aicfb_register_panel_callback(struct aicfb_info *fbi)
{
struct platform_driver *de = fbi->de;
struct platform_driver *di = fbi->di;
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
struct aic_panel_callbacks cb;
cb.di_enable = di->di_funcs->enable;
cb.di_disable = di->di_funcs->disable;
cb.di_send_cmd = di->di_funcs->send_cmd;
cb.di_set_videomode = di->di_funcs->set_videomode;
cb.timing_enable = de->de_funcs->timing_enable;
cb.timing_disable = de->de_funcs->timing_disable;
if (desc)
desc->funcs->register_callback(panel, &cb);
else
panel->funcs->register_callback(panel, &cb);
}
void fb_color_block(struct aicfb_info *fbi, size_t fb_size)
{
#ifdef AIC_DISP_COLOR_BLOCK
#ifdef AIC_FB_ROTATE_EN
if (!fb_size)
return;
memset(fbi->fb_start, 0x0, fb_size);
aicos_dcache_clean_range((unsigned long *)fbi->fb_start, fb_size);
#else
u32 width, height;
u32 i, j, index;
unsigned char color[5][3] = {
{ 0x00, 0x00, 0xFF },
{ 0x00, 0xFF, 0x00 },
{ 0xFF, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF },
};
unsigned char *pos = (unsigned char *)fbi->fb_start;
(void)fb_size;
width = fbi->width;
height = fbi->height;
switch (fbi->bits_per_pixel) {
#if defined(AICFB_ARGB8888) || defined(AICFB_ABGR8888) || defined(AICFB_XRGB8888)
case 32:
{
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
index = (i / 100 + j / 100) % 5;
*(pos++) = color[index][0];
*(pos++) = color[index][1];
*(pos++) = color[index][2];
*(pos++) = 0;
}
}
break;
}
#endif
#if defined(AICFB_RGB888)
case 24:
{
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
index = (i / 100 + j / 100) % 5;
*(pos++) = color[index][0];
*(pos++) = color[index][1];
*(pos++) = color[index][2];
}
}
break;
}
#endif
#if defined(AICFB_RGB565)
case 16:
{
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
index = (i / 100 + j / 100) % 5;
*(pos++) = (color[index][0] >> 3) | ((color[index][1] & 0x1c) << 3);
*(pos++) = ((color[index][1] & 0xe0) >> 5) | (color[index][2] & 0xf8);
}
}
break;
}
#endif
default:
*pos = color[0][0];
index = AICFB_FORMAT;
i = width;
j = height;
pr_info("format(%d) do not support %dx%d color block.\n", index, i, j);
return;
}
aicos_dcache_clean_range((unsigned long *)fbi->fb_start, fbi->fb_size);
#endif /* AIC_FB_ROTATE_EN */
#endif /* AIC_DISP_COLOR_BLOCK */
#ifdef AIC_DISP_BLACK_SCREEN
if (!fb_size)
return;
memset(fbi->fb_start, 0x0, fb_size);
aicos_dcache_clean_range((unsigned long *)fbi->fb_start, fb_size);
#endif
}
#ifdef AIC_DISPLAY_TEST
static void aicfb_reset(struct aicfb_info *fbi)
{
struct aicfb_layer_data layer = {0};
struct platform_driver *de = fbi->de;
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
aicfb_get_panel_info(fbi);
aicfb_fb_info_setup(fbi);
aicfb_enable_panel(fbi, AICFB_OFF);
layer.layer_id = AICFB_LAYER_TYPE_UI;
layer.rect_id = 0;
de->de_funcs->get_layer_config(&layer);
layer.enable = 0;
de->de_funcs->update_layer_config(&layer);
aicfb_enable_clk(fbi, AICFB_OFF);
aic_delay_ms(20);
aicfb_enable_clk(fbi, AICFB_ON);
layer.enable = 1;
layer.rect_id = 0;
layer.buf.size.width = desc ? desc->timings->hactive : panel->timings->hactive;
layer.buf.size.height = desc ? desc->timings->vactive : panel->timings->vactive;
layer.buf.stride[0] = fbi->stride;
de->de_funcs->update_layer_config(&layer);
aicfb_enable_panel(fbi, AICFB_ON);
}
static int handle_dbi_data(struct aic_panel *panel,
struct panel_desc *desc,
struct panel_dbi *dbi,
u32 *cmd_offset)
{
struct panel_dbi_commands *target = desc ?
&desc->dbi->commands :
&panel->dbi->commands;
if (!target->buf) {
target->buf = aicos_malloc(MEM_CMA, target->len);
if (!target->buf) {
pr_err("Malloc dbi buf failed!\n");
return -1;
}
}
const u32 total = dbi->commands.len + *cmd_offset;
if (total < target->len) {
memcpy((u8*)target->buf + *cmd_offset,
dbi->commands.buf, dbi->commands.len);
*cmd_offset += dbi->commands.len;
} else if (total == target->len) {
memcpy((u8*)target->buf + *cmd_offset,
dbi->commands.buf, dbi->commands.len);
*cmd_offset = 0;
} else {
pr_err("Commands len is too long!\n");
*cmd_offset = 0;
return -1;
}
return 0;
}
static int handle_dsi_data(struct aic_panel *panel,
struct panel_desc *desc,
struct panel_dsi *dsi,
u32 *cmd_offset)
{
struct dsi_command *target = desc ?
&desc->dsi->command :
&panel->dsi->command;
if (!target->buf) {
target->buf = aicos_malloc(MEM_CMA, 4096);
if (!target->buf) {
pr_err("Malloc dsi buf failed!\n");
return -1;
}
}
if ((*cmd_offset + dsi->command.len) > 4096) {
pr_err("Command buffer overflow!\n");
return -1;
}
memcpy(target->buf + *cmd_offset,
dsi->command.buf,
dsi->command.len);
*cmd_offset += dsi->command.len;
target->len = *cmd_offset;
target->command_on = 1;
return 0;
}
static int handle_spi_rgb_data(struct aic_panel *panel,
struct panel_desc *desc,
struct panel_rgb *rgb,
u32 *cmd_offset)
{
struct rgb_spi_command *target = desc ?
&desc->rgb->spi_command :
&panel->rgb->spi_command;
if (!target->buf) {
target->buf = aicos_malloc(MEM_CMA, 4096);
if(!target->buf) {
pr_err("Malloc rgb spi command buf failed\n");
return -1;
}
}
if ((*cmd_offset + rgb->spi_command.len) > 4096) {
pr_err("Command buffer overflow!\n");
return -1;
}
memcpy(target->buf + *cmd_offset,
rgb->spi_command.buf,
rgb->spi_command.len);
*cmd_offset += rgb->spi_command.len;
target->len = *cmd_offset;
target->command_on = 1;
return 0;
}
static bool handle_rgb_config(struct aicfb_info *fbi, struct aicfb_pq_config *config)
{
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
struct panel_rgb *rgb = config->data;
struct panel_rgb *target_rgb = desc ? desc->rgb : panel->rgb;
static u32 rgb_spi_command_offset = 0;
#define RGB_SPI_COMMAND_UPDATE 0
#define RGB_SPI_COMMAND_DISABLE 1
#define RGB_SPI_COMMAND_CLEAR 2
#define RGB_SPI_COMMAND_SEND 3
switch (rgb->spi_command.command_on)
{
case RGB_SPI_COMMAND_UPDATE:
{
if (handle_spi_rgb_data(panel, desc, rgb, &rgb_spi_command_offset) < 0) {
pr_err("handld spi-rgb command error!\n");
}
return true;
}
case RGB_SPI_COMMAND_DISABLE:
{
target_rgb->spi_command.command_on = 0;
return true;
}
case RGB_SPI_COMMAND_CLEAR:
{
target_rgb->spi_command.command_on = 2;
rgb_spi_command_offset = 0;
return true;
}
default:
{
target_rgb->mode = rgb->mode;
target_rgb->format = rgb->format;
target_rgb->data_order = rgb->data_order;
target_rgb->data_mirror = rgb->data_mirror;
target_rgb->clock_phase = rgb->clock_phase;
return false;
}
}
}
static bool handle_mipi_config(struct aicfb_info *fbi, struct aicfb_pq_config *config)
{
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
struct panel_dsi *dsi = config->data;
struct panel_dsi *target_dsi = desc ? desc->dsi : panel->dsi;
static u32 dsi_commands_offset = 0;
#define MIPI_DSI_DISABLE_COMMAND 0
#define MIPI_DSI_ADB_UPDATE_COMMAND 1
#define MIPI_DSI_SEND_COMMAND 2
#define MIPI_DSI_UART_UPDATE_COMMAND 3
#define MIPI_DSI_UART_COPY_COMMAND 4
switch (dsi->command.command_on)
{
case MIPI_DSI_ADB_UPDATE_COMMAND:
{
if (!target_dsi->command.buf)
target_dsi->command.buf = aicos_malloc(MEM_CMA, 4096);
target_dsi->command.command_on = 1;
target_dsi->command.len = dsi->command.len;
memcpy(target_dsi->command.buf, dsi->command.buf, dsi->command.len);
return true;
}
case MIPI_DSI_UART_COPY_COMMAND:
{
if (handle_dsi_data(panel, desc, dsi, &dsi_commands_offset) < 0) {
pr_err("handle dsi command error!\n");
}
memcpy(target_dsi->command.buf + dsi_commands_offset,
dsi->command.buf, dsi->command.len);
target_dsi->command.len = dsi_commands_offset;
target_dsi->command.command_on = 1;
return true;
}
case MIPI_DSI_UART_UPDATE_COMMAND:
{
target_dsi->command.command_on = 2;
dsi_commands_offset = 0;
return true;
}
case MIPI_DSI_DISABLE_COMMAND:
{
target_dsi->command.command_on = 0;
return true;
}
default:
{
target_dsi->mode = dsi->mode;
target_dsi->format = dsi->format;
target_dsi->lane_num = dsi->lane_num;
target_dsi->dc_inv = dsi->dc_inv;
target_dsi->vc_num = dsi->vc_num;
target_dsi->ln_polrs = dsi->ln_polrs;
target_dsi->ln_assign = dsi->ln_assign;
return false;
}
}
}
void aicfb_pq_set_config(struct aicfb_info *fbi, struct aicfb_pq_config *config)
{
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
switch (panel->connector_type)
{
case AIC_RGB_COM:
{
if (handle_rgb_config(fbi, config))
return;
break;
}
case AIC_LVDS_COM:
{
memcpy(desc ? desc->lvds : panel->lvds, config->data,
sizeof(struct panel_lvds));
break;
}
case AIC_MIPI_COM:
{
if (handle_mipi_config(fbi, config))
return;
break;
}
case AIC_DBI_COM:
{
static u32 dbi_commands_offset = 0;
struct panel_dbi *dbi = config->data;
struct panel_dbi *target_dbi = desc ? desc->dbi : panel->dbi;
#define MIPI_DBI_UPDATE_COMMAND 0
#define MIPI_DBI_SEND_COMMAND 1
if (dbi->commands.command_flag == MIPI_DBI_UPDATE_COMMAND) {
if (handle_dbi_data(panel, desc, dbi, &dbi_commands_offset) < 0) {
pr_err("handle dbi command error!\n");
return;
}
return;
} else {
target_dbi->type = dbi->type;
target_dbi->format = dbi->format;
target_dbi->first_line = dbi->first_line;
target_dbi->other_line = dbi->other_line;
if (target_dbi->spi != NULL) {
target_dbi->spi->qspi_mode = dbi->spi->qspi_mode;
target_dbi->spi->vbp_num = dbi->spi->vbp_num;
target_dbi->spi->code1_cfg = dbi->spi->code1_cfg;
target_dbi->spi->code[0] = dbi->spi->code[0];
target_dbi->spi->code[1] = dbi->spi->code[1];
target_dbi->spi->code[2] = dbi->spi->code[2];
}
}
break;
}
default:
break;
}
if (desc)
memcpy(desc->timings, config->timing, sizeof(struct display_timing));
else
memcpy(panel->timings, config->timing, sizeof(struct display_timing));
aicfb_reset(fbi);
}
void aicfb_pq_get_config(struct aicfb_info *fbi, struct aicfb_pq_config *config)
{
struct aic_panel *panel = fbi->panel;
struct panel_desc *desc = fbi->desc;
if (desc) {
memcpy(config->timing, desc->timings, sizeof(struct display_timing));
} else {
memcpy(config->timing, panel->timings, sizeof(struct display_timing));
}
if (config->connector_type != panel->connector_type)
return;
switch (panel->connector_type)
{
case AIC_RGB_COM:
{
if (desc) {
memcpy(config->data, desc->rgb, sizeof(struct panel_rgb));
} else {
memcpy(config->data, panel->rgb, sizeof(struct panel_rgb));
}
break;
}
case AIC_LVDS_COM:
{
if (desc) {
memcpy(config->data, desc->lvds, sizeof(struct panel_lvds));
} else {
memcpy(config->data, panel->lvds, sizeof(struct panel_lvds));
}
break;
}
case AIC_MIPI_COM:
{
struct panel_dsi *dsi = config->data;
struct panel_dsi *src_dsi = desc ? desc->dsi : panel->dsi;
dsi->mode = src_dsi->mode;
dsi->format = src_dsi->format;
dsi->lane_num = src_dsi->lane_num;
dsi->dc_inv = src_dsi->dc_inv;
dsi->vc_num = src_dsi->vc_num;
dsi->ln_polrs = src_dsi->ln_polrs;
dsi->ln_assign = src_dsi->ln_assign;
break;
}
case AIC_DBI_COM:
{
struct panel_dbi *dbi = config->data;
struct panel_dbi *src_dbi = desc ? desc->dbi : panel->dbi;
dbi->type = src_dbi->type;
dbi->format = src_dbi->format;
if (src_dbi->first_line) {
dbi->first_line = src_dbi->first_line;
} else {
dbi->first_line = 0x2C;
}
if (src_dbi->other_line) {
dbi->other_line = src_dbi->other_line;
} else {
dbi->other_line = 0x3C;
}
if (src_dbi->spi != NULL) {
dbi->spi->qspi_mode = src_dbi->spi->qspi_mode;
dbi->spi->vbp_num = src_dbi->spi->vbp_num;
dbi->spi->code1_cfg = src_dbi->spi->code1_cfg;
dbi->spi->code[0] = src_dbi->spi->code[0];
dbi->spi->code[1] = src_dbi->spi->code[1];
dbi->spi->code[2] = src_dbi->spi->code[2];
}
break;
}
default:
pr_err("AIPQ get config failed!\n");
break;
}
}
#endif /* AIC_DISPLAY_TEST */