Files
luban-lite-t3e-pro/bsp/artinchip/drv/display/drv_de.c
刘可亮 7bbc029dae v1.0.0
2023-08-30 16:21:18 +08:00

1334 lines
36 KiB
C

/*
* Copyright (c) 2023, Artinchip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <aic_core.h>
#include <aic_hal.h>
#include <aic_hal_de.h>
#include "disp_com.h"
#define MAX_LAYER_NUM 2
#define MAX_RECT_NUM 4
#define RECT_NUM_SHIFT 2
#define CONFIG_NUM (MAX_LAYER_NUM * MAX_RECT_NUM)
#define DITHER_RGB565 0x1
#define DITHER_RGB666 0x2
#define VSYNC_EVENT 1
#define DE_TIMEOUT 100
static unsigned int event_flag = 0;
struct aic_de_dither {
unsigned int enable;
unsigned int red_bitdepth;
unsigned int gleen_bitdepth;
unsigned int blue_bitdepth;
};
struct aic_de_configs {
const struct aicfb_layer_num *layer_num;
const struct aicfb_layer_capability *cap;
};
struct aic_de_comp {
void *regs;
const struct aic_de_configs *config;
struct aic_de_dither dither;
struct aicfb_layer_data layers[CONFIG_NUM];
struct aicfb_alpha_config alpha[MAX_LAYER_NUM];
struct aicfb_ck_config ck[MAX_LAYER_NUM];
struct aicfb_disp_prop disp_prop;
const struct display_timing *timing;
aicos_event_t vsync_event;
};
static struct aic_de_comp *g_aic_de_comp;
static struct aic_de_comp *aic_de_request_drvdata(void)
{
return g_aic_de_comp;
}
static void aic_de_release_drvdata(void)
{
}
static irqreturn_t aic_de_handler(int irq, void *ctx)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
u32 status;
status = de_timing_interrupt_status(comp->regs);
de_timing_interrupt_clean_status(comp->regs, status);
if (status & TIMING_INIT_V_BLANK_FLAG) {
if (event_flag) {
event_flag = 0;
aicos_event_send(comp->vsync_event, VSYNC_EVENT);
}
}
if (status & TIMING_INIT_UNDERFLOW_FLAG)
pr_err("ERROR: DE UNDERFLOW\n");
return IRQ_HANDLED;
}
static inline bool is_valid_layer_id(struct aic_de_comp *comp, u32 layer_id)
{
u32 total_num = comp->config->layer_num->vi_num
+ comp->config->layer_num->ui_num;
if (layer_id < total_num)
return true;
else
return false;
}
static inline bool need_update_disp_prop(struct aic_de_comp *comp,
struct aicfb_disp_prop *disp_prop)
{
if (disp_prop->bright != comp->disp_prop.bright ||
disp_prop->contrast != comp->disp_prop.contrast ||
disp_prop->saturation != comp->disp_prop.saturation ||
disp_prop->hue != comp->disp_prop.hue)
return true;
return false;
}
static inline bool need_use_hsbc(struct aic_de_comp *comp,
struct aicfb_disp_prop *disp_prop)
{
if (disp_prop->bright != 50 ||
disp_prop->contrast != 50 ||
disp_prop->saturation != 50 ||
disp_prop->hue != 50)
return true;
return false;
}
static inline bool need_update_csc(struct aic_de_comp *comp, int color_space)
{
/* get color space from video layer config */
struct aicfb_layer_data *layer_data = &comp->layers[0];
if (color_space != MPP_BUF_COLOR_SPACE_GET(layer_data->buf.flags))
return true;
return false;
}
static inline bool is_ui_layer(struct aic_de_comp *comp, u32 layer_id)
{
if (comp->config->cap[layer_id].layer_type == AICFB_LAYER_TYPE_UI)
return true;
else
return false;
}
static inline bool is_valid_layer_and_rect_id(struct aic_de_comp *comp,
u32 layer_id, u32 rect_id)
{
u32 flags;
if (!is_valid_layer_id(comp, layer_id))
return false;
flags = comp->config->cap[layer_id].cap_flags;
if (flags & AICFB_CAP_4_RECT_WIN_FLAG) {
if (layer_id != 0 && rect_id >= MAX_RECT_NUM)
return false;
}
return true;
}
static inline bool is_support_color_key(struct aic_de_comp *comp,
u32 layer_id)
{
u32 flags;
if (!is_valid_layer_id(comp, layer_id))
return false;
flags = comp->config->cap[layer_id].cap_flags;
if (flags & AICFB_CAP_CK_FLAG)
return true;
else
return false;
}
static inline bool is_support_alpha_blending(struct aic_de_comp *comp,
u32 layer_id)
{
u32 flags;
if (!is_valid_layer_id(comp, layer_id))
return false;
flags = comp->config->cap[layer_id].cap_flags;
if (flags & AICFB_CAP_ALPHA_FLAG)
return true;
else
return false;
}
static int aic_de_set_mode(struct aic_panel *panel)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
comp->timing = panel->timings;
return 0;
}
static int aic_de_clk_enable(void)
{
hal_clk_enable(CLK_DE);
hal_clk_enable(CLK_PIX);
hal_clk_set_freq(CLK_DE, DE_FREQ);
hal_reset_deassert(RESET_DE);
return 0;
}
static int aic_de_clk_disable(void)
{
hal_reset_assert(RESET_DE);
hal_clk_disable(CLK_DE);
hal_clk_disable(CLK_PIX);
return 0;
}
static int aic_de_timing_enable(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
u32 active_w = comp->timing->hactive;
u32 active_h = comp->timing->vactive;
u32 hfp = comp->timing->hfront_porch;
u32 hbp = comp->timing->hback_porch;
u32 vfp = comp->timing->vfront_porch;
u32 vbp = comp->timing->vback_porch;
u32 hsync = comp->timing->hsync_len;
u32 vsync = comp->timing->vsync_len;
de_config_timing(comp->regs, active_w, active_h, hfp, hbp,
vfp, vbp, hsync, vsync);
de_set_blending_size(comp->regs, active_w, active_h);
de_set_ui_layer_size(comp->regs, active_w, active_h, 0, 0);
comp->alpha[1].layer_id = AICFB_LAYER_TYPE_UI;
comp->alpha[1].enable = 1;
comp->alpha[1].mode = 0;
comp->alpha[1].value = 0xff;
de_config_prefetch_line_set(comp->regs, 2);
de_soft_reset_ctrl(comp->regs, 1);
de_set_qos(comp->regs);
if (comp->dither.enable)
de_set_dither(comp->regs,
comp->dither.red_bitdepth,
comp->dither.gleen_bitdepth,
comp->dither.blue_bitdepth,
comp->dither.enable);
de_ui_alpha_blending_enable(comp->regs, comp->alpha[1].value,
comp->alpha[1].mode,
comp->alpha[1].enable);
aicos_request_irq(DE_IRQn, aic_de_handler, 0, "aic-de", NULL);
#ifndef AIC_BOOTLOADER_CMD_PROGRESS_BAR
de_timing_enable_interrupt(comp->regs, true, TIMING_INIT_V_BLANK_FLAG);
de_timing_enable_interrupt(comp->regs, true, TIMING_INIT_UNDERFLOW_FLAG);
#endif
de_timing_enable(comp->regs, 1);
aic_de_release_drvdata();
return 0;
}
static int aic_de_timing_disable(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
de_timing_enable(comp->regs, 0);
aic_de_release_drvdata();
return 0;
}
static int aic_de_wait_for_vsync(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
uint32_t recved;
int ret;
event_flag = 1;
ret = aicos_event_recv(comp->vsync_event,
VSYNC_EVENT,
&recved,
DE_TIMEOUT);
if (ret < 0) {
hal_log_err("DE wait vsync irq failed\n");
return ret;
}
return 0;
}
static int aic_de_get_alpha_config(struct aicfb_alpha_config *alpha)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
if (is_support_alpha_blending(comp, alpha->layer_id) == false) {
pr_err("layer %d doesn't support alpha blending\n",
alpha->layer_id);
aic_de_release_drvdata();
return -EINVAL;
}
comp->alpha[alpha->layer_id].layer_id = alpha->layer_id;
memcpy(alpha, &comp->alpha[alpha->layer_id],
sizeof(struct aicfb_alpha_config));
aic_de_release_drvdata();
return 0;
}
static int aic_de_update_alpha_config(struct aicfb_alpha_config *alpha)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
if (is_support_alpha_blending(comp, alpha->layer_id) == false) {
pr_err("layer %d doesn't support alpha blending\n",
alpha->layer_id);
aic_de_release_drvdata();
return -EINVAL;
}
de_config_update_enable(comp->regs, 0);
de_ui_alpha_blending_enable(comp->regs,
alpha->value, alpha->mode, alpha->enable);
de_config_update_enable(comp->regs, 1);
memcpy(&comp->alpha[alpha->layer_id],
alpha, sizeof(struct aicfb_alpha_config));
aic_de_release_drvdata();
return 0;
}
static int aic_de_get_ck_config(struct aicfb_ck_config *ck)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
if (is_support_color_key(comp, ck->layer_id) == false) {
pr_err("Layer %d doesn't support color key blending\n", ck->layer_id);
aic_de_release_drvdata();
return -EINVAL;
}
comp->ck[ck->layer_id].layer_id = ck->layer_id;
memcpy(ck, &comp->ck[ck->layer_id], sizeof(struct aicfb_ck_config));
aic_de_release_drvdata();
return 0;
}
static int aic_de_update_ck_config(struct aicfb_ck_config *ck)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
if (is_support_color_key(comp, ck->layer_id) == false) {
pr_err("Layer %d doesn't support color key blending\n", ck->layer_id);
aic_de_release_drvdata();
return -EINVAL;
}
if (ck->enable > 1) {
pr_err("Invalid ck enable: %d\n", ck->enable);
aic_de_release_drvdata();
return -EINVAL;
}
de_config_update_enable(comp->regs, 0);
de_ui_layer_color_key_enable(comp->regs, ck->value, ck->enable);
de_config_update_enable(comp->regs, 1);
memcpy(&comp->ck[ck->layer_id], ck, sizeof(struct aicfb_ck_config));
aic_de_release_drvdata();
return 0;
}
static int aic_de_get_layer_config(struct aicfb_layer_data *layer_data)
{
u32 index;
struct aic_de_comp *comp = aic_de_request_drvdata();
if (is_valid_layer_and_rect_id(comp, layer_data->layer_id,
layer_data->rect_id) == false) {
pr_err("invalid layer_id %d or rect_id %d\n",
layer_data->layer_id, layer_data->rect_id);
aic_de_release_drvdata();
return -EINVAL;
}
index = (layer_data->layer_id << RECT_NUM_SHIFT) + layer_data->rect_id;
comp->layers[index].layer_id = layer_data->layer_id;
comp->layers[index].rect_id = layer_data->rect_id;
memcpy(layer_data, &comp->layers[index],
sizeof(struct aicfb_layer_data));
aic_de_release_drvdata();
return 0;
}
static bool is_valid_video_size(struct aic_de_comp *comp,
struct aicfb_layer_data *layer_data)
{
u32 src_width;
u32 src_height;
u32 x_offset;
u32 y_offset;
u32 active_w;
u32 active_h;
src_width = layer_data->buf.size.width;
src_height = layer_data->buf.size.height;
x_offset = layer_data->pos.x;
y_offset = layer_data->pos.y;
active_w = comp->timing->hactive;
active_h = comp->timing->vactive;
if (x_offset >= active_w || y_offset >= active_h) {
pr_err("video layer x or y offset is invalid\n");
return false;
}
if (layer_data->buf.crop_en) {
u32 crop_x = layer_data->buf.crop.x;
u32 crop_y = layer_data->buf.crop.y;
if (crop_x >= src_width || crop_y >= src_height) {
pr_err("video layer crop is invalid\n");
return false;
}
if (crop_x + layer_data->buf.crop.width > src_width)
layer_data->buf.crop.width = src_width - crop_x;
if (crop_y + layer_data->buf.crop.height > src_height)
layer_data->buf.crop.height = src_height - crop_y;
}
if (x_offset + layer_data->scale_size.width > active_w)
layer_data->scale_size.width = active_w - x_offset;
if (y_offset + layer_data->scale_size.height > active_h)
layer_data->scale_size.height = active_h - y_offset;
return true;
}
static bool is_ui_rect_win_overlap(struct aic_de_comp *comp,
u32 layer_id, u32 rect_id,
u32 x, u32 y, u32 w, u32 h)
{
int i;
u32 index;
u32 cur_x, cur_y;
u32 cur_w, cur_h;
index = (layer_id << RECT_NUM_SHIFT);
for (i = 0; i < MAX_RECT_NUM; i++) {
if (rect_id == i) {
index++;
continue;
}
if (comp->layers[index].enable) {
if (comp->layers[index].buf.crop_en) {
cur_w = comp->layers[index].buf.crop.width;
cur_h = comp->layers[index].buf.crop.height;
} else {
cur_w = comp->layers[index].buf.size.width;
cur_h = comp->layers[index].buf.size.height;
}
cur_x = comp->layers[index].pos.x;
cur_y = comp->layers[index].pos.y;
if ((min(y + h, cur_y + cur_h) > max(y, cur_y)) &&
(min(x + w, cur_x + cur_w) > max(x, cur_x))) {
return true;
}
}
index++;
}
return false;
}
static bool is_valid_ui_rect_size(struct aic_de_comp *comp,
struct aicfb_layer_data *layer_data)
{
u32 src_width;
u32 src_height;
u32 x_offset;
u32 y_offset;
u32 active_w;
u32 active_h;
u32 w;
u32 h;
src_width = layer_data->buf.size.width;
src_height = layer_data->buf.size.height;
x_offset = layer_data->pos.x;
y_offset = layer_data->pos.y;
active_w = comp->timing->hactive;
active_h = comp->timing->vactive;
if (x_offset >= active_w || y_offset >= active_h) {
pr_err("ui layer x or y offset is invalid\n");
return false;
}
if (layer_data->buf.crop_en) {
u32 crop_x = layer_data->buf.crop.x;
u32 crop_y = layer_data->buf.crop.y;
if (crop_x >= src_width || crop_y >= src_height) {
pr_err("ui layer crop is invalid\n");
return false;
}
if ((crop_x + layer_data->buf.crop.width) > src_width)
layer_data->buf.crop.width = src_width - crop_x;
if ((crop_y + layer_data->buf.crop.height) > src_height)
layer_data->buf.crop.height = src_height - crop_y;
if ((x_offset + layer_data->buf.crop.width) > active_w)
layer_data->buf.crop.width = active_w - x_offset;
if ((y_offset + layer_data->buf.crop.height) > active_h)
layer_data->buf.crop.height = active_h - y_offset;
w = layer_data->buf.crop.width;
h = layer_data->buf.crop.height;
} else {
if ((x_offset + src_width) > active_w)
layer_data->buf.size.width = active_w - x_offset;
if ((y_offset + src_height) > active_h)
layer_data->buf.size.height = active_h - y_offset;
w = layer_data->buf.size.width;
h = layer_data->buf.size.height;
}
/* check overlap */
if (is_ui_rect_win_overlap(comp, layer_data->layer_id,
layer_data->rect_id,
x_offset, y_offset, w, h)) {
pr_err("ui rect is overlap\n");
return false;
}
return true;
}
static inline bool is_all_rect_win_disabled(struct aic_de_comp *comp,
u32 layer_id)
{
int i;
u32 index;
index = (layer_id << RECT_NUM_SHIFT);
for (i = 0; i < MAX_RECT_NUM; i++) {
if (comp->layers[index].enable)
return false;
index++;
}
return true;
}
static int config_ui_layer_rect(struct aic_de_comp *comp,
struct aicfb_layer_data *layer_data)
{
enum mpp_pixel_format format = layer_data->buf.format;
u32 in_w = (u32)layer_data->buf.size.width;
u32 in_h = (u32)layer_data->buf.size.height;
u32 stride0 = layer_data->buf.stride[0];
u32 addr0 = layer_data->buf.phy_addr[0];
u32 x_offset = layer_data->pos.x;
u32 y_offset = layer_data->pos.y;
u32 crop_en = layer_data->buf.crop_en;
u32 crop_x = layer_data->buf.crop.x;
u32 crop_y = layer_data->buf.crop.y;
u32 crop_w = layer_data->buf.crop.width;
u32 crop_h = layer_data->buf.crop.height;
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:
if (crop_en) {
addr0 += crop_x * 4 + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
case MPP_FMT_RGB_888:
case MPP_FMT_BGR_888:
if (crop_en) {
addr0 += crop_x * 3 + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
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:
if (crop_en) {
addr0 += crop_x * 2 + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
default:
pr_err("invalid ui layer format: %d\n",
format);
return -EINVAL;
}
if (is_all_rect_win_disabled(comp, layer_data->layer_id)) {
de_set_ui_layer_format(comp->regs, format);
de_ui_layer_enable(comp->regs, 1);
}
de_ui_layer_set_rect(comp->regs, in_w, in_h, x_offset, y_offset,
stride0, addr0, layer_data->rect_id);
de_ui_layer_rect_enable(comp->regs, layer_data->rect_id, 1);
return 0;
}
static int de_update_csc(struct aic_de_comp *comp,
struct aicfb_disp_prop *disp_prop,
int color_space)
{
if (need_use_hsbc(comp, disp_prop)) {
int bright = (int)disp_prop->bright;
int contrast = (int)disp_prop->contrast;
int saturation = (int)disp_prop->saturation;
int hue = (int)disp_prop->hue;
de_set_hsbc_with_csc_coefs(comp->regs, color_space,
bright, contrast,
saturation, hue);
} else {
de_set_csc0_coefs(comp->regs, color_space);
}
return 0;
}
static int config_video_layer(struct aic_de_comp *comp,
struct aicfb_layer_data *layer_data)
{
enum mpp_pixel_format format = layer_data->buf.format;
u32 in_w = (u32)layer_data->buf.size.width;
u32 in_h = (u32)layer_data->buf.size.height;
u32 in_w_ch1;
u32 in_h_ch1;
u32 stride0 = layer_data->buf.stride[0];
u32 stride1 = layer_data->buf.stride[1];
u32 addr0, addr1, addr2;
u32 x_offset = layer_data->pos.x;
u32 y_offset = layer_data->pos.y;
u32 crop_en = layer_data->buf.crop_en;
u32 crop_x = layer_data->buf.crop.x;
u32 crop_y = layer_data->buf.crop.y;
u32 crop_w = layer_data->buf.crop.width;
u32 crop_h = layer_data->buf.crop.height;
u32 tile_p0_x_offset = 0;
u32 tile_p0_y_offset = 0;
u32 tile_p1_x_offset = 0;
u32 tile_p1_y_offset = 0;
u32 channel_num = 1;
u32 scaler_en = 0;
u32 scaler_w = layer_data->scale_size.width;
u32 scaler_h = layer_data->scale_size.height;
int color_space = MPP_BUF_COLOR_SPACE_GET(layer_data->buf.flags);
if (!scaler_w) {
scaler_w = in_w;
layer_data->scale_size.width = in_w;
}
if (!scaler_h) {
scaler_h = in_h;
layer_data->scale_size.height = in_h;
}
switch (layer_data->buf.buf_type) {
case MPP_PHY_ADDR:
addr0 = layer_data->buf.phy_addr[0];
addr1 = layer_data->buf.phy_addr[1];
addr2 = layer_data->buf.phy_addr[2];
break;
default:
pr_err("invalid buf type: %d\n",
layer_data->buf.buf_type);
return -EINVAL;
};
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:
if (crop_en) {
addr0 += crop_x * 4 + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
case MPP_FMT_RGB_888:
case MPP_FMT_BGR_888:
if (crop_en) {
addr0 += crop_x * 3 + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
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:
if (crop_en) {
addr0 += crop_x * 2 + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
case MPP_FMT_YUV420P:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
in_h = ALIGN_EVEN(in_h);
crop_x = ALIGN_EVEN(crop_x);
crop_y = ALIGN_EVEN(crop_y);
crop_w = ALIGN_EVEN(crop_w);
crop_h = ALIGN_EVEN(crop_h);
if (crop_en) {
u32 ch1_offset;
addr0 += crop_x + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
ch1_offset = (crop_x >> 1)
+ (crop_y >> 1) * stride1;
addr1 += ch1_offset;
addr2 += ch1_offset;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h >> 1;
break;
case MPP_FMT_NV12:
case MPP_FMT_NV21:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
in_h = ALIGN_EVEN(in_h);
crop_x = ALIGN_EVEN(crop_x);
crop_y = ALIGN_EVEN(crop_y);
crop_w = ALIGN_EVEN(crop_w);
crop_h = ALIGN_EVEN(crop_h);
if (crop_en) {
addr0 += crop_x + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
addr1 += crop_x + (crop_y >> 1) * stride1;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h >> 1;
break;
case MPP_FMT_YUV420_64x32_TILE:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
in_h = ALIGN_EVEN(in_h);
crop_x = ALIGN_EVEN(crop_x);
crop_y = ALIGN_EVEN(crop_y);
crop_w = ALIGN_EVEN(crop_w);
crop_h = ALIGN_EVEN(crop_h);
if (crop_en) {
u32 tile_p0_x, tile_p0_y;
u32 tile_p1_x, tile_p1_y;
u32 offset_p0, offset_p1;
tile_p0_x = crop_x >> 6;
tile_p0_x_offset = crop_x & 63;
tile_p0_y = crop_y >> 5;
tile_p0_y_offset = crop_y & 31;
tile_p1_x = crop_x >> 6;
tile_p1_x_offset = crop_x & 63;
tile_p1_y = (crop_y >> 1) >> 5;
tile_p1_y_offset = (crop_y >> 1) & 31;
offset_p0 = ALIGN_64B(stride0) * 32 * tile_p0_y
+ 64 * 32 * tile_p0_x;
offset_p1 = ALIGN_64B(stride1) * 32 * tile_p1_y
+ 64 * 32 * tile_p1_x;
addr0 += offset_p0;
addr1 += offset_p1;
in_w = crop_w;
in_h = crop_h;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h >> 1;
break;
case MPP_FMT_YUV420_128x16_TILE:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
in_h = ALIGN_EVEN(in_h);
crop_x = ALIGN_EVEN(crop_x);
crop_y = ALIGN_EVEN(crop_y);
crop_w = ALIGN_EVEN(crop_w);
crop_h = ALIGN_EVEN(crop_h);
if (crop_en) {
u32 tile_p0_x, tile_p0_y;
u32 tile_p1_x, tile_p1_y;
u32 offset_p0, offset_p1;
tile_p0_x = crop_x >> 7;
tile_p0_x_offset = crop_x & 127;
tile_p0_y = crop_y >> 4;
tile_p0_y_offset = crop_y & 15;
tile_p1_x = crop_x >> 7;
tile_p1_x_offset = crop_x & 127;
tile_p1_y = (crop_y >> 1) >> 4;
tile_p1_y_offset = (crop_y >> 1) & 15;
offset_p0 = ALIGN_128B(stride0) * 16 * tile_p0_y
+ 128 * 16 * tile_p0_x;
offset_p1 = ALIGN_128B(stride1) * 16 * tile_p1_y
+ 128 * 16 * tile_p1_x;
addr0 += offset_p0;
addr1 += offset_p1;
in_w = crop_w;
in_h = crop_h;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h >> 1;
break;
case MPP_FMT_YUV400:
channel_num = 1;
scaler_en = 1;
if (crop_en) {
addr0 += crop_x + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
break;
case MPP_FMT_YUV422P:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
crop_x = ALIGN_EVEN(crop_x);
crop_w = ALIGN_EVEN(crop_w);
if (crop_en) {
u32 ch1_offset;
addr0 += crop_x + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
ch1_offset = (crop_x >> 1) + crop_y * stride1;
addr1 += ch1_offset;
addr2 += ch1_offset;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h;
break;
case MPP_FMT_NV16:
case MPP_FMT_NV61:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
crop_x = ALIGN_EVEN(crop_x);
crop_w = ALIGN_EVEN(crop_w);
if (crop_en) {
u32 ch1_offset;
addr0 += crop_x + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
ch1_offset = crop_x + crop_y * stride1;
addr1 += ch1_offset;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h;
break;
case MPP_FMT_YUYV:
case MPP_FMT_YVYU:
case MPP_FMT_UYVY:
case MPP_FMT_VYUY:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
crop_x = ALIGN_EVEN(crop_x);
crop_w = ALIGN_EVEN(crop_w);
if (crop_en) {
addr0 += (crop_x << 1) + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h;
break;
case MPP_FMT_YUV422_64x32_TILE:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
crop_x = ALIGN_EVEN(crop_x);
crop_w = ALIGN_EVEN(crop_w);
if (crop_en) {
u32 tile_p0_x, tile_p0_y;
u32 tile_p1_x, tile_p1_y;
u32 offset_p0, offset_p1;
tile_p0_x = crop_x >> 6;
tile_p0_x_offset = crop_x & 63;
tile_p0_y = crop_y >> 5;
tile_p0_y_offset = crop_y & 31;
tile_p1_x = crop_x >> 6;
tile_p1_x_offset = crop_x & 63;
tile_p1_y = crop_y >> 5;
tile_p1_y_offset = crop_y & 31;
offset_p0 = ALIGN_64B(stride0) * 32 * tile_p0_y
+ 64 * 32 * tile_p0_x;
offset_p1 = ALIGN_64B(stride1) * 32 * tile_p1_y
+ 64 * 32 * tile_p1_x;
addr0 += offset_p0;
addr1 += offset_p1;
in_w = crop_w;
in_h = crop_h;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h;
break;
case MPP_FMT_YUV422_128x16_TILE:
channel_num = 2;
scaler_en = 1;
in_w = ALIGN_EVEN(in_w);
crop_x = ALIGN_EVEN(crop_x);
crop_w = ALIGN_EVEN(crop_w);
if (crop_en) {
u32 tile_p0_x, tile_p0_y;
u32 tile_p1_x, tile_p1_y;
u32 offset_p0, offset_p1;
tile_p0_x = crop_x >> 7;
tile_p0_x_offset = crop_x & 127;
tile_p0_y = crop_y >> 4;
tile_p0_y_offset = crop_y & 15;
tile_p1_x = crop_x >> 7;
tile_p1_x_offset = crop_x & 127;
tile_p1_y = crop_y >> 4;
tile_p1_y_offset = crop_y & 15;
offset_p0 = ALIGN_128B(stride0) * 16 * tile_p0_y
+ 128 * 16 * tile_p0_x;
offset_p1 = ALIGN_128B(stride1) * 16 * tile_p1_y
+ 128 * 16 * tile_p1_x;
addr0 += offset_p0;
addr1 += offset_p1;
in_w = crop_w;
in_h = crop_h;
}
in_w_ch1 = in_w >> 1;
in_h_ch1 = in_h;
break;
case MPP_FMT_YUV444P:
channel_num = 2;
scaler_en = 0;
if (crop_en) {
u32 ch1_offset;
addr0 += crop_x + crop_y * stride0;
in_w = crop_w;
in_h = crop_h;
ch1_offset = crop_x + crop_y * stride1;
addr1 += ch1_offset;
addr2 += ch1_offset;
}
break;
default:
pr_err("invalid video layer format: %d\n", format);
return -EINVAL;
}
de_set_video_layer_info(comp->regs, in_w, in_h, format,
stride0, stride1, addr0, addr1, addr2,
x_offset, y_offset);
de_set_video_layer_tile_offset(comp->regs,
tile_p0_x_offset, tile_p0_y_offset,
tile_p1_x_offset, tile_p1_y_offset);
if (scaler_en) {
if (need_update_csc(comp, color_space)) {
struct aicfb_disp_prop *disp_prop = &comp->disp_prop;
de_update_csc(comp, disp_prop, color_space);
}
de_set_scaler0_channel(comp->regs, in_w, in_h,
scaler_w, scaler_h, 0);
if (channel_num == 2) {
de_set_scaler0_channel(comp->regs,
in_w_ch1, in_h_ch1,
scaler_w, scaler_h, 1);
}
de_scaler0_enable(comp->regs, 1);
} else {
de_scaler0_enable(comp->regs, 0);
}
de_video_layer_enable(comp->regs, 1);
return 0;
}
static inline int ui_rect_disable(struct aic_de_comp *comp,
u32 layer_id, u32 rect_id)
{
de_ui_layer_rect_enable(comp->regs, rect_id, 0);
if (is_all_rect_win_disabled(comp, layer_id))
de_ui_layer_enable(comp->regs, 0);
return 0;
}
static int update_one_layer_config(struct aic_de_comp *comp,
struct aicfb_layer_data *layer_data)
{
u32 index;
int ret;
if (!is_valid_layer_and_rect_id(comp, layer_data->layer_id,
layer_data->rect_id)) {
pr_err("%s() - layer_id %d or rect_id %d is invalid\n",
__func__, layer_data->layer_id, layer_data->rect_id);
return -EINVAL;
}
if (layer_data->enable == 0) {
index = (layer_data->layer_id << RECT_NUM_SHIFT)
+ layer_data->rect_id;
comp->layers[index].enable = 0;
if (is_ui_layer(comp, layer_data->layer_id)) {
ui_rect_disable(comp, layer_data->layer_id,
layer_data->rect_id);
} else {
de_video_layer_enable(comp->regs, 0);
}
return 0;
}
if (is_ui_layer(comp, layer_data->layer_id)) {
index = (layer_data->layer_id << RECT_NUM_SHIFT)
+ layer_data->rect_id;
if (!is_valid_ui_rect_size(comp, layer_data))
return -EINVAL;
ret = config_ui_layer_rect(comp, layer_data);
if (ret != 0)
return -EINVAL;
else
memcpy(&comp->layers[index], layer_data,
sizeof(struct aicfb_layer_data));
} else {
index = layer_data->layer_id << RECT_NUM_SHIFT;
if (!is_valid_video_size(comp, layer_data)) {
comp->layers[index].enable = 0;
de_video_layer_enable(comp->regs, 0);
return -EINVAL;
}
ret = config_video_layer(comp, layer_data);
if (ret != 0) {
comp->layers[index].enable = 0;
de_video_layer_enable(comp->regs, 0);
} else {
memcpy(&comp->layers[index], layer_data,
sizeof(struct aicfb_layer_data));
}
return ret;
}
return 0;
}
static int aic_de_update_layer_config(struct aicfb_layer_data *layer_data)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
int ret;
de_config_update_enable(comp->regs, 0);
ret = update_one_layer_config(comp, layer_data);
de_config_update_enable(comp->regs, 1);
aic_de_release_drvdata();
return ret;
}
static int aic_de_update_layer_config_list(struct aicfb_config_lists *list)
{
int ret, i;
struct aic_de_comp *comp = aic_de_request_drvdata();
de_config_update_enable(comp->regs, 0);
for (i = 0; i < list->num; i++) {
if (is_ui_layer(comp, list->layers[i].layer_id)) {
int index = (list->layers[i].layer_id << RECT_NUM_SHIFT)
+ list->layers[i].rect_id;
comp->layers[index].enable = 0;
}
}
for (i = 0; i < list->num; i++) {
ret = update_one_layer_config(comp, &list->layers[i]);
if (ret)
return ret;
}
de_config_update_enable(comp->regs, 1);
aic_de_release_drvdata();
return 0;
}
static int aic_de_set_display_prop(struct aicfb_disp_prop *disp_prop)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
de_config_update_enable(comp->regs, 0);
if (need_update_disp_prop(comp, disp_prop)) {
/* get color space from video layer config */
struct aicfb_layer_data *layer_data = &comp->layers[0];
int color_space = MPP_BUF_COLOR_SPACE_GET(layer_data->buf.flags);
de_update_csc(comp, disp_prop, color_space);
}
de_config_update_enable(comp->regs, 1);
memcpy(&comp->disp_prop, disp_prop, sizeof(struct aicfb_disp_prop));
aic_de_release_drvdata();
return 0;
}
static int aic_de_get_display_prop(struct aicfb_disp_prop *disp_prop)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
memcpy(disp_prop, &comp->disp_prop, sizeof(struct aicfb_disp_prop));
aic_de_release_drvdata();
return 0;
}
struct de_funcs aic_de_funcs = {
.set_mode = aic_de_set_mode,
.clk_enable = aic_de_clk_enable,
.clk_disable = aic_de_clk_disable,
.timing_enable = aic_de_timing_enable,
.timing_disable = aic_de_timing_disable,
.wait_for_vsync = aic_de_wait_for_vsync,
.get_alpha_config = aic_de_get_alpha_config,
.update_alpha_config = aic_de_update_alpha_config,
.get_ck_config = aic_de_get_ck_config,
.update_ck_config = aic_de_update_ck_config,
.get_layer_config = aic_de_get_layer_config,
.update_layer_config = aic_de_update_layer_config,
.update_layer_config_list = aic_de_update_layer_config_list,
.set_display_prop = aic_de_set_display_prop,
.get_display_prop = aic_de_get_display_prop,
};
static const struct aicfb_layer_num layer_num = {
.vi_num = 1,
.ui_num = 1,
};
static const struct aicfb_layer_capability aicfb_layer_cap[] = {
{0, AICFB_LAYER_TYPE_VIDEO, 2048, 2048, AICFB_CAP_SCALING_FLAG},
{1, AICFB_LAYER_TYPE_UI, 2048, 2048,
AICFB_CAP_4_RECT_WIN_FLAG|AICFB_CAP_ALPHA_FLAG|AICFB_CAP_CK_FLAG},
};
static const struct aic_de_configs aic_de_cfg = {
.layer_num = &layer_num,
.cap = aicfb_layer_cap,
};
static int aic_de_probe(void)
{
struct aic_de_comp *comp;
comp = aicos_malloc(0, sizeof(struct aic_de_comp));
if (!comp)
{
pr_err("allloc de comp failed\n");
return -ENOMEM;
}
memset(comp, 0, sizeof(*comp));
comp->regs = (void *)DE_BASE;
comp->config = &aic_de_cfg;
comp->vsync_event = aicos_event_create();
#ifdef AIC_DISPLAY_DITHER
switch (AIC_DISP_OUTPUT_DEPTH)
{
case DITHER_RGB565:
comp->dither.red_bitdepth = 5;
comp->dither.gleen_bitdepth = 6;
comp->dither.blue_bitdepth = 5;
comp->dither.enable = 1;
break;
case DITHER_RGB666:
comp->dither.red_bitdepth = 6;
comp->dither.gleen_bitdepth = 6;
comp->dither.blue_bitdepth = 6;
comp->dither.enable = 1;
break;
default:
pr_err("Invalid dither mode\n");
break;
}
#endif
/* set display properties to default value */
comp->disp_prop.bright = 50;
comp->disp_prop.contrast = 50;
comp->disp_prop.saturation = 50;
comp->disp_prop.hue = 50;
g_aic_de_comp = comp;
return 0;
}
static void aic_de_remove(void)
{
}
struct platform_driver artinchip_de_driver = {
.name = "artinchip-de",
.component_type = AIC_DE_COM,
.probe = aic_de_probe,
.remove = aic_de_remove,
.de_funcs = &aic_de_funcs,
};