linuxOS_D21X/source/linux-5.10/drivers/video/artinchip/disp/aic_de.c
2025-08-14 15:17:16 +08:00

2082 lines
49 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2024 ArtInChip Technology Co., Ltd.
* Authors: Ning Fang <ning.fang@artinchip.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/reset.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <dt-bindings/display/artinchip,aic-disp.h>
#ifdef CONFIG_DMA_SHARED_BUFFER
#include <linux/dma-buf.h>
#endif
#include "aic_fb.h"
#include "video/artinchip_fb.h"
#include "hw/de_hw.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)
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;
};
#ifdef CONFIG_DMA_SHARED_BUFFER
struct aic_de_dmabuf {
struct list_head list;
struct dma_buf_info fds;
struct dma_buf *buf;
struct dma_buf_attachment *attach;
struct sg_table *sgt;
dma_addr_t phy_addr;
int pid;
};
#endif
struct aic_de_comp {
/* de_funcs must be the first member */
struct de_funcs funcs;
struct device *dev;
struct videomode vm;
bool vm_flag;
bool init_flag;
int vsync_flag;
u32 scaler_active;
u32 accum_line;
wait_queue_head_t vsync_wait;
spinlock_t slock;
const struct aic_de_configs *config;
struct aic_panel *panel;
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];
#ifdef CONFIG_DMA_SHARED_BUFFER
struct mutex mutex;
struct list_head dma_buf;
#endif
void __iomem *regs;
struct reset_control *reset;
struct clk *mclk;
struct clk *pclk;
int irq;
ulong mclk_rate;
struct aicfb_disp_prop disp_prop;
};
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 = ctx;
unsigned int status;
unsigned long flags;
status = de_timing_interrupt_status(comp->regs);
de_timing_interrupt_clean_status(comp->regs, status);
if (status & TIMING_INIT_V_BLANK_FLAG) {
spin_lock_irqsave(&comp->slock, flags);
comp->vsync_flag = 1;
spin_unlock_irqrestore(&comp->slock, flags);
wake_up_interruptible(&comp->vsync_wait);
}
if (comp->scaler_active & SCALER0_CTRL_ACTIVE) {
comp->scaler_active = comp->scaler_active & 0xF;
de_scaler0_active_handle(comp->regs, comp->scaler_active);
}
if (status & TIMING_INIT_UNDERFLOW_FLAG)
dev_err(comp->dev, "DE underflow error (irq status: %#x)\n",
status);
return IRQ_HANDLED;
}
static int aic_de_request_irq(struct device *dev,
struct aic_de_comp *comp)
{
struct platform_device *pdev = to_platform_device(dev);
int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "Couldn't get disp engine interrupt\n");
return irq;
}
comp->irq = irq;
ret = devm_request_irq(dev, irq, aic_de_handler, 0,
dev_name(dev), comp);
if (ret) {
dev_err(dev, "Couldn't request the IRQ\n");
return ret;
}
return 0;
}
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 void de_check_scaler0_active(struct aic_de_comp *comp,
u32 input_w, u32 input_h,
u32 output_w, u32 output_h)
{
int step = (input_h << 16) / output_h;
u32 scaler_active = comp->scaler_active & 0xF;
u32 index = 0;
if (step <= 0x18000)
index = 0;
else if (step <= 0x20000)
index = 1;
else if (step <= 0x2C000)
index = 2;
else
index = 3;
if (scaler_active != index)
comp->scaler_active = index | SCALER0_CTRL_ACTIVE;
}
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 void aic_de_calc_config(struct aic_de_comp *comp)
{
struct videomode *vm = &comp->vm;
u32 vtotal = vm->vactive + vm->vfront_porch +
vm->vback_porch + vm->vsync_len;
u32 htotal = vm->hactive + vm->hfront_porch +
vm->hback_porch + vm->hsync_len;
u32 pixelclock = vm->pixelclock;
u32 fps = pixelclock / htotal / vtotal;
u32 frame_us = 1000000 / fps;
u32 line_us = frame_us / vtotal;
u32 line = 1;
while (line_us < LAYER_CONFIG_TIME_US) {
line_us = line_us << 1;
line++;
}
comp->accum_line = vtotal - line - 1;
}
static int aic_de_set_mode(struct aic_panel *panel, struct videomode *vm)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
int disp_dither = panel->disp_dither;
memcpy(&comp->vm, vm, sizeof(struct videomode));
comp->vm_flag = true;
comp->panel = panel;
aic_de_calc_config(comp);
switch (disp_dither) {
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:
memset(&comp->dither, 0, sizeof(struct aic_de_dither));
break;
}
aic_de_release_drvdata();
return 0;
}
static int aic_de_clk_enable(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
int ret = 0;
ret = clk_set_rate(comp->mclk, comp->mclk_rate);
if (ret)
dev_err(comp->dev, "Failed to set CLK_DE %ld\n",
comp->mclk_rate);
ret = reset_control_deassert(comp->reset);
if (ret) {
dev_err(comp->dev, "%s() - Couldn't deassert\n", __func__);
aic_de_release_drvdata();
return ret;
}
ret = clk_prepare_enable(comp->mclk);
if (ret)
dev_err(comp->dev, "%s() - Couldn't enable mclk\n", __func__);
aic_de_release_drvdata();
return ret;
}
static int aic_de_clk_disable(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
clk_disable_unprepare(comp->mclk);
reset_control_assert(comp->reset);
aic_de_release_drvdata();
return 0;
}
static int aic_de_timing_enable(u32 flags)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
u32 active_w = comp->vm.hactive;
u32 active_h = comp->vm.vactive;
u32 hfp = comp->vm.hfront_porch;
u32 hbp = comp->vm.hback_porch;
u32 vfp = comp->vm.vfront_porch;
u32 vbp = comp->vm.vback_porch;
u32 hsync = comp->vm.hsync_len;
u32 vsync = comp->vm.vsync_len;
bool h_pol = !!(comp->vm.flags & DISPLAY_FLAGS_HSYNC_HIGH);
bool v_pol = !!(comp->vm.flags & DISPLAY_FLAGS_VSYNC_HIGH);
struct aic_tearing_effect *te = &comp->panel->te;
int ret;
if (!comp->vm_flag) {
dev_err(comp->dev, "%s() - videomode isn't init\n", __func__);
aic_de_release_drvdata();
return -EINVAL;
}
ret = clk_prepare_enable(comp->pclk);
if (ret) {
dev_err(comp->dev, "%s() - Couldn't enable pclk\n", __func__);
aic_de_release_drvdata();
return ret;
}
if (flags)
de_config_timing(comp->regs, active_w, active_h, hfp, hbp,
vfp, vbp, hsync, vsync, h_pol, v_pol);
/* set default config */
de_set_qos(comp->regs);
de_set_blending_size(comp->regs, active_w, active_h);
de_set_ui_layer_size(comp->regs, active_w, active_h, 0, 0);
de_scaler0_active_handle(comp->regs, 0);
if (!comp->init_flag) {
comp->alpha[1].layer_id = AICFB_LAYER_TYPE_UI;
comp->alpha[1].enable = 1;
comp->alpha[1].mode = AICFB_PIXEL_ALPHA_MODE;
comp->alpha[1].value = 0xff;
comp->init_flag = true;
}
de_config_prefetch_line_set(comp->regs, DE_PREFETCH_LINE);
de_soft_reset_ctrl(comp->regs, 1);
if (te->mode)
de_config_tearing_effect(comp->regs,
te->mode, te->pulse_width);
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);
de_timing_enable_interrupt(comp->regs, true, TIMING_INIT_V_BLANK_FLAG);
de_timing_enable_interrupt(comp->regs, true, TIMING_INIT_UNDERFLOW_FLAG);
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);
clk_disable_unprepare(comp->pclk);
aic_de_release_drvdata();
return 0;
}
static int aic_de_get_layer_num(struct aicfb_layer_num *num)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
memcpy(num, comp->config->layer_num, sizeof(struct aicfb_layer_num));
aic_de_release_drvdata();
return 0;
}
static int aic_de_get_layer_cap(struct aicfb_layer_capability *cap)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
if (is_valid_layer_id(comp, cap->layer_id) == false) {
dev_err(comp->dev, "Invalid layer id %d\n", cap->layer_id);
aic_de_release_drvdata();
return -EINVAL;
}
memcpy(cap, &comp->config->cap[cap->layer_id],
sizeof(struct aicfb_layer_capability));
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) {
dev_err(comp->dev, "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;
if (!comp->vm_flag) {
dev_err(comp->dev, "videomode isn't init\n");
return false;
}
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->vm.hactive;
active_h = comp->vm.vactive;
if (x_offset >= active_w || y_offset >= active_h) {
dev_err(comp->dev, "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) {
dev_err(comp->dev, "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;
if (!comp->vm_flag) {
dev_err(comp->dev, "videomode isn't init\n");
return false;
}
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->vm.hactive;
active_h = comp->vm.vactive;
if (x_offset >= active_w || y_offset >= active_h) {
dev_err(comp->dev, "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) {
dev_err(comp->dev, "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)) {
dev_err(comp->dev, "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;
}
#ifdef CONFIG_DMA_SHARED_BUFFER
static struct aic_de_dmabuf *aic_de_fd2index(struct aic_de_comp *comp, int fd)
{
struct aic_de_dmabuf *pos = NULL;
mutex_lock(&comp->mutex);
list_for_each_entry(pos, &comp->dma_buf, list) {
if (pos->fds.fd == fd) {
mutex_unlock(&comp->mutex);
return pos;
}
}
mutex_unlock(&comp->mutex);
dev_err(comp->dev, "failed to find out the given fd %d\n", fd);
return NULL;
}
static int config_video_dmabuf(struct aic_de_comp *comp,
struct mpp_buf *buf, unsigned int *phy_addr)
{
int i;
struct aic_de_dmabuf *dbuf = NULL;
for (i = 0; i < AICFB_PLANE_NUM; i++) {
if (buf->fd[i] <= 0)
continue;
dbuf = aic_de_fd2index(comp, buf->fd[i]);
if (dbuf == NULL)
return -1;
phy_addr[i] = dbuf->phy_addr;
}
return 0;
}
#endif
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;
u32 flags = layer_data->buf.flags;
if (MPP_BUF_PAN_DISPLAY_DMABUF_GET(flags)) {
unsigned int phy_addr[3] = {0};
if (config_video_dmabuf(comp, &layer_data->buf, phy_addr) < 0)
return -EINVAL;
addr0 = phy_addr[0];
layer_data->buf.phy_addr[0] = addr0;
}
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:
dev_err(comp->dev, "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) {
#ifdef CONFIG_DMA_SHARED_BUFFER
case MPP_DMA_BUF_FD:
{
unsigned int phy_addr[3] = {0};
if (config_video_dmabuf(comp, &layer_data->buf, phy_addr) < 0)
return -EINVAL;
addr0 = phy_addr[0];
addr1 = phy_addr[1];
addr2 = phy_addr[2];
break;
}
#endif
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:
dev_err(comp->dev, "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:
dev_err(comp->dev, "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_check_scaler0_active(comp, in_w_ch1, in_h_ch1,
scaler_w, scaler_h);
}
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)) {
dev_err(comp->dev,
"%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();
u32 output_line, lock = 1;
unsigned long flags;
int ret;
spin_lock_irqsave(&comp->slock, flags);
output_line = de_get_output_line(comp->regs);
if (output_line >= comp->accum_line || output_line <= DE_PREFETCH_LINE) {
spin_unlock_irqrestore(&comp->slock, flags);
aic_delay_ms(1);
lock = 0;
}
de_config_update_enable(comp->regs, 0);
ret = update_one_layer_config(comp, layer_data);
de_config_update_enable(comp->regs, 1);
if (lock)
spin_unlock_irqrestore(&comp->slock, flags);
aic_de_release_drvdata();
return ret;
}
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;
}
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_shadow_reg_ctrl(int enable)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
de_config_update_enable(comp->regs, (u32)enable);
aic_de_release_drvdata();
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) {
dev_err(comp->dev, "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) {
dev_err(comp->dev, "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) {
dev_err(comp->dev,
"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) {
dev_err(comp->dev,
"Layer %d doesn't support color key blending\n",
ck->layer_id);
aic_de_release_drvdata();
return -EINVAL;
}
if (ck->enable > 1) {
dev_err(comp->dev, "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_wait_for_vsync(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
unsigned long flags;
int ret;
spin_lock_irqsave(&comp->slock, flags);
comp->vsync_flag = 0;
spin_unlock_irqrestore(&comp->slock, flags);
ret = wait_event_interruptible_timeout(comp->vsync_wait,
comp->vsync_flag != 0,
msecs_to_jiffies(100));
if (ret < 0)
return ret;
if (ret == 0)
return -ETIMEDOUT;
return 0;
}
#ifdef CONFIG_DMA_SHARED_BUFFER
static int aic_de_add_dmabuf(struct dma_buf_info *fds)
{
int ret = 0;
struct aic_de_comp *comp = aic_de_request_drvdata();
struct aic_de_dmabuf *dmabuf = NULL;
struct dma_buf_attachment *attach;
struct sg_table *sgt;
struct dma_buf *buf;
dmabuf = kmalloc(sizeof(*dmabuf), GFP_KERNEL);
if (!dmabuf) {
dev_err(comp->dev, "kmalloc dmabuf failed!\n");
return -ENOMEM;
}
memset(dmabuf, 0, sizeof(struct aic_de_dmabuf));
memcpy(&dmabuf->fds, fds, sizeof(*fds));
buf = dma_buf_get(fds->fd);
if (IS_ERR(buf)) {
dev_err(comp->dev, "dma_buf_get(%d) failed\n",
fds->fd);
ret = PTR_ERR(buf);
goto end;
}
attach = dma_buf_attach(buf, comp->dev);
if (IS_ERR(attach)) {
dev_err(comp->dev, "dma_buf_attach(%d) failed\n",
fds->fd);
dma_buf_put(buf);
ret = PTR_ERR(attach);
goto end;
}
sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE);
if (IS_ERR(sgt)) {
dev_err(comp->dev,
"dma_buf_map_attachment(%d) failed\n",
fds->fd);
dma_buf_detach(buf, attach);
dma_buf_put(buf);
ret = PTR_ERR(sgt);
goto end;
}
dmabuf->buf = buf;
dmabuf->attach = attach;
dmabuf->sgt = sgt;
/* The scatterlist in sgt must be physically consecutive.
* So we consider the first dma_address as the buffer begin. */
dmabuf->phy_addr = sg_dma_address(sgt->sgl);
dmabuf->pid = current->tgid;
fds->phy_addr = dmabuf->phy_addr;
mutex_lock(&comp->mutex);
list_add_tail(&dmabuf->list, &comp->dma_buf);
mutex_unlock(&comp->mutex);
aic_de_release_drvdata();
return 0;
end:
kfree(dmabuf);
aic_de_release_drvdata();
return -1;
}
static int aic_de_remove_dmabuf(struct dma_buf_info *fds)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
struct aic_de_dmabuf *dmabuf = NULL;
mutex_lock(&comp->mutex);
if (!list_empty(&comp->dma_buf)) {
struct aic_de_dmabuf *pos = NULL, *n = NULL;
list_for_each_entry_safe(pos, n, &comp->dma_buf, list) {
if ((pos->fds.fd == fds->fd) && (pos->pid == current->tgid)) {
dmabuf = pos;
dma_buf_unmap_attachment(dmabuf->attach,
dmabuf->sgt, DMA_TO_DEVICE);
dma_buf_detach(dmabuf->buf, dmabuf->attach);
dma_buf_put(dmabuf->buf);
dmabuf->buf = NULL;
dmabuf->attach = NULL;
dmabuf->sgt = NULL;
dmabuf->phy_addr = 0;
dmabuf->fds.fd = 0;
list_del_init(&dmabuf->list);
kfree(dmabuf);
break;
}
}
}
aic_de_release_drvdata();
mutex_unlock(&comp->mutex);
return 0;
}
/* Called when /dev/fb0 closes. */
static int aic_de_release_dmabuf(void)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
struct aic_de_dmabuf *dmabuf = NULL;
mutex_lock(&comp->mutex);
if (!list_empty(&comp->dma_buf)) {
struct aic_de_dmabuf *pos = NULL, *n = NULL;
list_for_each_entry_safe(pos, n, &comp->dma_buf, list) {
if (pos->pid == current->tgid) {
dmabuf = pos;
dma_buf_unmap_attachment(dmabuf->attach,
dmabuf->sgt, DMA_TO_DEVICE);
dma_buf_detach(dmabuf->buf, dmabuf->attach);
dma_buf_put(dmabuf->buf);
list_del_init(&dmabuf->list);
kfree(dmabuf);
}
}
}
aic_de_release_drvdata();
mutex_unlock(&comp->mutex);
return 0;
}
#endif
static int aic_de_color_bar_ctrl(int enable)
{
struct aic_de_comp *comp = aic_de_request_drvdata();
de_config_update_enable(comp->regs, 0);
de_colorbar_ctrl(comp->regs, (u32)enable);
de_config_update_enable(comp->regs, 1);
aic_de_release_drvdata();
return 0;
}
static ulong aic_de_pixclk_rate(void)
{
ulong rate;
struct aic_de_comp *comp = aic_de_request_drvdata();
rate = comp->vm.pixelclock;
aic_de_release_drvdata();
return rate;
}
static int aic_de_pixclk_enable(void)
{
s32 ret = 0;
struct aic_de_comp *comp = aic_de_request_drvdata();
ret = clk_set_rate(comp->pclk, comp->vm.pixelclock);
if (ret)
dev_err(comp->dev, "Failed to set CLK_PIX %ld\n",
comp->vm.pixelclock);
aic_de_release_drvdata();
return ret;
}
static int aic_de_parse_dt(struct device *dev)
{
int ret;
struct device_node *np = dev->of_node;
struct aic_de_comp *comp = dev_get_drvdata(dev);
ret = of_property_read_u32(np, "mclk", (u32 *)&comp->mclk_rate);
if (ret) {
dev_err(dev, "Can't parse CLK_DE rate\n");
return ret;
}
return 0;
}
#define UI_RECT(x) ((AICFB_LAYER_TYPE_UI << RECT_NUM_SHIFT) + (x))
static ssize_t display_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct aic_de_comp *comp = dev_get_drvdata(dev);
sprintf(buf, "Video Layer Enable \t : %u\n"
"Video Layer Format \t : %d\n"
"Video Layer Input Size \t : %d x %d\n"
"Video Layer Stride \t : %d %d\n"
"Scaler Output Size \t : %d x %d\n"
"UI Layer Control \t : format: %d, color key: %d, alpha: %d\n"
"UI Layer Input Size \t : %d x %d\n"
"UI Layer Color Key \t : %x\n"
"UI Layer Alpha \t\t : mode: %d, g_alpha: %d\n"
"UI Rectangle Control \t : %d %d %d %d\n"
"UI Rectangle Size \t : "
"(%d, %d) (%d, %d) (%d, %d) (%d, %d)\n"
"UI Rectangle Offset \t : "
"(%d, %d) (%d, %d) (%d, %d) (%d, %d)\n"
"UI Rectangle Stride \t : %d %d %d %d\n"
"Tearing-effect\t\t : TE mode: %d, TE pulse width: %d\n"
"Display Dither\t\t : dither_en: %d, "
"red depth: %d, green depth: %d, blue depth: %d\n"
"Display Timing \t\t : hactive: %d, vactive: %d "
"hfp: %d hbp: %d vfp: %d vbp: %d "
"hsync: %d vsync: %d\n"
"Display Pixelclock \t : %ld HZ\n",
comp->layers[AICFB_LAYER_TYPE_VIDEO].enable,
comp->layers[AICFB_LAYER_TYPE_VIDEO].buf.format,
comp->layers[AICFB_LAYER_TYPE_VIDEO].buf.size.width,
comp->layers[AICFB_LAYER_TYPE_VIDEO].buf.size.height,
comp->layers[AICFB_LAYER_TYPE_VIDEO].buf.stride[0],
comp->layers[AICFB_LAYER_TYPE_VIDEO].buf.stride[1],
comp->layers[AICFB_LAYER_TYPE_VIDEO].scale_size.width,
comp->layers[AICFB_LAYER_TYPE_VIDEO].scale_size.height,
comp->layers[UI_RECT(0)].buf.format,
comp->ck[AICFB_LAYER_TYPE_UI].enable,
comp->alpha[AICFB_LAYER_TYPE_UI].enable,
comp->vm.hactive,
comp->vm.vactive,
comp->ck[AICFB_LAYER_TYPE_UI].value,
comp->alpha[AICFB_LAYER_TYPE_UI].mode,
comp->alpha[AICFB_LAYER_TYPE_UI].value,
comp->layers[UI_RECT(0)].enable,
comp->layers[UI_RECT(1)].enable,
comp->layers[UI_RECT(2)].enable,
comp->layers[UI_RECT(3)].enable,
comp->layers[UI_RECT(0)].buf.size.width,
comp->layers[UI_RECT(0)].buf.size.height,
comp->layers[UI_RECT(1)].buf.size.width,
comp->layers[UI_RECT(1)].buf.size.height,
comp->layers[UI_RECT(2)].buf.size.width,
comp->layers[UI_RECT(2)].buf.size.height,
comp->layers[UI_RECT(3)].buf.size.width,
comp->layers[UI_RECT(3)].buf.size.height,
comp->layers[UI_RECT(0)].pos.x,
comp->layers[UI_RECT(0)].pos.y,
comp->layers[UI_RECT(1)].pos.x,
comp->layers[UI_RECT(1)].pos.y,
comp->layers[UI_RECT(2)].pos.x,
comp->layers[UI_RECT(2)].pos.y,
comp->layers[UI_RECT(3)].pos.x,
comp->layers[UI_RECT(3)].pos.y,
comp->layers[UI_RECT(0)].buf.stride[0],
comp->layers[UI_RECT(1)].buf.stride[0],
comp->layers[UI_RECT(2)].buf.stride[0],
comp->layers[UI_RECT(3)].buf.stride[0],
comp->panel->te.mode, comp->panel->te.pulse_width,
comp->dither.enable, comp->dither.red_bitdepth,
comp->dither.gleen_bitdepth, comp->dither.blue_bitdepth,
comp->vm.hactive, comp->vm.vactive,
comp->vm.hfront_porch, comp->vm.hback_porch,
comp->vm.vfront_porch, comp->vm.vback_porch,
comp->vm.hsync_len, comp->vm.vsync_len,
comp->vm.pixelclock);
return strlen(buf);
}
static DEVICE_ATTR_RO(display);
static ssize_t color_bar_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct aic_de_comp *comp = dev_get_drvdata(dev);
bool enable;
int ret;
ret = kstrtobool(buf, &enable);
if (ret)
return ret;
de_config_update_enable(comp->regs, 0);
if (enable)
de_colorbar_ctrl(comp->regs, 1);
else
de_colorbar_ctrl(comp->regs, 0);
de_config_update_enable(comp->regs, 1);
return size;
}
static DEVICE_ATTR_WO(color_bar);
static ssize_t
dither_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct aic_de_comp *comp = dev_get_drvdata(dev);
struct aic_de_dither *dither = &comp->dither;
u32 out_depth;
out_depth = dither->enable ? dither->red_bitdepth +
dither->gleen_bitdepth + dither->blue_bitdepth : 24;
return sprintf(buf, "Dither: %s, Output depth: %d\n",
dither->enable ? "Enable" : "Disable",
out_depth);
}
static ssize_t dither_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct aic_de_comp *comp = dev_get_drvdata(dev);
struct aic_panel *panel = comp->panel;
u32 val;
int ret;
ret = kstrtou32(buf, 0, &val);
if (ret)
return ret;
switch (val) {
case 16:
comp->dither.red_bitdepth = 5;
comp->dither.gleen_bitdepth = 6;
comp->dither.blue_bitdepth = 5;
comp->dither.enable = 1;
break;
case 18:
comp->dither.red_bitdepth = 6;
comp->dither.gleen_bitdepth = 6;
comp->dither.blue_bitdepth = 6;
comp->dither.enable = 1;
break;
case 0:
case 24:
memset(&comp->dither, 0, sizeof(struct aic_de_dither));
break;
default:
pr_err("Invalid output depth, 16/18/24\n");
return size;
}
de_config_update_enable(comp->regs, 0);
de_set_dither(comp->regs,
comp->dither.red_bitdepth,
comp->dither.gleen_bitdepth,
comp->dither.blue_bitdepth,
comp->dither.enable);
de_config_update_enable(comp->regs, 1);
panel->disp_dither = val;
return size;
}
static DEVICE_ATTR_RW(dither);
static struct attribute *aic_de_attrs[] = {
&dev_attr_display.attr,
&dev_attr_color_bar.attr,
&dev_attr_dither.attr,
NULL
};
static const struct attribute_group aic_de_attr_group = {
.attrs = aic_de_attrs,
.name = "debug",
};
static void aic_de_unbind(struct device *dev, struct device *master,
void *data)
{
sysfs_remove_group(&dev->kobj, &aic_de_attr_group);
}
static int aic_de_register_funcs(struct aic_de_comp *comp)
{
comp->funcs.set_mode = aic_de_set_mode;
comp->funcs.clk_enable = aic_de_clk_enable;
comp->funcs.clk_disable = aic_de_clk_disable;
comp->funcs.timing_enable = aic_de_timing_enable;
comp->funcs.timing_disable = aic_de_timing_disable;
comp->funcs.get_layer_num = aic_de_get_layer_num;
comp->funcs.get_layer_cap = aic_de_get_layer_cap;
comp->funcs.get_layer_config = aic_de_get_layer_config;
comp->funcs.update_layer_config = aic_de_update_layer_config;
comp->funcs.update_layer_config_list = aic_de_update_layer_config_list;
comp->funcs.shadow_reg_ctrl = aic_de_shadow_reg_ctrl;
comp->funcs.get_alpha_config = aic_de_get_alpha_config;
comp->funcs.update_alpha_config = aic_de_update_alpha_config;
comp->funcs.get_ck_config = aic_de_get_ck_config;
comp->funcs.update_ck_config = aic_de_update_ck_config;
comp->funcs.wait_for_vsync = aic_de_wait_for_vsync;
#ifdef CONFIG_DMA_SHARED_BUFFER
comp->funcs.add_dmabuf = aic_de_add_dmabuf;
comp->funcs.remove_dmabuf = aic_de_remove_dmabuf;
comp->funcs.release_dmabuf = aic_de_release_dmabuf;
#endif
comp->funcs.color_bar_ctrl = aic_de_color_bar_ctrl;
comp->funcs.pixclk_rate = aic_de_pixclk_rate;
comp->funcs.pixclk_enable = aic_de_pixclk_enable;
comp->funcs.set_display_prop = aic_de_set_display_prop;
comp->funcs.get_display_prop = aic_de_get_display_prop;
return 0;
}
static int aic_de_bind(struct device *dev, struct device *master,
void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct aic_de_comp *comp;
struct resource *res;
void __iomem *regs;
int ret;
comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
if (!comp)
return -ENOMEM;
comp->dev = dev;
comp->config = of_device_get_match_data(dev);
dev_set_drvdata(dev, comp);
g_aic_de_comp = comp;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
comp->regs = regs;
comp->mclk = devm_clk_get(dev, "de0");
if (IS_ERR(comp->mclk)) {
dev_err(dev, "Couldn't get de0 clock\n");
return PTR_ERR(comp->mclk);
}
comp->pclk = devm_clk_get(dev, "pix");
if (IS_ERR(comp->pclk)) {
dev_err(dev, "Couldn't get pix clock\n");
return PTR_ERR(comp->pclk);
}
comp->reset = devm_reset_control_get(dev, "de0");
if (IS_ERR(comp->reset)) {
dev_err(dev, "Couldn't get reset line\n");
return PTR_ERR(comp->reset);
}
ret = aic_de_parse_dt(dev);
if (ret)
return ret;
ret = aic_de_request_irq(dev, comp);
if (ret) {
dev_err(dev, "Couldn't request disp engine IRQ\n");
return ret;
}
ret = sysfs_create_group(&dev->kobj, &aic_de_attr_group);
if (ret) {
dev_err(dev, "Failed to create %s node.\n",
aic_de_attr_group.name);
return ret;
}
/* initialize the vsync wait queue */
init_waitqueue_head(&comp->vsync_wait);
spin_lock_init(&comp->slock);
INIT_LIST_HEAD(&comp->dma_buf);
/* 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;
aic_de_register_funcs(comp);
return 0;
}
static const struct component_ops aic_de_com_ops = {
.bind = aic_de_bind,
.unbind = aic_de_unbind,
};
static int aic_de_probe(struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "%s()\n", __func__);
return component_add(&pdev->dev, &aic_de_com_ops);
}
static int aic_de_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &aic_de_com_ops);
return 0;
}
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 const struct of_device_id aic_de_match_table[] = {
{.compatible = "artinchip,aic-de-v1.0",
.data = &aic_de_cfg},
{},
};
MODULE_DEVICE_TABLE(of, aic_de_match_table);
static struct platform_driver aic_de_driver = {
.probe = aic_de_probe,
.remove = aic_de_remove,
.driver = {
.name = "disp_engine",
.of_match_table = aic_de_match_table,
},
};
module_platform_driver(aic_de_driver);
MODULE_AUTHOR("Ning Fang <ning.fang@artinchip.com>");
MODULE_DESCRIPTION("AIC disp engine driver");
MODULE_ALIAS("platform:disp engine");
MODULE_LICENSE("GPL v2");