// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2020-2024 ArtInChip Technology Co., Ltd. * Authors: Ning Fang */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_DMA_SHARED_BUFFER #include #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 "); MODULE_DESCRIPTION("AIC disp engine driver"); MODULE_ALIAS("platform:disp engine"); MODULE_LICENSE("GPL v2");