From 2c3c1048746a4622d8c89a29670120dc8fab93c4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:49:45 +0200 Subject: Adding upstream version 6.1.76. Signed-off-by: Daniel Baumann --- .../gpu/drm/arm/display/komeda/d71/d71_component.c | 1442 ++++++++++++++++++++ drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c | 643 +++++++++ drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h | 54 + drivers/gpu/drm/arm/display/komeda/d71/d71_regs.h | 541 ++++++++ 4 files changed, 2680 insertions(+) create mode 100644 drivers/gpu/drm/arm/display/komeda/d71/d71_component.c create mode 100644 drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c create mode 100644 drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h create mode 100644 drivers/gpu/drm/arm/display/komeda/d71/d71_regs.h (limited to 'drivers/gpu/drm/arm/display/komeda/d71') diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c new file mode 100644 index 000000000..42510fdea --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c @@ -0,0 +1,1442 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang + * + */ +#include "d71_dev.h" +#include "komeda_kms.h" +#include "malidp_io.h" +#include "komeda_framebuffer.h" +#include "komeda_color_mgmt.h" + +static void get_resources_id(u32 hw_id, u32 *pipe_id, u32 *comp_id) +{ + u32 id = BLOCK_INFO_BLK_ID(hw_id); + u32 pipe = id; + + switch (BLOCK_INFO_BLK_TYPE(hw_id)) { + case D71_BLK_TYPE_LPU_WB_LAYER: + id = KOMEDA_COMPONENT_WB_LAYER; + break; + case D71_BLK_TYPE_CU_SPLITTER: + id = KOMEDA_COMPONENT_SPLITTER; + break; + case D71_BLK_TYPE_CU_SCALER: + pipe = id / D71_PIPELINE_MAX_SCALERS; + id %= D71_PIPELINE_MAX_SCALERS; + id += KOMEDA_COMPONENT_SCALER0; + break; + case D71_BLK_TYPE_CU: + id += KOMEDA_COMPONENT_COMPIZ0; + break; + case D71_BLK_TYPE_LPU_LAYER: + pipe = id / D71_PIPELINE_MAX_LAYERS; + id %= D71_PIPELINE_MAX_LAYERS; + id += KOMEDA_COMPONENT_LAYER0; + break; + case D71_BLK_TYPE_DOU_IPS: + id += KOMEDA_COMPONENT_IPS0; + break; + case D71_BLK_TYPE_CU_MERGER: + id = KOMEDA_COMPONENT_MERGER; + break; + case D71_BLK_TYPE_DOU: + id = KOMEDA_COMPONENT_TIMING_CTRLR; + break; + default: + id = 0xFFFFFFFF; + } + + if (comp_id) + *comp_id = id; + + if (pipe_id) + *pipe_id = pipe; +} + +static u32 get_valid_inputs(struct block_header *blk) +{ + u32 valid_inputs = 0, comp_id; + int i; + + for (i = 0; i < PIPELINE_INFO_N_VALID_INPUTS(blk->pipeline_info); i++) { + get_resources_id(blk->input_ids[i], NULL, &comp_id); + if (comp_id == 0xFFFFFFFF) + continue; + valid_inputs |= BIT(comp_id); + } + + return valid_inputs; +} + +static void get_values_from_reg(void __iomem *reg, u32 offset, + u32 count, u32 *val) +{ + u32 i, addr; + + for (i = 0; i < count; i++) { + addr = offset + (i << 2); + /* 0xA4 is WO register */ + if (addr != 0xA4) + val[i] = malidp_read32(reg, addr); + else + val[i] = 0xDEADDEAD; + } +} + +static void dump_block_header(struct seq_file *sf, void __iomem *reg) +{ + struct block_header hdr; + u32 i, n_input, n_output; + + d71_read_block_header(reg, &hdr); + seq_printf(sf, "BLOCK_INFO:\t\t0x%X\n", hdr.block_info); + seq_printf(sf, "PIPELINE_INFO:\t\t0x%X\n", hdr.pipeline_info); + + n_output = PIPELINE_INFO_N_OUTPUTS(hdr.pipeline_info); + n_input = PIPELINE_INFO_N_VALID_INPUTS(hdr.pipeline_info); + + for (i = 0; i < n_input; i++) + seq_printf(sf, "VALID_INPUT_ID%u:\t0x%X\n", + i, hdr.input_ids[i]); + + for (i = 0; i < n_output; i++) + seq_printf(sf, "OUTPUT_ID%u:\t\t0x%X\n", + i, hdr.output_ids[i]); +} + +/* On D71, we are using the global line size. From D32, every component have + * a line size register to indicate the fifo size. + */ +static u32 __get_blk_line_size(struct d71_dev *d71, u32 __iomem *reg, + u32 max_default) +{ + if (!d71->periph_addr) + max_default = malidp_read32(reg, BLK_MAX_LINE_SIZE); + + return max_default; +} + +static u32 get_blk_line_size(struct d71_dev *d71, u32 __iomem *reg) +{ + return __get_blk_line_size(d71, reg, d71->max_line_size); +} + +static u32 to_rot_ctrl(u32 rot) +{ + u32 lr_ctrl = 0; + + switch (rot & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: + lr_ctrl |= L_ROT(L_ROT_R0); + break; + case DRM_MODE_ROTATE_90: + lr_ctrl |= L_ROT(L_ROT_R90); + break; + case DRM_MODE_ROTATE_180: + lr_ctrl |= L_ROT(L_ROT_R180); + break; + case DRM_MODE_ROTATE_270: + lr_ctrl |= L_ROT(L_ROT_R270); + break; + } + + if (rot & DRM_MODE_REFLECT_X) + lr_ctrl |= L_HFLIP; + if (rot & DRM_MODE_REFLECT_Y) + lr_ctrl |= L_VFLIP; + + return lr_ctrl; +} + +static u32 to_ad_ctrl(u64 modifier) +{ + u32 afbc_ctrl = AD_AEN; + + if (!modifier) + return 0; + + if ((modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) == + AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) + afbc_ctrl |= AD_WB; + + if (modifier & AFBC_FORMAT_MOD_YTR) + afbc_ctrl |= AD_YT; + if (modifier & AFBC_FORMAT_MOD_SPLIT) + afbc_ctrl |= AD_BS; + if (modifier & AFBC_FORMAT_MOD_TILED) + afbc_ctrl |= AD_TH; + + return afbc_ctrl; +} + +static inline u32 to_d71_input_id(struct komeda_component_state *st, int idx) +{ + struct komeda_component_output *input = &st->inputs[idx]; + + /* if input is not active, set hw input_id(0) to disable it */ + if (has_bit(idx, st->active_inputs)) + return input->component->hw_id + input->output_port; + else + return 0; +} + +static void d71_layer_update_fb(struct komeda_component *c, + struct komeda_fb *kfb, + dma_addr_t *addr) +{ + struct drm_framebuffer *fb = &kfb->base; + const struct drm_format_info *info = fb->format; + u32 __iomem *reg = c->reg; + int block_h; + + if (info->num_planes > 2) + malidp_write64(reg, BLK_P2_PTR_LOW, addr[2]); + + if (info->num_planes > 1) { + block_h = drm_format_info_block_height(info, 1); + malidp_write32(reg, BLK_P1_STRIDE, fb->pitches[1] * block_h); + malidp_write64(reg, BLK_P1_PTR_LOW, addr[1]); + } + + block_h = drm_format_info_block_height(info, 0); + malidp_write32(reg, BLK_P0_STRIDE, fb->pitches[0] * block_h); + malidp_write64(reg, BLK_P0_PTR_LOW, addr[0]); + malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id); +} + +static void d71_layer_disable(struct komeda_component *c) +{ + malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0); +} + +static void d71_layer_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_layer_state *st = to_layer_st(state); + struct drm_plane_state *plane_st = state->plane->state; + struct drm_framebuffer *fb = plane_st->fb; + struct komeda_fb *kfb = to_kfb(fb); + u32 __iomem *reg = c->reg; + u32 ctrl_mask = L_EN | L_ROT(L_ROT_R270) | L_HFLIP | L_VFLIP | L_TBU_EN; + u32 ctrl = L_EN | to_rot_ctrl(st->rot); + + d71_layer_update_fb(c, kfb, st->addr); + + malidp_write32(reg, AD_CONTROL, to_ad_ctrl(fb->modifier)); + if (fb->modifier) { + u64 addr; + + malidp_write32(reg, LAYER_AD_H_CROP, HV_CROP(st->afbc_crop_l, + st->afbc_crop_r)); + malidp_write32(reg, LAYER_AD_V_CROP, HV_CROP(st->afbc_crop_t, + st->afbc_crop_b)); + /* afbc 1.2 wants payload, afbc 1.0/1.1 wants end_addr */ + if (fb->modifier & AFBC_FORMAT_MOD_TILED) + addr = st->addr[0] + kfb->offset_payload; + else + addr = st->addr[0] + kfb->afbc_size - 1; + + malidp_write32(reg, BLK_P1_PTR_LOW, lower_32_bits(addr)); + malidp_write32(reg, BLK_P1_PTR_HIGH, upper_32_bits(addr)); + } + + if (fb->format->is_yuv) { + u32 upsampling = 0; + + switch (kfb->format_caps->fourcc) { + case DRM_FORMAT_YUYV: + upsampling = fb->modifier ? LR_CHI422_BILINEAR : + LR_CHI422_REPLICATION; + break; + case DRM_FORMAT_UYVY: + upsampling = LR_CHI422_REPLICATION; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_YUV420_8BIT: + case DRM_FORMAT_YUV420_10BIT: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_P010: + /* these fmt support MPGE/JPEG both, here perfer JPEG*/ + upsampling = LR_CHI420_JPEG; + break; + case DRM_FORMAT_X0L2: + upsampling = LR_CHI420_JPEG; + break; + default: + break; + } + + malidp_write32(reg, LAYER_R_CONTROL, upsampling); + malidp_write_group(reg, LAYER_YUV_RGB_COEFF0, + KOMEDA_N_YUV2RGB_COEFFS, + komeda_select_yuv2rgb_coeffs( + plane_st->color_encoding, + plane_st->color_range)); + } + + malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize)); + + if (kfb->is_va) + ctrl |= L_TBU_EN; + malidp_write32_mask(reg, BLK_CONTROL, ctrl_mask, ctrl); +} + +static void d71_layer_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v[15], i; + bool rich, rgb2rgb; + char *prefix; + + get_values_from_reg(c->reg, LAYER_INFO, 1, &v[14]); + if (v[14] & 0x1) { + rich = true; + prefix = "LR_"; + } else { + rich = false; + prefix = "LS_"; + } + + rgb2rgb = !!(v[14] & L_INFO_CM); + + dump_block_header(sf, c->reg); + + seq_printf(sf, "%sLAYER_INFO:\t\t0x%X\n", prefix, v[14]); + + get_values_from_reg(c->reg, 0xD0, 1, v); + seq_printf(sf, "%sCONTROL:\t\t0x%X\n", prefix, v[0]); + if (rich) { + get_values_from_reg(c->reg, 0xD4, 1, v); + seq_printf(sf, "LR_RICH_CONTROL:\t0x%X\n", v[0]); + } + get_values_from_reg(c->reg, 0xD8, 4, v); + seq_printf(sf, "%sFORMAT:\t\t0x%X\n", prefix, v[0]); + seq_printf(sf, "%sIT_COEFFTAB:\t\t0x%X\n", prefix, v[1]); + seq_printf(sf, "%sIN_SIZE:\t\t0x%X\n", prefix, v[2]); + seq_printf(sf, "%sPALPHA:\t\t0x%X\n", prefix, v[3]); + + get_values_from_reg(c->reg, 0x100, 3, v); + seq_printf(sf, "%sP0_PTR_LOW:\t\t0x%X\n", prefix, v[0]); + seq_printf(sf, "%sP0_PTR_HIGH:\t\t0x%X\n", prefix, v[1]); + seq_printf(sf, "%sP0_STRIDE:\t\t0x%X\n", prefix, v[2]); + + get_values_from_reg(c->reg, 0x110, 2, v); + seq_printf(sf, "%sP1_PTR_LOW:\t\t0x%X\n", prefix, v[0]); + seq_printf(sf, "%sP1_PTR_HIGH:\t\t0x%X\n", prefix, v[1]); + if (rich) { + get_values_from_reg(c->reg, 0x118, 1, v); + seq_printf(sf, "LR_P1_STRIDE:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, 0x120, 2, v); + seq_printf(sf, "LR_P2_PTR_LOW:\t\t0x%X\n", v[0]); + seq_printf(sf, "LR_P2_PTR_HIGH:\t\t0x%X\n", v[1]); + + get_values_from_reg(c->reg, 0x130, 12, v); + for (i = 0; i < 12; i++) + seq_printf(sf, "LR_YUV_RGB_COEFF%u:\t0x%X\n", i, v[i]); + } + + if (rgb2rgb) { + get_values_from_reg(c->reg, LAYER_RGB_RGB_COEFF0, 12, v); + for (i = 0; i < 12; i++) + seq_printf(sf, "LS_RGB_RGB_COEFF%u:\t0x%X\n", i, v[i]); + } + + get_values_from_reg(c->reg, 0x160, 3, v); + seq_printf(sf, "%sAD_CONTROL:\t\t0x%X\n", prefix, v[0]); + seq_printf(sf, "%sAD_H_CROP:\t\t0x%X\n", prefix, v[1]); + seq_printf(sf, "%sAD_V_CROP:\t\t0x%X\n", prefix, v[2]); +} + +static int d71_layer_validate(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_layer_state *st = to_layer_st(state); + struct komeda_layer *layer = to_layer(c); + struct drm_plane_state *plane_st; + struct drm_framebuffer *fb; + u32 fourcc, line_sz, max_line_sz; + + plane_st = drm_atomic_get_new_plane_state(state->obj.state, + state->plane); + fb = plane_st->fb; + fourcc = fb->format->format; + + if (drm_rotation_90_or_270(st->rot)) + line_sz = st->vsize - st->afbc_crop_t - st->afbc_crop_b; + else + line_sz = st->hsize - st->afbc_crop_l - st->afbc_crop_r; + + if (fb->modifier) { + if ((fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) == + AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) + max_line_sz = layer->line_sz; + else + max_line_sz = layer->line_sz / 2; + + if (line_sz > max_line_sz) { + DRM_DEBUG_ATOMIC("afbc request line_sz: %d exceed the max afbc line_sz: %d.\n", + line_sz, max_line_sz); + return -EINVAL; + } + } + + if (fourcc == DRM_FORMAT_YUV420_10BIT && line_sz > 2046 && (st->afbc_crop_l % 4)) { + DRM_DEBUG_ATOMIC("YUV420_10BIT input_hsize: %d exceed the max size 2046.\n", + line_sz); + return -EINVAL; + } + + if (fourcc == DRM_FORMAT_X0L2 && line_sz > 2046 && (st->addr[0] % 16)) { + DRM_DEBUG_ATOMIC("X0L2 input_hsize: %d exceed the max size 2046.\n", + line_sz); + return -EINVAL; + } + + return 0; +} + +static const struct komeda_component_funcs d71_layer_funcs = { + .validate = d71_layer_validate, + .update = d71_layer_update, + .disable = d71_layer_disable, + .dump_register = d71_layer_dump, +}; + +static int d71_layer_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_layer *layer; + u32 pipe_id, layer_id, layer_info; + + get_resources_id(blk->block_info, &pipe_id, &layer_id); + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*layer), + layer_id, + BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_layer_funcs, 0, + get_valid_inputs(blk), + 1, reg, "LPU%d_LAYER%d", pipe_id, layer_id); + if (IS_ERR(c)) { + DRM_ERROR("Failed to add layer component\n"); + return PTR_ERR(c); + } + + layer = to_layer(c); + layer_info = malidp_read32(reg, LAYER_INFO); + + if (layer_info & L_INFO_RF) + layer->layer_type = KOMEDA_FMT_RICH_LAYER; + else + layer->layer_type = KOMEDA_FMT_SIMPLE_LAYER; + + if (!d71->periph_addr) { + /* D32 or newer product */ + layer->line_sz = malidp_read32(reg, BLK_MAX_LINE_SIZE); + layer->yuv_line_sz = L_INFO_YUV_MAX_LINESZ(layer_info); + } else if (d71->max_line_size > 2048) { + /* D71 4K */ + layer->line_sz = d71->max_line_size; + layer->yuv_line_sz = layer->line_sz / 2; + } else { + /* D71 2K */ + if (layer->layer_type == KOMEDA_FMT_RICH_LAYER) { + /* rich layer is 4K configuration */ + layer->line_sz = d71->max_line_size * 2; + layer->yuv_line_sz = layer->line_sz / 2; + } else { + layer->line_sz = d71->max_line_size; + layer->yuv_line_sz = 0; + } + } + + set_range(&layer->hsize_in, 4, layer->line_sz); + + set_range(&layer->vsize_in, 4, d71->max_vsize); + + malidp_write32(reg, LAYER_PALPHA, D71_PALPHA_DEF_MAP); + + layer->supported_rots = DRM_MODE_ROTATE_MASK | DRM_MODE_REFLECT_MASK; + + return 0; +} + +static void d71_wb_layer_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_layer_state *st = to_layer_st(state); + struct drm_connector_state *conn_st = state->wb_conn->state; + struct komeda_fb *kfb = to_kfb(conn_st->writeback_job->fb); + u32 ctrl = L_EN | LW_OFM, mask = L_EN | LW_OFM | LW_TBU_EN; + u32 __iomem *reg = c->reg; + + d71_layer_update_fb(c, kfb, st->addr); + + if (kfb->is_va) + ctrl |= LW_TBU_EN; + + malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize)); + malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(state, 0)); + malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl); +} + +static void d71_wb_layer_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v[12], i; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, 0x80, 1, v); + seq_printf(sf, "LW_INPUT_ID0:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, 0xD0, 3, v); + seq_printf(sf, "LW_CONTROL:\t\t0x%X\n", v[0]); + seq_printf(sf, "LW_PROG_LINE:\t\t0x%X\n", v[1]); + seq_printf(sf, "LW_FORMAT:\t\t0x%X\n", v[2]); + + get_values_from_reg(c->reg, 0xE0, 1, v); + seq_printf(sf, "LW_IN_SIZE:\t\t0x%X\n", v[0]); + + for (i = 0; i < 2; i++) { + get_values_from_reg(c->reg, 0x100 + i * 0x10, 3, v); + seq_printf(sf, "LW_P%u_PTR_LOW:\t\t0x%X\n", i, v[0]); + seq_printf(sf, "LW_P%u_PTR_HIGH:\t\t0x%X\n", i, v[1]); + seq_printf(sf, "LW_P%u_STRIDE:\t\t0x%X\n", i, v[2]); + } + + get_values_from_reg(c->reg, 0x130, 12, v); + for (i = 0; i < 12; i++) + seq_printf(sf, "LW_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]); +} + +static void d71_wb_layer_disable(struct komeda_component *c) +{ + malidp_write32(c->reg, BLK_INPUT_ID0, 0); + malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0); +} + +static const struct komeda_component_funcs d71_wb_layer_funcs = { + .update = d71_wb_layer_update, + .disable = d71_wb_layer_disable, + .dump_register = d71_wb_layer_dump, +}; + +static int d71_wb_layer_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_layer *wb_layer; + u32 pipe_id, layer_id; + + get_resources_id(blk->block_info, &pipe_id, &layer_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*wb_layer), + layer_id, BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_wb_layer_funcs, + 1, get_valid_inputs(blk), 0, reg, + "LPU%d_LAYER_WR", pipe_id); + if (IS_ERR(c)) { + DRM_ERROR("Failed to add wb_layer component\n"); + return PTR_ERR(c); + } + + wb_layer = to_layer(c); + wb_layer->layer_type = KOMEDA_FMT_WB_LAYER; + wb_layer->line_sz = get_blk_line_size(d71, reg); + wb_layer->yuv_line_sz = wb_layer->line_sz; + + set_range(&wb_layer->hsize_in, 64, wb_layer->line_sz); + set_range(&wb_layer->vsize_in, 64, d71->max_vsize); + + return 0; +} + +static void d71_component_disable(struct komeda_component *c) +{ + u32 __iomem *reg = c->reg; + u32 i; + + malidp_write32(reg, BLK_CONTROL, 0); + + for (i = 0; i < c->max_active_inputs; i++) { + malidp_write32(reg, BLK_INPUT_ID0 + (i << 2), 0); + + /* Besides clearing the input ID to zero, D71 compiz also has + * input enable bit in CU_INPUTx_CONTROL which need to be + * cleared. + */ + if (has_bit(c->id, KOMEDA_PIPELINE_COMPIZS)) + malidp_write32(reg, CU_INPUT0_CONTROL + + i * CU_PER_INPUT_REGS * 4, + CU_INPUT_CTRL_ALPHA(0xFF)); + } +} + +static void compiz_enable_input(u32 __iomem *id_reg, + u32 __iomem *cfg_reg, + u32 input_hw_id, + struct komeda_compiz_input_cfg *cin) +{ + u32 ctrl = CU_INPUT_CTRL_EN; + u8 blend = cin->pixel_blend_mode; + + if (blend == DRM_MODE_BLEND_PIXEL_NONE) + ctrl |= CU_INPUT_CTRL_PAD; + else if (blend == DRM_MODE_BLEND_PREMULTI) + ctrl |= CU_INPUT_CTRL_PMUL; + + ctrl |= CU_INPUT_CTRL_ALPHA(cin->layer_alpha); + + malidp_write32(id_reg, BLK_INPUT_ID0, input_hw_id); + + malidp_write32(cfg_reg, CU_INPUT0_SIZE, + HV_SIZE(cin->hsize, cin->vsize)); + malidp_write32(cfg_reg, CU_INPUT0_OFFSET, + HV_OFFSET(cin->hoffset, cin->voffset)); + malidp_write32(cfg_reg, CU_INPUT0_CONTROL, ctrl); +} + +static void d71_compiz_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_compiz_state *st = to_compiz_st(state); + u32 __iomem *reg = c->reg; + u32 __iomem *id_reg, *cfg_reg; + u32 index; + + for_each_changed_input(state, index) { + id_reg = reg + index; + cfg_reg = reg + index * CU_PER_INPUT_REGS; + if (state->active_inputs & BIT(index)) { + compiz_enable_input(id_reg, cfg_reg, + to_d71_input_id(state, index), + &st->cins[index]); + } else { + malidp_write32(id_reg, BLK_INPUT_ID0, 0); + malidp_write32(cfg_reg, CU_INPUT0_CONTROL, 0); + } + } + + malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize)); +} + +static void d71_compiz_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v[8], i; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, 0x80, 5, v); + for (i = 0; i < 5; i++) + seq_printf(sf, "CU_INPUT_ID%u:\t\t0x%X\n", i, v[i]); + + get_values_from_reg(c->reg, 0xA0, 5, v); + seq_printf(sf, "CU_IRQ_RAW_STATUS:\t0x%X\n", v[0]); + seq_printf(sf, "CU_IRQ_CLEAR:\t\t0x%X\n", v[1]); + seq_printf(sf, "CU_IRQ_MASK:\t\t0x%X\n", v[2]); + seq_printf(sf, "CU_IRQ_STATUS:\t\t0x%X\n", v[3]); + seq_printf(sf, "CU_STATUS:\t\t0x%X\n", v[4]); + + get_values_from_reg(c->reg, 0xD0, 2, v); + seq_printf(sf, "CU_CONTROL:\t\t0x%X\n", v[0]); + seq_printf(sf, "CU_SIZE:\t\t0x%X\n", v[1]); + + get_values_from_reg(c->reg, 0xDC, 1, v); + seq_printf(sf, "CU_BG_COLOR:\t\t0x%X\n", v[0]); + + for (i = 0, v[4] = 0xE0; i < 5; i++, v[4] += 0x10) { + get_values_from_reg(c->reg, v[4], 3, v); + seq_printf(sf, "CU_INPUT%u_SIZE:\t\t0x%X\n", i, v[0]); + seq_printf(sf, "CU_INPUT%u_OFFSET:\t0x%X\n", i, v[1]); + seq_printf(sf, "CU_INPUT%u_CONTROL:\t0x%X\n", i, v[2]); + } + + get_values_from_reg(c->reg, 0x130, 2, v); + seq_printf(sf, "CU_USER_LOW:\t\t0x%X\n", v[0]); + seq_printf(sf, "CU_USER_HIGH:\t\t0x%X\n", v[1]); +} + +static const struct komeda_component_funcs d71_compiz_funcs = { + .update = d71_compiz_update, + .disable = d71_component_disable, + .dump_register = d71_compiz_dump, +}; + +static int d71_compiz_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_compiz *compiz; + u32 pipe_id, comp_id; + + get_resources_id(blk->block_info, &pipe_id, &comp_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*compiz), + comp_id, + BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_compiz_funcs, + CU_NUM_INPUT_IDS, get_valid_inputs(blk), + CU_NUM_OUTPUT_IDS, reg, + "CU%d", pipe_id); + if (IS_ERR(c)) + return PTR_ERR(c); + + compiz = to_compiz(c); + + set_range(&compiz->hsize, 64, get_blk_line_size(d71, reg)); + set_range(&compiz->vsize, 64, d71->max_vsize); + + return 0; +} + +static void d71_scaler_update_filter_lut(u32 __iomem *reg, u32 hsize_in, + u32 vsize_in, u32 hsize_out, + u32 vsize_out) +{ + u32 val = 0; + + if (hsize_in <= hsize_out) + val |= 0x62; + else if (hsize_in <= (hsize_out + hsize_out / 2)) + val |= 0x63; + else if (hsize_in <= hsize_out * 2) + val |= 0x64; + else if (hsize_in <= hsize_out * 2 + (hsize_out * 3) / 4) + val |= 0x65; + else + val |= 0x66; + + if (vsize_in <= vsize_out) + val |= SC_VTSEL(0x6A); + else if (vsize_in <= (vsize_out + vsize_out / 2)) + val |= SC_VTSEL(0x6B); + else if (vsize_in <= vsize_out * 2) + val |= SC_VTSEL(0x6C); + else if (vsize_in <= vsize_out * 2 + vsize_out * 3 / 4) + val |= SC_VTSEL(0x6D); + else + val |= SC_VTSEL(0x6E); + + malidp_write32(reg, SC_COEFFTAB, val); +} + +static void d71_scaler_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_scaler_state *st = to_scaler_st(state); + u32 __iomem *reg = c->reg; + u32 init_ph, delta_ph, ctrl; + + d71_scaler_update_filter_lut(reg, st->hsize_in, st->vsize_in, + st->hsize_out, st->vsize_out); + + malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize_in, st->vsize_in)); + malidp_write32(reg, SC_OUT_SIZE, HV_SIZE(st->hsize_out, st->vsize_out)); + malidp_write32(reg, SC_H_CROP, HV_CROP(st->left_crop, st->right_crop)); + + /* for right part, HW only sample the valid pixel which means the pixels + * in left_crop will be jumpped, and the first sample pixel is: + * + * dst_a = st->total_hsize_out - st->hsize_out + st->left_crop + 0.5; + * + * Then the corresponding texel in src is: + * + * h_delta_phase = st->total_hsize_in / st->total_hsize_out; + * src_a = dst_A * h_delta_phase; + * + * and h_init_phase is src_a deduct the real source start src_S; + * + * src_S = st->total_hsize_in - st->hsize_in; + * h_init_phase = src_a - src_S; + * + * And HW precision for the initial/delta_phase is 16:16 fixed point, + * the following is the simplified formula + */ + if (st->right_part) { + u32 dst_a = st->total_hsize_out - st->hsize_out + st->left_crop; + + if (st->en_img_enhancement) + dst_a -= 1; + + init_ph = ((st->total_hsize_in * (2 * dst_a + 1) - + 2 * st->total_hsize_out * (st->total_hsize_in - + st->hsize_in)) << 15) / st->total_hsize_out; + } else { + init_ph = (st->total_hsize_in << 15) / st->total_hsize_out; + } + + malidp_write32(reg, SC_H_INIT_PH, init_ph); + + delta_ph = (st->total_hsize_in << 16) / st->total_hsize_out; + malidp_write32(reg, SC_H_DELTA_PH, delta_ph); + + init_ph = (st->total_vsize_in << 15) / st->vsize_out; + malidp_write32(reg, SC_V_INIT_PH, init_ph); + + delta_ph = (st->total_vsize_in << 16) / st->vsize_out; + malidp_write32(reg, SC_V_DELTA_PH, delta_ph); + + ctrl = 0; + ctrl |= st->en_scaling ? SC_CTRL_SCL : 0; + ctrl |= st->en_alpha ? SC_CTRL_AP : 0; + ctrl |= st->en_img_enhancement ? SC_CTRL_IENH : 0; + /* If we use the hardware splitter we shouldn't set SC_CTRL_LS */ + if (st->en_split && + state->inputs[0].component->id != KOMEDA_COMPONENT_SPLITTER) + ctrl |= SC_CTRL_LS; + + malidp_write32(reg, BLK_CONTROL, ctrl); + malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(state, 0)); +} + +static void d71_scaler_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v[10]; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, 0x80, 1, v); + seq_printf(sf, "SC_INPUT_ID0:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, 0xD0, 1, v); + seq_printf(sf, "SC_CONTROL:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, 0xDC, 9, v); + seq_printf(sf, "SC_COEFFTAB:\t\t0x%X\n", v[0]); + seq_printf(sf, "SC_IN_SIZE:\t\t0x%X\n", v[1]); + seq_printf(sf, "SC_OUT_SIZE:\t\t0x%X\n", v[2]); + seq_printf(sf, "SC_H_CROP:\t\t0x%X\n", v[3]); + seq_printf(sf, "SC_V_CROP:\t\t0x%X\n", v[4]); + seq_printf(sf, "SC_H_INIT_PH:\t\t0x%X\n", v[5]); + seq_printf(sf, "SC_H_DELTA_PH:\t\t0x%X\n", v[6]); + seq_printf(sf, "SC_V_INIT_PH:\t\t0x%X\n", v[7]); + seq_printf(sf, "SC_V_DELTA_PH:\t\t0x%X\n", v[8]); + + get_values_from_reg(c->reg, 0x130, 10, v); + seq_printf(sf, "SC_ENH_LIMITS:\t\t0x%X\n", v[0]); + seq_printf(sf, "SC_ENH_COEFF0:\t\t0x%X\n", v[1]); + seq_printf(sf, "SC_ENH_COEFF1:\t\t0x%X\n", v[2]); + seq_printf(sf, "SC_ENH_COEFF2:\t\t0x%X\n", v[3]); + seq_printf(sf, "SC_ENH_COEFF3:\t\t0x%X\n", v[4]); + seq_printf(sf, "SC_ENH_COEFF4:\t\t0x%X\n", v[5]); + seq_printf(sf, "SC_ENH_COEFF5:\t\t0x%X\n", v[6]); + seq_printf(sf, "SC_ENH_COEFF6:\t\t0x%X\n", v[7]); + seq_printf(sf, "SC_ENH_COEFF7:\t\t0x%X\n", v[8]); + seq_printf(sf, "SC_ENH_COEFF8:\t\t0x%X\n", v[9]); +} + +static const struct komeda_component_funcs d71_scaler_funcs = { + .update = d71_scaler_update, + .disable = d71_component_disable, + .dump_register = d71_scaler_dump, +}; + +static int d71_scaler_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_scaler *scaler; + u32 pipe_id, comp_id; + + get_resources_id(blk->block_info, &pipe_id, &comp_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*scaler), + comp_id, BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_scaler_funcs, + 1, get_valid_inputs(blk), 1, reg, + "CU%d_SCALER%d", + pipe_id, BLOCK_INFO_BLK_ID(blk->block_info)); + + if (IS_ERR(c)) { + DRM_ERROR("Failed to initialize scaler"); + return PTR_ERR(c); + } + + scaler = to_scaler(c); + set_range(&scaler->hsize, 4, __get_blk_line_size(d71, reg, 2048)); + set_range(&scaler->vsize, 4, 4096); + scaler->max_downscaling = 6; + scaler->max_upscaling = 64; + scaler->scaling_split_overlap = 8; + scaler->enh_split_overlap = 1; + + malidp_write32(c->reg, BLK_CONTROL, 0); + + return 0; +} + +static int d71_downscaling_clk_check(struct komeda_pipeline *pipe, + struct drm_display_mode *mode, + unsigned long aclk_rate, + struct komeda_data_flow_cfg *dflow) +{ + u32 h_in = dflow->in_w; + u32 v_in = dflow->in_h; + u32 v_out = dflow->out_h; + u64 fraction, denominator; + + /* D71 downscaling must satisfy the following equation + * + * ACLK h_in * v_in + * ------- >= --------------------------------------------- + * PXLCLK (h_total - (1 + 2 * v_in / v_out)) * v_out + * + * In only horizontal downscaling situation, the right side should be + * multiplied by (h_total - 3) / (h_active - 3), then equation becomes + * + * ACLK h_in + * ------- >= ---------------- + * PXLCLK (h_active - 3) + * + * To avoid precision lost the equation 1 will be convert to: + * + * ACLK h_in * v_in + * ------- >= ----------------------------------- + * PXLCLK (h_total -1 ) * v_out - 2 * v_in + */ + if (v_in == v_out) { + fraction = h_in; + denominator = mode->hdisplay - 3; + } else { + fraction = h_in * v_in; + denominator = (mode->htotal - 1) * v_out - 2 * v_in; + } + + return aclk_rate * denominator >= mode->crtc_clock * 1000 * fraction ? + 0 : -EINVAL; +} + +static void d71_splitter_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_splitter_state *st = to_splitter_st(state); + u32 __iomem *reg = c->reg; + + malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(state, 0)); + malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize)); + malidp_write32(reg, SP_OVERLAP_SIZE, st->overlap & 0x1FFF); + malidp_write32(reg, BLK_CONTROL, BLK_CTRL_EN); +} + +static void d71_splitter_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v[3]; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, BLK_INPUT_ID0, 1, v); + seq_printf(sf, "SP_INPUT_ID0:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, BLK_CONTROL, 3, v); + seq_printf(sf, "SP_CONTROL:\t\t0x%X\n", v[0]); + seq_printf(sf, "SP_SIZE:\t\t0x%X\n", v[1]); + seq_printf(sf, "SP_OVERLAP_SIZE:\t0x%X\n", v[2]); +} + +static const struct komeda_component_funcs d71_splitter_funcs = { + .update = d71_splitter_update, + .disable = d71_component_disable, + .dump_register = d71_splitter_dump, +}; + +static int d71_splitter_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_splitter *splitter; + u32 pipe_id, comp_id; + + get_resources_id(blk->block_info, &pipe_id, &comp_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*splitter), + comp_id, + BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_splitter_funcs, + 1, get_valid_inputs(blk), 2, reg, + "CU%d_SPLITTER", pipe_id); + + if (IS_ERR(c)) { + DRM_ERROR("Failed to initialize splitter"); + return -1; + } + + splitter = to_splitter(c); + + set_range(&splitter->hsize, 4, get_blk_line_size(d71, reg)); + set_range(&splitter->vsize, 4, d71->max_vsize); + + return 0; +} + +static void d71_merger_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_merger_state *st = to_merger_st(state); + u32 __iomem *reg = c->reg; + u32 index; + + for_each_changed_input(state, index) + malidp_write32(reg, MG_INPUT_ID0 + index * 4, + to_d71_input_id(state, index)); + + malidp_write32(reg, MG_SIZE, HV_SIZE(st->hsize_merged, + st->vsize_merged)); + malidp_write32(reg, BLK_CONTROL, BLK_CTRL_EN); +} + +static void d71_merger_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, MG_INPUT_ID0, 1, &v); + seq_printf(sf, "MG_INPUT_ID0:\t\t0x%X\n", v); + + get_values_from_reg(c->reg, MG_INPUT_ID1, 1, &v); + seq_printf(sf, "MG_INPUT_ID1:\t\t0x%X\n", v); + + get_values_from_reg(c->reg, BLK_CONTROL, 1, &v); + seq_printf(sf, "MG_CONTROL:\t\t0x%X\n", v); + + get_values_from_reg(c->reg, MG_SIZE, 1, &v); + seq_printf(sf, "MG_SIZE:\t\t0x%X\n", v); +} + +static const struct komeda_component_funcs d71_merger_funcs = { + .update = d71_merger_update, + .disable = d71_component_disable, + .dump_register = d71_merger_dump, +}; + +static int d71_merger_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_merger *merger; + u32 pipe_id, comp_id; + + get_resources_id(blk->block_info, &pipe_id, &comp_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*merger), + comp_id, + BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_merger_funcs, + MG_NUM_INPUTS_IDS, get_valid_inputs(blk), + MG_NUM_OUTPUTS_IDS, reg, + "CU%d_MERGER", pipe_id); + + if (IS_ERR(c)) { + DRM_ERROR("Failed to initialize merger.\n"); + return PTR_ERR(c); + } + + merger = to_merger(c); + + set_range(&merger->hsize_merged, 4, + __get_blk_line_size(d71, reg, 4032)); + set_range(&merger->vsize_merged, 4, 4096); + + return 0; +} + +static void d71_improc_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct drm_crtc_state *crtc_st = state->crtc->state; + struct komeda_improc_state *st = to_improc_st(state); + struct d71_pipeline *pipe = to_d71_pipeline(c->pipeline); + u32 __iomem *reg = c->reg; + u32 index, mask = 0, ctrl = 0; + + for_each_changed_input(state, index) + malidp_write32(reg, BLK_INPUT_ID0 + index * 4, + to_d71_input_id(state, index)); + + malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize)); + malidp_write32(reg, IPS_DEPTH, st->color_depth); + + if (crtc_st->color_mgmt_changed) { + mask |= IPS_CTRL_FT | IPS_CTRL_RGB; + + if (crtc_st->gamma_lut) { + malidp_write_group(pipe->dou_ft_coeff_addr, FT_COEFF0, + KOMEDA_N_GAMMA_COEFFS, + st->fgamma_coeffs); + ctrl |= IPS_CTRL_FT; /* enable gamma */ + } + + if (crtc_st->ctm) { + malidp_write_group(reg, IPS_RGB_RGB_COEFF0, + KOMEDA_N_CTM_COEFFS, + st->ctm_coeffs); + ctrl |= IPS_CTRL_RGB; /* enable gamut */ + } + } + + mask |= IPS_CTRL_YUV | IPS_CTRL_CHD422 | IPS_CTRL_CHD420; + + /* config color format */ + if (st->color_format == DRM_COLOR_FORMAT_YCBCR420) + ctrl |= IPS_CTRL_YUV | IPS_CTRL_CHD422 | IPS_CTRL_CHD420; + else if (st->color_format == DRM_COLOR_FORMAT_YCBCR422) + ctrl |= IPS_CTRL_YUV | IPS_CTRL_CHD422; + else if (st->color_format == DRM_COLOR_FORMAT_YCBCR444) + ctrl |= IPS_CTRL_YUV; + + malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl); +} + +static void d71_improc_dump(struct komeda_component *c, struct seq_file *sf) +{ + u32 v[12], i; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, 0x80, 2, v); + seq_printf(sf, "IPS_INPUT_ID0:\t\t0x%X\n", v[0]); + seq_printf(sf, "IPS_INPUT_ID1:\t\t0x%X\n", v[1]); + + get_values_from_reg(c->reg, 0xC0, 1, v); + seq_printf(sf, "IPS_INFO:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, 0xD0, 3, v); + seq_printf(sf, "IPS_CONTROL:\t\t0x%X\n", v[0]); + seq_printf(sf, "IPS_SIZE:\t\t0x%X\n", v[1]); + seq_printf(sf, "IPS_DEPTH:\t\t0x%X\n", v[2]); + + get_values_from_reg(c->reg, 0x130, 12, v); + for (i = 0; i < 12; i++) + seq_printf(sf, "IPS_RGB_RGB_COEFF%u:\t0x%X\n", i, v[i]); + + get_values_from_reg(c->reg, 0x170, 12, v); + for (i = 0; i < 12; i++) + seq_printf(sf, "IPS_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]); +} + +static const struct komeda_component_funcs d71_improc_funcs = { + .update = d71_improc_update, + .disable = d71_component_disable, + .dump_register = d71_improc_dump, +}; + +static int d71_improc_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_improc *improc; + u32 pipe_id, comp_id, value; + + get_resources_id(blk->block_info, &pipe_id, &comp_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*improc), + comp_id, + BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_improc_funcs, IPS_NUM_INPUT_IDS, + get_valid_inputs(blk), + IPS_NUM_OUTPUT_IDS, reg, "DOU%d_IPS", pipe_id); + if (IS_ERR(c)) { + DRM_ERROR("Failed to add improc component\n"); + return PTR_ERR(c); + } + + improc = to_improc(c); + improc->supported_color_depths = BIT(8) | BIT(10); + improc->supported_color_formats = DRM_COLOR_FORMAT_RGB444 | + DRM_COLOR_FORMAT_YCBCR444 | + DRM_COLOR_FORMAT_YCBCR422; + value = malidp_read32(reg, BLK_INFO); + if (value & IPS_INFO_CHD420) + improc->supported_color_formats |= DRM_COLOR_FORMAT_YCBCR420; + + improc->supports_csc = true; + improc->supports_gamma = true; + + return 0; +} + +static void d71_timing_ctrlr_disable(struct komeda_component *c) +{ + malidp_write32_mask(c->reg, BLK_CONTROL, BS_CTRL_EN, 0); +} + +static void d71_timing_ctrlr_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct drm_crtc_state *crtc_st = state->crtc->state; + struct drm_display_mode *mode = &crtc_st->adjusted_mode; + u32 __iomem *reg = c->reg; + u32 hactive, hfront_porch, hback_porch, hsync_len; + u32 vactive, vfront_porch, vback_porch, vsync_len; + u32 value; + + hactive = mode->crtc_hdisplay; + hfront_porch = mode->crtc_hsync_start - mode->crtc_hdisplay; + hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + hback_porch = mode->crtc_htotal - mode->crtc_hsync_end; + + vactive = mode->crtc_vdisplay; + vfront_porch = mode->crtc_vsync_start - mode->crtc_vdisplay; + vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + vback_porch = mode->crtc_vtotal - mode->crtc_vsync_end; + + malidp_write32(reg, BS_ACTIVESIZE, HV_SIZE(hactive, vactive)); + malidp_write32(reg, BS_HINTERVALS, BS_H_INTVALS(hfront_porch, + hback_porch)); + malidp_write32(reg, BS_VINTERVALS, BS_V_INTVALS(vfront_porch, + vback_porch)); + + value = BS_SYNC_VSW(vsync_len) | BS_SYNC_HSW(hsync_len); + value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? BS_SYNC_VSP : 0; + value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? BS_SYNC_HSP : 0; + malidp_write32(reg, BS_SYNC, value); + + malidp_write32(reg, BS_PROG_LINE, D71_DEFAULT_PREPRETCH_LINE - 1); + malidp_write32(reg, BS_PREFETCH_LINE, D71_DEFAULT_PREPRETCH_LINE); + + /* configure bs control register */ + value = BS_CTRL_EN | BS_CTRL_VM; + if (c->pipeline->dual_link) { + malidp_write32(reg, BS_DRIFT_TO, hfront_porch + 16); + value |= BS_CTRL_DL; + } + + malidp_write32(reg, BLK_CONTROL, value); +} + +static void d71_timing_ctrlr_dump(struct komeda_component *c, + struct seq_file *sf) +{ + u32 v[8], i; + + dump_block_header(sf, c->reg); + + get_values_from_reg(c->reg, 0xC0, 1, v); + seq_printf(sf, "BS_INFO:\t\t0x%X\n", v[0]); + + get_values_from_reg(c->reg, 0xD0, 8, v); + seq_printf(sf, "BS_CONTROL:\t\t0x%X\n", v[0]); + seq_printf(sf, "BS_PROG_LINE:\t\t0x%X\n", v[1]); + seq_printf(sf, "BS_PREFETCH_LINE:\t0x%X\n", v[2]); + seq_printf(sf, "BS_BG_COLOR:\t\t0x%X\n", v[3]); + seq_printf(sf, "BS_ACTIVESIZE:\t\t0x%X\n", v[4]); + seq_printf(sf, "BS_HINTERVALS:\t\t0x%X\n", v[5]); + seq_printf(sf, "BS_VINTERVALS:\t\t0x%X\n", v[6]); + seq_printf(sf, "BS_SYNC:\t\t0x%X\n", v[7]); + + get_values_from_reg(c->reg, 0x100, 3, v); + seq_printf(sf, "BS_DRIFT_TO:\t\t0x%X\n", v[0]); + seq_printf(sf, "BS_FRAME_TO:\t\t0x%X\n", v[1]); + seq_printf(sf, "BS_TE_TO:\t\t0x%X\n", v[2]); + + get_values_from_reg(c->reg, 0x110, 3, v); + for (i = 0; i < 3; i++) + seq_printf(sf, "BS_T%u_INTERVAL:\t\t0x%X\n", i, v[i]); + + get_values_from_reg(c->reg, 0x120, 5, v); + for (i = 0; i < 2; i++) { + seq_printf(sf, "BS_CRC%u_LOW:\t\t0x%X\n", i, v[i << 1]); + seq_printf(sf, "BS_CRC%u_HIGH:\t\t0x%X\n", i, v[(i << 1) + 1]); + } + seq_printf(sf, "BS_USER:\t\t0x%X\n", v[4]); +} + +static const struct komeda_component_funcs d71_timing_ctrlr_funcs = { + .update = d71_timing_ctrlr_update, + .disable = d71_timing_ctrlr_disable, + .dump_register = d71_timing_ctrlr_dump, +}; + +static int d71_timing_ctrlr_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct komeda_component *c; + struct komeda_timing_ctrlr *ctrlr; + u32 pipe_id, comp_id; + + get_resources_id(blk->block_info, &pipe_id, &comp_id); + + c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*ctrlr), + KOMEDA_COMPONENT_TIMING_CTRLR, + BLOCK_INFO_INPUT_ID(blk->block_info), + &d71_timing_ctrlr_funcs, + 1, BIT(KOMEDA_COMPONENT_IPS0 + pipe_id), + BS_NUM_OUTPUT_IDS, reg, "DOU%d_BS", pipe_id); + if (IS_ERR(c)) { + DRM_ERROR("Failed to add display_ctrl component\n"); + return PTR_ERR(c); + } + + ctrlr = to_ctrlr(c); + + ctrlr->supports_dual_link = d71->supports_dual_link; + + return 0; +} + +int d71_probe_block(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + struct d71_pipeline *pipe; + int blk_id = BLOCK_INFO_BLK_ID(blk->block_info); + + int err = 0; + + switch (BLOCK_INFO_BLK_TYPE(blk->block_info)) { + case D71_BLK_TYPE_GCU: + break; + + case D71_BLK_TYPE_LPU: + pipe = d71->pipes[blk_id]; + pipe->lpu_addr = reg; + break; + + case D71_BLK_TYPE_LPU_LAYER: + err = d71_layer_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_LPU_WB_LAYER: + err = d71_wb_layer_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_CU: + pipe = d71->pipes[blk_id]; + pipe->cu_addr = reg; + err = d71_compiz_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_CU_SCALER: + err = d71_scaler_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_CU_SPLITTER: + err = d71_splitter_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_CU_MERGER: + err = d71_merger_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_DOU: + pipe = d71->pipes[blk_id]; + pipe->dou_addr = reg; + break; + + case D71_BLK_TYPE_DOU_IPS: + err = d71_improc_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_DOU_FT_COEFF: + pipe = d71->pipes[blk_id]; + pipe->dou_ft_coeff_addr = reg; + break; + + case D71_BLK_TYPE_DOU_BS: + err = d71_timing_ctrlr_init(d71, blk, reg); + break; + + case D71_BLK_TYPE_GLB_LT_COEFF: + break; + + case D71_BLK_TYPE_GLB_SCL_COEFF: + d71->glb_scl_coeff_addr[blk_id] = reg; + break; + + default: + DRM_ERROR("Unknown block (block_info: 0x%x) is found\n", + blk->block_info); + err = -EINVAL; + break; + } + + return err; +} + +static void d71_gcu_dump(struct d71_dev *d71, struct seq_file *sf) +{ + u32 v[5]; + + seq_puts(sf, "\n------ GCU ------\n"); + + get_values_from_reg(d71->gcu_addr, 0, 3, v); + seq_printf(sf, "GLB_ARCH_ID:\t\t0x%X\n", v[0]); + seq_printf(sf, "GLB_CORE_ID:\t\t0x%X\n", v[1]); + seq_printf(sf, "GLB_CORE_INFO:\t\t0x%X\n", v[2]); + + get_values_from_reg(d71->gcu_addr, 0x10, 1, v); + seq_printf(sf, "GLB_IRQ_STATUS:\t\t0x%X\n", v[0]); + + get_values_from_reg(d71->gcu_addr, 0xA0, 5, v); + seq_printf(sf, "GCU_IRQ_RAW_STATUS:\t0x%X\n", v[0]); + seq_printf(sf, "GCU_IRQ_CLEAR:\t\t0x%X\n", v[1]); + seq_printf(sf, "GCU_IRQ_MASK:\t\t0x%X\n", v[2]); + seq_printf(sf, "GCU_IRQ_STATUS:\t\t0x%X\n", v[3]); + seq_printf(sf, "GCU_STATUS:\t\t0x%X\n", v[4]); + + get_values_from_reg(d71->gcu_addr, 0xD0, 3, v); + seq_printf(sf, "GCU_CONTROL:\t\t0x%X\n", v[0]); + seq_printf(sf, "GCU_CONFIG_VALID0:\t0x%X\n", v[1]); + seq_printf(sf, "GCU_CONFIG_VALID1:\t0x%X\n", v[2]); +} + +static void d71_lpu_dump(struct d71_pipeline *pipe, struct seq_file *sf) +{ + u32 v[6]; + + seq_printf(sf, "\n------ LPU%d ------\n", pipe->base.id); + + dump_block_header(sf, pipe->lpu_addr); + + get_values_from_reg(pipe->lpu_addr, 0xA0, 6, v); + seq_printf(sf, "LPU_IRQ_RAW_STATUS:\t0x%X\n", v[0]); + seq_printf(sf, "LPU_IRQ_CLEAR:\t\t0x%X\n", v[1]); + seq_printf(sf, "LPU_IRQ_MASK:\t\t0x%X\n", v[2]); + seq_printf(sf, "LPU_IRQ_STATUS:\t\t0x%X\n", v[3]); + seq_printf(sf, "LPU_STATUS:\t\t0x%X\n", v[4]); + seq_printf(sf, "LPU_TBU_STATUS:\t\t0x%X\n", v[5]); + + get_values_from_reg(pipe->lpu_addr, 0xC0, 1, v); + seq_printf(sf, "LPU_INFO:\t\t0x%X\n", v[0]); + + get_values_from_reg(pipe->lpu_addr, 0xD0, 3, v); + seq_printf(sf, "LPU_RAXI_CONTROL:\t0x%X\n", v[0]); + seq_printf(sf, "LPU_WAXI_CONTROL:\t0x%X\n", v[1]); + seq_printf(sf, "LPU_TBU_CONTROL:\t0x%X\n", v[2]); +} + +static void d71_dou_dump(struct d71_pipeline *pipe, struct seq_file *sf) +{ + u32 v[5]; + + seq_printf(sf, "\n------ DOU%d ------\n", pipe->base.id); + + dump_block_header(sf, pipe->dou_addr); + + get_values_from_reg(pipe->dou_addr, 0xA0, 5, v); + seq_printf(sf, "DOU_IRQ_RAW_STATUS:\t0x%X\n", v[0]); + seq_printf(sf, "DOU_IRQ_CLEAR:\t\t0x%X\n", v[1]); + seq_printf(sf, "DOU_IRQ_MASK:\t\t0x%X\n", v[2]); + seq_printf(sf, "DOU_IRQ_STATUS:\t\t0x%X\n", v[3]); + seq_printf(sf, "DOU_STATUS:\t\t0x%X\n", v[4]); +} + +static void d71_pipeline_dump(struct komeda_pipeline *pipe, struct seq_file *sf) +{ + struct d71_pipeline *d71_pipe = to_d71_pipeline(pipe); + + d71_lpu_dump(d71_pipe, sf); + d71_dou_dump(d71_pipe, sf); +} + +const struct komeda_pipeline_funcs d71_pipeline_funcs = { + .downscaling_clk_check = d71_downscaling_clk_check, + .dump_register = d71_pipeline_dump, +}; + +void d71_dump(struct komeda_dev *mdev, struct seq_file *sf) +{ + struct d71_dev *d71 = mdev->chip_data; + + d71_gcu_dump(d71, sf); +} diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c new file mode 100644 index 000000000..6c56f5662 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang + * + */ + +#include +#include +#include "d71_dev.h" +#include "malidp_io.h" + +static u64 get_lpu_event(struct d71_pipeline *d71_pipeline) +{ + u32 __iomem *reg = d71_pipeline->lpu_addr; + u32 status, raw_status; + u64 evts = 0ULL; + + raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS); + if (raw_status & LPU_IRQ_IBSY) + evts |= KOMEDA_EVENT_IBSY; + if (raw_status & LPU_IRQ_EOW) + evts |= KOMEDA_EVENT_EOW; + if (raw_status & LPU_IRQ_OVR) + evts |= KOMEDA_EVENT_OVR; + + if (raw_status & (LPU_IRQ_ERR | LPU_IRQ_IBSY | LPU_IRQ_OVR)) { + u32 restore = 0, tbu_status; + /* Check error of LPU status */ + status = malidp_read32(reg, BLK_STATUS); + if (status & LPU_STATUS_AXIE) { + restore |= LPU_STATUS_AXIE; + evts |= KOMEDA_ERR_AXIE; + } + if (status & LPU_STATUS_ACE0) { + restore |= LPU_STATUS_ACE0; + evts |= KOMEDA_ERR_ACE0; + } + if (status & LPU_STATUS_ACE1) { + restore |= LPU_STATUS_ACE1; + evts |= KOMEDA_ERR_ACE1; + } + if (status & LPU_STATUS_ACE2) { + restore |= LPU_STATUS_ACE2; + evts |= KOMEDA_ERR_ACE2; + } + if (status & LPU_STATUS_ACE3) { + restore |= LPU_STATUS_ACE3; + evts |= KOMEDA_ERR_ACE3; + } + if (status & LPU_STATUS_FEMPTY) { + restore |= LPU_STATUS_FEMPTY; + evts |= KOMEDA_EVENT_EMPTY; + } + if (status & LPU_STATUS_FFULL) { + restore |= LPU_STATUS_FFULL; + evts |= KOMEDA_EVENT_FULL; + } + + if (restore != 0) + malidp_write32_mask(reg, BLK_STATUS, restore, 0); + + restore = 0; + /* Check errors of TBU status */ + tbu_status = malidp_read32(reg, LPU_TBU_STATUS); + if (tbu_status & LPU_TBU_STATUS_TCF) { + restore |= LPU_TBU_STATUS_TCF; + evts |= KOMEDA_ERR_TCF; + } + if (tbu_status & LPU_TBU_STATUS_TTNG) { + restore |= LPU_TBU_STATUS_TTNG; + evts |= KOMEDA_ERR_TTNG; + } + if (tbu_status & LPU_TBU_STATUS_TITR) { + restore |= LPU_TBU_STATUS_TITR; + evts |= KOMEDA_ERR_TITR; + } + if (tbu_status & LPU_TBU_STATUS_TEMR) { + restore |= LPU_TBU_STATUS_TEMR; + evts |= KOMEDA_ERR_TEMR; + } + if (tbu_status & LPU_TBU_STATUS_TTF) { + restore |= LPU_TBU_STATUS_TTF; + evts |= KOMEDA_ERR_TTF; + } + if (restore != 0) + malidp_write32_mask(reg, LPU_TBU_STATUS, restore, 0); + } + + malidp_write32(reg, BLK_IRQ_CLEAR, raw_status); + return evts; +} + +static u64 get_cu_event(struct d71_pipeline *d71_pipeline) +{ + u32 __iomem *reg = d71_pipeline->cu_addr; + u32 status, raw_status; + u64 evts = 0ULL; + + raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS); + if (raw_status & CU_IRQ_OVR) + evts |= KOMEDA_EVENT_OVR; + + if (raw_status & (CU_IRQ_ERR | CU_IRQ_OVR)) { + status = malidp_read32(reg, BLK_STATUS) & 0x7FFFFFFF; + if (status & CU_STATUS_CPE) + evts |= KOMEDA_ERR_CPE; + if (status & CU_STATUS_ZME) + evts |= KOMEDA_ERR_ZME; + if (status & CU_STATUS_CFGE) + evts |= KOMEDA_ERR_CFGE; + if (status) + malidp_write32_mask(reg, BLK_STATUS, status, 0); + } + + malidp_write32(reg, BLK_IRQ_CLEAR, raw_status); + + return evts; +} + +static u64 get_dou_event(struct d71_pipeline *d71_pipeline) +{ + u32 __iomem *reg = d71_pipeline->dou_addr; + u32 status, raw_status; + u64 evts = 0ULL; + + raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS); + if (raw_status & DOU_IRQ_PL0) + evts |= KOMEDA_EVENT_VSYNC; + if (raw_status & DOU_IRQ_UND) + evts |= KOMEDA_EVENT_URUN; + + if (raw_status & (DOU_IRQ_ERR | DOU_IRQ_UND)) { + u32 restore = 0; + + status = malidp_read32(reg, BLK_STATUS); + if (status & DOU_STATUS_DRIFTTO) { + restore |= DOU_STATUS_DRIFTTO; + evts |= KOMEDA_ERR_DRIFTTO; + } + if (status & DOU_STATUS_FRAMETO) { + restore |= DOU_STATUS_FRAMETO; + evts |= KOMEDA_ERR_FRAMETO; + } + if (status & DOU_STATUS_TETO) { + restore |= DOU_STATUS_TETO; + evts |= KOMEDA_ERR_TETO; + } + if (status & DOU_STATUS_CSCE) { + restore |= DOU_STATUS_CSCE; + evts |= KOMEDA_ERR_CSCE; + } + + if (restore != 0) + malidp_write32_mask(reg, BLK_STATUS, restore, 0); + } + + malidp_write32(reg, BLK_IRQ_CLEAR, raw_status); + return evts; +} + +static u64 get_pipeline_event(struct d71_pipeline *d71_pipeline, u32 gcu_status) +{ + u32 evts = 0ULL; + + if (gcu_status & (GLB_IRQ_STATUS_LPU0 | GLB_IRQ_STATUS_LPU1)) + evts |= get_lpu_event(d71_pipeline); + + if (gcu_status & (GLB_IRQ_STATUS_CU0 | GLB_IRQ_STATUS_CU1)) + evts |= get_cu_event(d71_pipeline); + + if (gcu_status & (GLB_IRQ_STATUS_DOU0 | GLB_IRQ_STATUS_DOU1)) + evts |= get_dou_event(d71_pipeline); + + return evts; +} + +static irqreturn_t +d71_irq_handler(struct komeda_dev *mdev, struct komeda_events *evts) +{ + struct d71_dev *d71 = mdev->chip_data; + u32 status, gcu_status, raw_status; + + gcu_status = malidp_read32(d71->gcu_addr, GLB_IRQ_STATUS); + + if (gcu_status & GLB_IRQ_STATUS_GCU) { + raw_status = malidp_read32(d71->gcu_addr, BLK_IRQ_RAW_STATUS); + if (raw_status & GCU_IRQ_CVAL0) + evts->pipes[0] |= KOMEDA_EVENT_FLIP; + if (raw_status & GCU_IRQ_CVAL1) + evts->pipes[1] |= KOMEDA_EVENT_FLIP; + if (raw_status & GCU_IRQ_ERR) { + status = malidp_read32(d71->gcu_addr, BLK_STATUS); + if (status & GCU_STATUS_MERR) { + evts->global |= KOMEDA_ERR_MERR; + malidp_write32_mask(d71->gcu_addr, BLK_STATUS, + GCU_STATUS_MERR, 0); + } + } + + malidp_write32(d71->gcu_addr, BLK_IRQ_CLEAR, raw_status); + } + + if (gcu_status & GLB_IRQ_STATUS_PIPE0) + evts->pipes[0] |= get_pipeline_event(d71->pipes[0], gcu_status); + + if (gcu_status & GLB_IRQ_STATUS_PIPE1) + evts->pipes[1] |= get_pipeline_event(d71->pipes[1], gcu_status); + + return IRQ_RETVAL(gcu_status); +} + +#define ENABLED_GCU_IRQS (GCU_IRQ_CVAL0 | GCU_IRQ_CVAL1 | \ + GCU_IRQ_MODE | GCU_IRQ_ERR) +#define ENABLED_LPU_IRQS (LPU_IRQ_IBSY | LPU_IRQ_ERR | LPU_IRQ_EOW) +#define ENABLED_CU_IRQS (CU_IRQ_OVR | CU_IRQ_ERR) +#define ENABLED_DOU_IRQS (DOU_IRQ_UND | DOU_IRQ_ERR) + +static int d71_enable_irq(struct komeda_dev *mdev) +{ + struct d71_dev *d71 = mdev->chip_data; + struct d71_pipeline *pipe; + u32 i; + + malidp_write32_mask(d71->gcu_addr, BLK_IRQ_MASK, + ENABLED_GCU_IRQS, ENABLED_GCU_IRQS); + for (i = 0; i < d71->num_pipelines; i++) { + pipe = d71->pipes[i]; + malidp_write32_mask(pipe->cu_addr, BLK_IRQ_MASK, + ENABLED_CU_IRQS, ENABLED_CU_IRQS); + malidp_write32_mask(pipe->lpu_addr, BLK_IRQ_MASK, + ENABLED_LPU_IRQS, ENABLED_LPU_IRQS); + malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK, + ENABLED_DOU_IRQS, ENABLED_DOU_IRQS); + } + return 0; +} + +static int d71_disable_irq(struct komeda_dev *mdev) +{ + struct d71_dev *d71 = mdev->chip_data; + struct d71_pipeline *pipe; + u32 i; + + malidp_write32_mask(d71->gcu_addr, BLK_IRQ_MASK, ENABLED_GCU_IRQS, 0); + for (i = 0; i < d71->num_pipelines; i++) { + pipe = d71->pipes[i]; + malidp_write32_mask(pipe->cu_addr, BLK_IRQ_MASK, + ENABLED_CU_IRQS, 0); + malidp_write32_mask(pipe->lpu_addr, BLK_IRQ_MASK, + ENABLED_LPU_IRQS, 0); + malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK, + ENABLED_DOU_IRQS, 0); + } + return 0; +} + +static void d71_on_off_vblank(struct komeda_dev *mdev, int master_pipe, bool on) +{ + struct d71_dev *d71 = mdev->chip_data; + struct d71_pipeline *pipe = d71->pipes[master_pipe]; + + malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK, + DOU_IRQ_PL0, on ? DOU_IRQ_PL0 : 0); +} + +static int to_d71_opmode(int core_mode) +{ + switch (core_mode) { + case KOMEDA_MODE_DISP0: + return DO0_ACTIVE_MODE; + case KOMEDA_MODE_DISP1: + return DO1_ACTIVE_MODE; + case KOMEDA_MODE_DUAL_DISP: + return DO01_ACTIVE_MODE; + case KOMEDA_MODE_INACTIVE: + return INACTIVE_MODE; + default: + WARN(1, "Unknown operation mode"); + return INACTIVE_MODE; + } +} + +static int d71_change_opmode(struct komeda_dev *mdev, int new_mode) +{ + struct d71_dev *d71 = mdev->chip_data; + u32 opmode = to_d71_opmode(new_mode); + int ret; + + malidp_write32_mask(d71->gcu_addr, BLK_CONTROL, 0x7, opmode); + + ret = dp_wait_cond(((malidp_read32(d71->gcu_addr, BLK_CONTROL) & 0x7) == opmode), + 100, 1000, 10000); + + return ret; +} + +static void d71_flush(struct komeda_dev *mdev, + int master_pipe, u32 active_pipes) +{ + struct d71_dev *d71 = mdev->chip_data; + u32 reg_offset = (master_pipe == 0) ? + GCU_CONFIG_VALID0 : GCU_CONFIG_VALID1; + + malidp_write32(d71->gcu_addr, reg_offset, GCU_CONFIG_CVAL); +} + +static int d71_reset(struct d71_dev *d71) +{ + u32 __iomem *gcu = d71->gcu_addr; + int ret; + + malidp_write32(gcu, BLK_CONTROL, GCU_CONTROL_SRST); + + ret = dp_wait_cond(!(malidp_read32(gcu, BLK_CONTROL) & GCU_CONTROL_SRST), + 100, 1000, 10000); + + return ret; +} + +void d71_read_block_header(u32 __iomem *reg, struct block_header *blk) +{ + int i; + + blk->block_info = malidp_read32(reg, BLK_BLOCK_INFO); + if (BLOCK_INFO_BLK_TYPE(blk->block_info) == D71_BLK_TYPE_RESERVED) + return; + + blk->pipeline_info = malidp_read32(reg, BLK_PIPELINE_INFO); + + /* get valid input and output ids */ + for (i = 0; i < PIPELINE_INFO_N_VALID_INPUTS(blk->pipeline_info); i++) + blk->input_ids[i] = malidp_read32(reg + i, BLK_VALID_INPUT_ID0); + for (i = 0; i < PIPELINE_INFO_N_OUTPUTS(blk->pipeline_info); i++) + blk->output_ids[i] = malidp_read32(reg + i, BLK_OUTPUT_ID0); +} + +static void d71_cleanup(struct komeda_dev *mdev) +{ + struct d71_dev *d71 = mdev->chip_data; + + if (!d71) + return; + + devm_kfree(mdev->dev, d71); + mdev->chip_data = NULL; +} + +static int d71_enum_resources(struct komeda_dev *mdev) +{ + struct d71_dev *d71; + struct komeda_pipeline *pipe; + struct block_header blk; + u32 __iomem *blk_base; + u32 i, value, offset; + int err; + + d71 = devm_kzalloc(mdev->dev, sizeof(*d71), GFP_KERNEL); + if (!d71) + return -ENOMEM; + + mdev->chip_data = d71; + d71->mdev = mdev; + d71->gcu_addr = mdev->reg_base; + d71->periph_addr = mdev->reg_base + (D71_BLOCK_OFFSET_PERIPH >> 2); + + err = d71_reset(d71); + if (err) { + DRM_ERROR("Fail to reset d71 device.\n"); + goto err_cleanup; + } + + /* probe GCU */ + value = malidp_read32(d71->gcu_addr, GLB_CORE_INFO); + d71->num_blocks = value & 0xFF; + d71->num_pipelines = (value >> 8) & 0x7; + + if (d71->num_pipelines > D71_MAX_PIPELINE) { + DRM_ERROR("d71 supports %d pipelines, but got: %d.\n", + D71_MAX_PIPELINE, d71->num_pipelines); + err = -EINVAL; + goto err_cleanup; + } + + /* Only the legacy HW has the periph block, the newer merges the periph + * into GCU + */ + value = malidp_read32(d71->periph_addr, BLK_BLOCK_INFO); + if (BLOCK_INFO_BLK_TYPE(value) != D71_BLK_TYPE_PERIPH) + d71->periph_addr = NULL; + + if (d71->periph_addr) { + /* probe PERIPHERAL in legacy HW */ + value = malidp_read32(d71->periph_addr, PERIPH_CONFIGURATION_ID); + + d71->max_line_size = value & PERIPH_MAX_LINE_SIZE ? 4096 : 2048; + d71->max_vsize = 4096; + d71->num_rich_layers = value & PERIPH_NUM_RICH_LAYERS ? 2 : 1; + d71->supports_dual_link = !!(value & PERIPH_SPLIT_EN); + d71->integrates_tbu = !!(value & PERIPH_TBU_EN); + } else { + value = malidp_read32(d71->gcu_addr, GCU_CONFIGURATION_ID0); + d71->max_line_size = GCU_MAX_LINE_SIZE(value); + d71->max_vsize = GCU_MAX_NUM_LINES(value); + + value = malidp_read32(d71->gcu_addr, GCU_CONFIGURATION_ID1); + d71->num_rich_layers = GCU_NUM_RICH_LAYERS(value); + d71->supports_dual_link = GCU_DISPLAY_SPLIT_EN(value); + d71->integrates_tbu = GCU_DISPLAY_TBU_EN(value); + } + + for (i = 0; i < d71->num_pipelines; i++) { + pipe = komeda_pipeline_add(mdev, sizeof(struct d71_pipeline), + &d71_pipeline_funcs); + if (IS_ERR(pipe)) { + err = PTR_ERR(pipe); + goto err_cleanup; + } + + /* D71 HW doesn't update shadow registers when display output + * is turning off, so when we disable all pipeline components + * together with display output disable by one flush or one + * operation, the disable operation updated registers will not + * be flush to or valid in HW, which may leads problem. + * To workaround this problem, introduce a two phase disable. + * Phase1: Disabling components with display is on to make sure + * the disable can be flushed to HW. + * Phase2: Only turn-off display output. + */ + value = KOMEDA_PIPELINE_IMPROCS | + BIT(KOMEDA_COMPONENT_TIMING_CTRLR); + + pipe->standalone_disabled_comps = value; + + d71->pipes[i] = to_d71_pipeline(pipe); + } + + /* loop the register blks and probe. + * NOTE: d71->num_blocks includes reserved blocks. + * d71->num_blocks = GCU + valid blocks + reserved blocks + */ + i = 1; /* exclude GCU */ + offset = D71_BLOCK_SIZE; /* skip GCU */ + while (i < d71->num_blocks) { + blk_base = mdev->reg_base + (offset >> 2); + + d71_read_block_header(blk_base, &blk); + if (BLOCK_INFO_BLK_TYPE(blk.block_info) != D71_BLK_TYPE_RESERVED) { + err = d71_probe_block(d71, &blk, blk_base); + if (err) + goto err_cleanup; + } + + i++; + offset += D71_BLOCK_SIZE; + } + + DRM_DEBUG("total %d (out of %d) blocks are found.\n", + i, d71->num_blocks); + + return 0; + +err_cleanup: + d71_cleanup(mdev); + return err; +} + +#define __HW_ID(__group, __format) \ + ((((__group) & 0x7) << 3) | ((__format) & 0x7)) + +#define RICH KOMEDA_FMT_RICH_LAYER +#define SIMPLE KOMEDA_FMT_SIMPLE_LAYER +#define RICH_SIMPLE (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_SIMPLE_LAYER) +#define RICH_WB (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_WB_LAYER) +#define RICH_SIMPLE_WB (RICH_SIMPLE | KOMEDA_FMT_WB_LAYER) + +#define Rot_0 DRM_MODE_ROTATE_0 +#define Flip_H_V (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y | Rot_0) +#define Rot_ALL_H_V (DRM_MODE_ROTATE_MASK | Flip_H_V) + +#define LYT_NM BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16) +#define LYT_WB BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) +#define LYT_NM_WB (LYT_NM | LYT_WB) + +#define AFB_TH AFBC(_TILED | _SPARSE) +#define AFB_TH_SC_YTR AFBC(_TILED | _SC | _SPARSE | _YTR) +#define AFB_TH_SC_YTR_BS AFBC(_TILED | _SC | _SPARSE | _YTR | _SPLIT) + +static struct komeda_format_caps d71_format_caps_table[] = { + /* HW_ID | fourcc | layer_types | rots | afbc_layouts | afbc_features */ + /* ABGR_2101010*/ + {__HW_ID(0, 0), DRM_FORMAT_ARGB2101010, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ + {__HW_ID(0, 2), DRM_FORMAT_RGBA1010102, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(0, 3), DRM_FORMAT_BGRA1010102, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + /* ABGR_8888*/ + {__HW_ID(1, 0), DRM_FORMAT_ARGB8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(1, 1), DRM_FORMAT_ABGR8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(1, 1), DRM_FORMAT_ABGR8888, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ + {__HW_ID(1, 2), DRM_FORMAT_RGBA8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(1, 3), DRM_FORMAT_BGRA8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + /* XBGB_8888 */ + {__HW_ID(2, 0), DRM_FORMAT_XRGB8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(2, 1), DRM_FORMAT_XBGR8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(2, 2), DRM_FORMAT_RGBX8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + {__HW_ID(2, 3), DRM_FORMAT_BGRX8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, + /* BGR_888 */ /* none-afbc RGB888 doesn't support rotation and flip */ + {__HW_ID(3, 0), DRM_FORMAT_RGB888, RICH_SIMPLE_WB, Rot_0, 0, 0}, + {__HW_ID(3, 1), DRM_FORMAT_BGR888, RICH_SIMPLE_WB, Rot_0, 0, 0}, + {__HW_ID(3, 1), DRM_FORMAT_BGR888, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ + /* BGR 16bpp */ + {__HW_ID(4, 0), DRM_FORMAT_RGBA5551, RICH_SIMPLE, Flip_H_V, 0, 0}, + {__HW_ID(4, 1), DRM_FORMAT_ABGR1555, RICH_SIMPLE, Flip_H_V, 0, 0}, + {__HW_ID(4, 1), DRM_FORMAT_ABGR1555, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */ + {__HW_ID(4, 2), DRM_FORMAT_RGB565, RICH_SIMPLE, Flip_H_V, 0, 0}, + {__HW_ID(4, 3), DRM_FORMAT_BGR565, RICH_SIMPLE, Flip_H_V, 0, 0}, + {__HW_ID(4, 3), DRM_FORMAT_BGR565, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */ + {__HW_ID(4, 4), DRM_FORMAT_R8, SIMPLE, Rot_0, 0, 0}, + /* YUV 444/422/420 8bit */ + {__HW_ID(5, 1), DRM_FORMAT_YUYV, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ + {__HW_ID(5, 2), DRM_FORMAT_YUYV, RICH, Flip_H_V, 0, 0}, + {__HW_ID(5, 3), DRM_FORMAT_UYVY, RICH, Flip_H_V, 0, 0}, + {__HW_ID(5, 6), DRM_FORMAT_NV12, RICH, Flip_H_V, 0, 0}, + {__HW_ID(5, 6), DRM_FORMAT_YUV420_8BIT, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ + {__HW_ID(5, 7), DRM_FORMAT_YUV420, RICH, Flip_H_V, 0, 0}, + /* YUV 10bit*/ + {__HW_ID(6, 6), DRM_FORMAT_X0L2, RICH, Flip_H_V, 0, 0}, + {__HW_ID(6, 7), DRM_FORMAT_P010, RICH, Flip_H_V, 0, 0}, + {__HW_ID(6, 7), DRM_FORMAT_YUV420_10BIT, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, +}; + +static bool d71_format_mod_supported(const struct komeda_format_caps *caps, + u32 layer_type, u64 modifier, u32 rot) +{ + uint64_t layout = modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK; + + if ((layout == AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) && + drm_rotation_90_or_270(rot)) { + DRM_DEBUG_ATOMIC("D71 doesn't support ROT90 for WB-AFBC.\n"); + return false; + } + + return true; +} + +static void d71_init_fmt_tbl(struct komeda_dev *mdev) +{ + struct komeda_format_caps_table *table = &mdev->fmt_tbl; + + table->format_caps = d71_format_caps_table; + table->format_mod_supported = d71_format_mod_supported; + table->n_formats = ARRAY_SIZE(d71_format_caps_table); +} + +static int d71_connect_iommu(struct komeda_dev *mdev) +{ + struct d71_dev *d71 = mdev->chip_data; + u32 __iomem *reg = d71->gcu_addr; + u32 check_bits = (d71->num_pipelines == 2) ? + GCU_STATUS_TCS0 | GCU_STATUS_TCS1 : GCU_STATUS_TCS0; + int i, ret; + + if (!d71->integrates_tbu) + return -1; + + malidp_write32_mask(reg, BLK_CONTROL, 0x7, TBU_CONNECT_MODE); + + ret = dp_wait_cond(has_bits(check_bits, malidp_read32(reg, BLK_STATUS)), + 100, 1000, 1000); + if (ret < 0) { + DRM_ERROR("timed out connecting to TCU!\n"); + malidp_write32_mask(reg, BLK_CONTROL, 0x7, INACTIVE_MODE); + return ret; + } + + for (i = 0; i < d71->num_pipelines; i++) + malidp_write32_mask(d71->pipes[i]->lpu_addr, LPU_TBU_CONTROL, + LPU_TBU_CTRL_TLBPEN, LPU_TBU_CTRL_TLBPEN); + return 0; +} + +static int d71_disconnect_iommu(struct komeda_dev *mdev) +{ + struct d71_dev *d71 = mdev->chip_data; + u32 __iomem *reg = d71->gcu_addr; + u32 check_bits = (d71->num_pipelines == 2) ? + GCU_STATUS_TCS0 | GCU_STATUS_TCS1 : GCU_STATUS_TCS0; + int ret; + + malidp_write32_mask(reg, BLK_CONTROL, 0x7, TBU_DISCONNECT_MODE); + + ret = dp_wait_cond(((malidp_read32(reg, BLK_STATUS) & check_bits) == 0), + 100, 1000, 1000); + if (ret < 0) { + DRM_ERROR("timed out disconnecting from TCU!\n"); + malidp_write32_mask(reg, BLK_CONTROL, 0x7, INACTIVE_MODE); + } + + return ret; +} + +static const struct komeda_dev_funcs d71_chip_funcs = { + .init_format_table = d71_init_fmt_tbl, + .enum_resources = d71_enum_resources, + .cleanup = d71_cleanup, + .irq_handler = d71_irq_handler, + .enable_irq = d71_enable_irq, + .disable_irq = d71_disable_irq, + .on_off_vblank = d71_on_off_vblank, + .change_opmode = d71_change_opmode, + .flush = d71_flush, + .connect_iommu = d71_connect_iommu, + .disconnect_iommu = d71_disconnect_iommu, + .dump_register = d71_dump, +}; + +const struct komeda_dev_funcs * +d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip) +{ + const struct komeda_dev_funcs *funcs; + u32 product_id; + + chip->core_id = malidp_read32(reg_base, GLB_CORE_ID); + + product_id = MALIDP_CORE_ID_PRODUCT_ID(chip->core_id); + + switch (product_id) { + case MALIDP_D71_PRODUCT_ID: + case MALIDP_D32_PRODUCT_ID: + funcs = &d71_chip_funcs; + break; + default: + DRM_ERROR("Unsupported product: 0x%x\n", product_id); + return NULL; + } + + chip->arch_id = malidp_read32(reg_base, GLB_ARCH_ID); + chip->core_info = malidp_read32(reg_base, GLB_CORE_INFO); + chip->bus_width = D71_BUS_WIDTH_16_BYTES; + + return funcs; +} diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h new file mode 100644 index 000000000..c7357c2b9 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang + * + */ +#ifndef _D71_DEV_H_ +#define _D71_DEV_H_ + +#include "komeda_dev.h" +#include "komeda_pipeline.h" +#include "d71_regs.h" + +struct d71_pipeline { + struct komeda_pipeline base; + + /* d71 private pipeline blocks */ + u32 __iomem *lpu_addr; + u32 __iomem *cu_addr; + u32 __iomem *dou_addr; + u32 __iomem *dou_ft_coeff_addr; /* forward transform coeffs table */ +}; + +struct d71_dev { + struct komeda_dev *mdev; + + int num_blocks; + int num_pipelines; + int num_rich_layers; + u32 max_line_size; + u32 max_vsize; + u32 supports_dual_link : 1; + u32 integrates_tbu : 1; + + /* global register blocks */ + u32 __iomem *gcu_addr; + /* scaling coeffs table */ + u32 __iomem *glb_scl_coeff_addr[D71_MAX_GLB_SCL_COEFF]; + u32 __iomem *periph_addr; + + struct d71_pipeline *pipes[D71_MAX_PIPELINE]; +}; + +#define to_d71_pipeline(x) container_of(x, struct d71_pipeline, base) + +extern const struct komeda_pipeline_funcs d71_pipeline_funcs; + +int d71_probe_block(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg); +void d71_read_block_header(u32 __iomem *reg, struct block_header *blk); + +void d71_dump(struct komeda_dev *mdev, struct seq_file *sf); + +#endif /* !_D71_DEV_H_ */ diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_regs.h b/drivers/gpu/drm/arm/display/komeda/d71/d71_regs.h new file mode 100644 index 000000000..e80172a0b --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_regs.h @@ -0,0 +1,541 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang + * + */ +#ifndef _D71_REG_H_ +#define _D71_REG_H_ + +/* Common block registers offset */ +#define BLK_BLOCK_INFO 0x000 +#define BLK_PIPELINE_INFO 0x004 +#define BLK_MAX_LINE_SIZE 0x008 +#define BLK_VALID_INPUT_ID0 0x020 +#define BLK_OUTPUT_ID0 0x060 +#define BLK_INPUT_ID0 0x080 +#define BLK_IRQ_RAW_STATUS 0x0A0 +#define BLK_IRQ_CLEAR 0x0A4 +#define BLK_IRQ_MASK 0x0A8 +#define BLK_IRQ_STATUS 0x0AC +#define BLK_STATUS 0x0B0 +#define BLK_INFO 0x0C0 +#define BLK_CONTROL 0x0D0 +#define BLK_SIZE 0x0D4 +#define BLK_IN_SIZE 0x0E0 + +#define BLK_P0_PTR_LOW 0x100 +#define BLK_P0_PTR_HIGH 0x104 +#define BLK_P0_STRIDE 0x108 +#define BLK_P1_PTR_LOW 0x110 +#define BLK_P1_PTR_HIGH 0x114 +#define BLK_P1_STRIDE 0x118 +#define BLK_P2_PTR_LOW 0x120 +#define BLK_P2_PTR_HIGH 0x124 + +#define BLOCK_INFO_N_SUBBLKS(x) ((x) & 0x000F) +#define BLOCK_INFO_BLK_ID(x) (((x) & 0x00F0) >> 4) +#define BLOCK_INFO_BLK_TYPE(x) (((x) & 0xFF00) >> 8) +#define BLOCK_INFO_INPUT_ID(x) ((x) & 0xFFF0) +#define BLOCK_INFO_TYPE_ID(x) (((x) & 0x0FF0) >> 4) + +#define PIPELINE_INFO_N_OUTPUTS(x) ((x) & 0x000F) +#define PIPELINE_INFO_N_VALID_INPUTS(x) (((x) & 0x0F00) >> 8) + +/* Common block control register bits */ +#define BLK_CTRL_EN BIT(0) +/* Common size macro */ +#define HV_SIZE(h, v) (((h) & 0x1FFF) + (((v) & 0x1FFF) << 16)) +#define HV_OFFSET(h, v) (((h) & 0xFFF) + (((v) & 0xFFF) << 16)) +#define HV_CROP(h, v) (((h) & 0xFFF) + (((v) & 0xFFF) << 16)) + +/* AD_CONTROL register */ +#define AD_CONTROL 0x160 + +/* AD_CONTROL register bits */ +#define AD_AEN BIT(0) +#define AD_YT BIT(1) +#define AD_BS BIT(2) +#define AD_WB BIT(3) +#define AD_TH BIT(4) + +/* Global Control Unit */ +#define GLB_ARCH_ID 0x000 +#define GLB_CORE_ID 0x004 +#define GLB_CORE_INFO 0x008 +#define GLB_IRQ_STATUS 0x010 + +#define GCU_CONFIG_VALID0 0x0D4 +#define GCU_CONFIG_VALID1 0x0D8 + +/* GCU_CONTROL_BITS */ +#define GCU_CONTROL_MODE(x) ((x) & 0x7) +#define GCU_CONTROL_SRST BIT(16) + +/* GCU_CONFIGURATION registers */ +#define GCU_CONFIGURATION_ID0 0x100 +#define GCU_CONFIGURATION_ID1 0x104 + +/* GCU configuration */ +#define GCU_MAX_LINE_SIZE(x) ((x) & 0xFFFF) +#define GCU_MAX_NUM_LINES(x) ((x) >> 16) +#define GCU_NUM_RICH_LAYERS(x) ((x) & 0x7) +#define GCU_NUM_PIPELINES(x) (((x) >> 3) & 0x7) +#define GCU_NUM_SCALERS(x) (((x) >> 6) & 0x7) +#define GCU_DISPLAY_SPLIT_EN(x) (((x) >> 16) & 0x1) +#define GCU_DISPLAY_TBU_EN(x) (((x) >> 17) & 0x1) + +/* GCU opmode */ +#define INACTIVE_MODE 0 +#define TBU_CONNECT_MODE 1 +#define TBU_DISCONNECT_MODE 2 +#define DO0_ACTIVE_MODE 3 +#define DO1_ACTIVE_MODE 4 +#define DO01_ACTIVE_MODE 5 + +/* GLB_IRQ_STATUS bits */ +#define GLB_IRQ_STATUS_GCU BIT(0) +#define GLB_IRQ_STATUS_LPU0 BIT(8) +#define GLB_IRQ_STATUS_LPU1 BIT(9) +#define GLB_IRQ_STATUS_ATU0 BIT(10) +#define GLB_IRQ_STATUS_ATU1 BIT(11) +#define GLB_IRQ_STATUS_ATU2 BIT(12) +#define GLB_IRQ_STATUS_ATU3 BIT(13) +#define GLB_IRQ_STATUS_CU0 BIT(16) +#define GLB_IRQ_STATUS_CU1 BIT(17) +#define GLB_IRQ_STATUS_DOU0 BIT(24) +#define GLB_IRQ_STATUS_DOU1 BIT(25) + +#define GLB_IRQ_STATUS_PIPE0 (GLB_IRQ_STATUS_LPU0 |\ + GLB_IRQ_STATUS_ATU0 |\ + GLB_IRQ_STATUS_ATU1 |\ + GLB_IRQ_STATUS_CU0 |\ + GLB_IRQ_STATUS_DOU0) + +#define GLB_IRQ_STATUS_PIPE1 (GLB_IRQ_STATUS_LPU1 |\ + GLB_IRQ_STATUS_ATU2 |\ + GLB_IRQ_STATUS_ATU3 |\ + GLB_IRQ_STATUS_CU1 |\ + GLB_IRQ_STATUS_DOU1) + +#define GLB_IRQ_STATUS_ATU (GLB_IRQ_STATUS_ATU0 |\ + GLB_IRQ_STATUS_ATU1 |\ + GLB_IRQ_STATUS_ATU2 |\ + GLB_IRQ_STATUS_ATU3) + +/* GCU_IRQ_BITS */ +#define GCU_IRQ_CVAL0 BIT(0) +#define GCU_IRQ_CVAL1 BIT(1) +#define GCU_IRQ_MODE BIT(4) +#define GCU_IRQ_ERR BIT(11) + +/* GCU_STATUS_BITS */ +#define GCU_STATUS_MODE(x) ((x) & 0x7) +#define GCU_STATUS_MERR BIT(4) +#define GCU_STATUS_TCS0 BIT(8) +#define GCU_STATUS_TCS1 BIT(9) +#define GCU_STATUS_ACTIVE BIT(31) + +/* GCU_CONFIG_VALIDx BITS */ +#define GCU_CONFIG_CVAL BIT(0) + +/* PERIPHERAL registers */ +#define PERIPH_MAX_LINE_SIZE BIT(0) +#define PERIPH_NUM_RICH_LAYERS BIT(4) +#define PERIPH_SPLIT_EN BIT(8) +#define PERIPH_TBU_EN BIT(12) +#define PERIPH_AFBC_DMA_EN BIT(16) +#define PERIPH_CONFIGURATION_ID 0x1D4 + +/* LPU register */ +#define LPU_TBU_STATUS 0x0B4 +#define LPU_RAXI_CONTROL 0x0D0 +#define LPU_WAXI_CONTROL 0x0D4 +#define LPU_TBU_CONTROL 0x0D8 + +/* LPU_xAXI_CONTROL_BITS */ +#define TO_RAXI_AOUTSTDCAPB(x) (x) +#define TO_RAXI_BOUTSTDCAPB(x) ((x) << 8) +#define TO_RAXI_BEN(x) ((x) << 15) +#define TO_xAXI_BURSTLEN(x) ((x) << 16) +#define TO_xAXI_AxQOS(x) ((x) << 24) +#define TO_xAXI_ORD(x) ((x) << 31) +#define TO_WAXI_OUTSTDCAPB(x) (x) + +#define RAXI_AOUTSTDCAPB_MASK 0x7F +#define RAXI_BOUTSTDCAPB_MASK 0x7F00 +#define RAXI_BEN_MASK BIT(15) +#define xAXI_BURSTLEN_MASK 0x3F0000 +#define xAXI_AxQOS_MASK 0xF000000 +#define xAXI_ORD_MASK BIT(31) +#define WAXI_OUTSTDCAPB_MASK 0x3F + +/* LPU_TBU_CONTROL BITS */ +#define TO_TBU_DOUTSTDCAPB(x) (x) +#define TBU_DOUTSTDCAPB_MASK 0x3F + +/* LPU_IRQ_BITS */ +#define LPU_IRQ_OVR BIT(9) +#define LPU_IRQ_IBSY BIT(10) +#define LPU_IRQ_ERR BIT(11) +#define LPU_IRQ_EOW BIT(12) +#define LPU_IRQ_PL0 BIT(13) + +/* LPU_STATUS_BITS */ +#define LPU_STATUS_AXIED(x) ((x) & 0xF) +#define LPU_STATUS_AXIE BIT(4) +#define LPU_STATUS_AXIRP BIT(5) +#define LPU_STATUS_AXIWP BIT(6) +#define LPU_STATUS_FEMPTY BIT(11) +#define LPU_STATUS_FFULL BIT(14) +#define LPU_STATUS_ACE0 BIT(16) +#define LPU_STATUS_ACE1 BIT(17) +#define LPU_STATUS_ACE2 BIT(18) +#define LPU_STATUS_ACE3 BIT(19) +#define LPU_STATUS_ACTIVE BIT(31) + +#define AXIEID_MASK 0xF +#define AXIE_MASK LPU_STATUS_AXIE +#define AXIRP_MASK LPU_STATUS_AXIRP +#define AXIWP_MASK LPU_STATUS_AXIWP + +#define FROM_AXIEID(reg) ((reg) & AXIEID_MASK) +#define TO_AXIE(x) ((x) << 4) +#define FROM_AXIRP(reg) (((reg) & AXIRP_MASK) >> 5) +#define FROM_AXIWP(reg) (((reg) & AXIWP_MASK) >> 6) + +/* LPU_TBU_STATUS_BITS */ +#define LPU_TBU_STATUS_TCF BIT(1) +#define LPU_TBU_STATUS_TTNG BIT(2) +#define LPU_TBU_STATUS_TITR BIT(8) +#define LPU_TBU_STATUS_TEMR BIT(16) +#define LPU_TBU_STATUS_TTF BIT(31) + +/* LPU_TBU_CONTROL BITS */ +#define LPU_TBU_CTRL_TLBPEN BIT(16) + +/* CROSSBAR CONTROL BITS */ +#define CBU_INPUT_CTRL_EN BIT(0) +#define CBU_NUM_INPUT_IDS 5 +#define CBU_NUM_OUTPUT_IDS 5 + +/* CU register */ +#define CU_BG_COLOR 0x0DC +#define CU_INPUT0_SIZE 0x0E0 +#define CU_INPUT0_OFFSET 0x0E4 +#define CU_INPUT0_CONTROL 0x0E8 +#define CU_INPUT1_SIZE 0x0F0 +#define CU_INPUT1_OFFSET 0x0F4 +#define CU_INPUT1_CONTROL 0x0F8 +#define CU_INPUT2_SIZE 0x100 +#define CU_INPUT2_OFFSET 0x104 +#define CU_INPUT2_CONTROL 0x108 +#define CU_INPUT3_SIZE 0x110 +#define CU_INPUT3_OFFSET 0x114 +#define CU_INPUT3_CONTROL 0x118 +#define CU_INPUT4_SIZE 0x120 +#define CU_INPUT4_OFFSET 0x124 +#define CU_INPUT4_CONTROL 0x128 + +#define CU_PER_INPUT_REGS 4 + +#define CU_NUM_INPUT_IDS 5 +#define CU_NUM_OUTPUT_IDS 1 + +/* CU control register bits */ +#define CU_CTRL_COPROC BIT(0) + +/* CU_IRQ_BITS */ +#define CU_IRQ_OVR BIT(9) +#define CU_IRQ_ERR BIT(11) + +/* CU_STATUS_BITS */ +#define CU_STATUS_CPE BIT(0) +#define CU_STATUS_ZME BIT(1) +#define CU_STATUS_CFGE BIT(2) +#define CU_STATUS_ACTIVE BIT(31) + +/* CU input control register bits */ +#define CU_INPUT_CTRL_EN BIT(0) +#define CU_INPUT_CTRL_PAD BIT(1) +#define CU_INPUT_CTRL_PMUL BIT(2) +#define CU_INPUT_CTRL_ALPHA(x) (((x) & 0xFF) << 8) + +/* DOU register */ + +/* DOU_IRQ_BITS */ +#define DOU_IRQ_UND BIT(8) +#define DOU_IRQ_ERR BIT(11) +#define DOU_IRQ_PL0 BIT(13) +#define DOU_IRQ_PL1 BIT(14) + +/* DOU_STATUS_BITS */ +#define DOU_STATUS_DRIFTTO BIT(0) +#define DOU_STATUS_FRAMETO BIT(1) +#define DOU_STATUS_TETO BIT(2) +#define DOU_STATUS_CSCE BIT(8) +#define DOU_STATUS_ACTIVE BIT(31) + +/* Layer registers */ +#define LAYER_INFO 0x0C0 +#define LAYER_R_CONTROL 0x0D4 +#define LAYER_FMT 0x0D8 +#define LAYER_LT_COEFFTAB 0x0DC +#define LAYER_PALPHA 0x0E4 + +#define LAYER_YUV_RGB_COEFF0 0x130 + +#define LAYER_AD_H_CROP 0x164 +#define LAYER_AD_V_CROP 0x168 + +#define LAYER_RGB_RGB_COEFF0 0x170 + +/* L_CONTROL_BITS */ +#define L_EN BIT(0) +#define L_IT BIT(4) +#define L_R2R BIT(5) +#define L_FT BIT(6) +#define L_ROT(x) (((x) & 3) << 8) +#define L_HFLIP BIT(10) +#define L_VFLIP BIT(11) +#define L_TBU_EN BIT(16) +#define L_A_RCACHE(x) (((x) & 0xF) << 28) +#define L_ROT_R0 0 +#define L_ROT_R90 1 +#define L_ROT_R180 2 +#define L_ROT_R270 3 + +/* LAYER_R_CONTROL BITS */ +#define LR_CHI422_BILINEAR 0 +#define LR_CHI422_REPLICATION 1 +#define LR_CHI420_JPEG (0 << 2) +#define LR_CHI420_MPEG (1 << 2) + +#define L_ITSEL(x) ((x) & 0xFFF) +#define L_FTSEL(x) (((x) & 0xFFF) << 16) + +#define LAYER_PER_PLANE_REGS 4 + +/* Layer_WR registers */ +#define LAYER_WR_PROG_LINE 0x0D4 +#define LAYER_WR_FORMAT 0x0D8 + +/* Layer_WR control bits */ +#define LW_OFM BIT(4) +#define LW_LALPHA(x) (((x) & 0xFF) << 8) +#define LW_A_WCACHE(x) (((x) & 0xF) << 28) +#define LW_TBU_EN BIT(16) + +#define AxCACHE_MASK 0xF0000000 + +/* Layer AXI R/W cache setting */ +#define AxCACHE_B BIT(0) /* Bufferable */ +#define AxCACHE_M BIT(1) /* Modifiable */ +#define AxCACHE_RA BIT(2) /* Read-Allocate */ +#define AxCACHE_WA BIT(3) /* Write-Allocate */ + +/* Layer info bits */ +#define L_INFO_RF BIT(0) +#define L_INFO_CM BIT(1) +#define L_INFO_ABUF_SIZE(x) (((x) >> 4) & 0x7) +#define L_INFO_YUV_MAX_LINESZ(x) (((x) >> 16) & 0xFFFF) + +/* Scaler registers */ +#define SC_COEFFTAB 0x0DC +#define SC_OUT_SIZE 0x0E4 +#define SC_H_CROP 0x0E8 +#define SC_V_CROP 0x0EC +#define SC_H_INIT_PH 0x0F0 +#define SC_H_DELTA_PH 0x0F4 +#define SC_V_INIT_PH 0x0F8 +#define SC_V_DELTA_PH 0x0FC +#define SC_ENH_LIMITS 0x130 +#define SC_ENH_COEFF0 0x134 + +#define SC_MAX_ENH_COEFF 9 + +/* SC_CTRL_BITS */ +#define SC_CTRL_SCL BIT(0) +#define SC_CTRL_LS BIT(1) +#define SC_CTRL_AP BIT(4) +#define SC_CTRL_IENH BIT(8) +#define SC_CTRL_RGBSM BIT(16) +#define SC_CTRL_ASM BIT(17) + +#define SC_VTSEL(vtal) ((vtal) << 16) + +#define SC_NUM_INPUTS_IDS 1 +#define SC_NUM_OUTPUTS_IDS 1 + +#define MG_NUM_INPUTS_IDS 2 +#define MG_NUM_OUTPUTS_IDS 1 + +/* Merger registers */ +#define MG_INPUT_ID0 BLK_INPUT_ID0 +#define MG_INPUT_ID1 (MG_INPUT_ID0 + 4) +#define MG_SIZE BLK_SIZE + +/* Splitter registers */ +#define SP_OVERLAP_SIZE 0xD8 + +/* Backend registers */ +#define BS_INFO 0x0C0 +#define BS_PROG_LINE 0x0D4 +#define BS_PREFETCH_LINE 0x0D8 +#define BS_BG_COLOR 0x0DC +#define BS_ACTIVESIZE 0x0E0 +#define BS_HINTERVALS 0x0E4 +#define BS_VINTERVALS 0x0E8 +#define BS_SYNC 0x0EC +#define BS_DRIFT_TO 0x100 +#define BS_FRAME_TO 0x104 +#define BS_TE_TO 0x108 +#define BS_T0_INTERVAL 0x110 +#define BS_T1_INTERVAL 0x114 +#define BS_T2_INTERVAL 0x118 +#define BS_CRC0_LOW 0x120 +#define BS_CRC0_HIGH 0x124 +#define BS_CRC1_LOW 0x128 +#define BS_CRC1_HIGH 0x12C +#define BS_USER 0x130 + +/* BS control register bits */ +#define BS_CTRL_EN BIT(0) +#define BS_CTRL_VM BIT(1) +#define BS_CTRL_BM BIT(2) +#define BS_CTRL_HMASK BIT(4) +#define BS_CTRL_VD BIT(5) +#define BS_CTRL_TE BIT(8) +#define BS_CTRL_TS BIT(9) +#define BS_CTRL_TM BIT(12) +#define BS_CTRL_DL BIT(16) +#define BS_CTRL_SBS BIT(17) +#define BS_CTRL_CRC BIT(18) +#define BS_CTRL_PM BIT(20) + +/* BS active size/intervals */ +#define BS_H_INTVALS(hfp, hbp) (((hfp) & 0xFFF) + (((hbp) & 0x3FF) << 16)) +#define BS_V_INTVALS(vfp, vbp) (((vfp) & 0x3FFF) + (((vbp) & 0xFF) << 16)) + +/* BS_SYNC bits */ +#define BS_SYNC_HSW(x) ((x) & 0x3FF) +#define BS_SYNC_HSP BIT(12) +#define BS_SYNC_VSW(x) (((x) & 0xFF) << 16) +#define BS_SYNC_VSP BIT(28) + +#define BS_NUM_INPUT_IDS 0 +#define BS_NUM_OUTPUT_IDS 0 + +/* Image process registers */ +#define IPS_DEPTH 0x0D8 +#define IPS_RGB_RGB_COEFF0 0x130 +#define IPS_RGB_YUV_COEFF0 0x170 + +#define IPS_DEPTH_MARK 0xF + +/* IPS control register bits */ +#define IPS_CTRL_RGB BIT(0) +#define IPS_CTRL_FT BIT(4) +#define IPS_CTRL_YUV BIT(8) +#define IPS_CTRL_CHD422 BIT(9) +#define IPS_CTRL_CHD420 BIT(10) +#define IPS_CTRL_LPF BIT(11) +#define IPS_CTRL_DITH BIT(12) +#define IPS_CTRL_CLAMP BIT(16) +#define IPS_CTRL_SBS BIT(17) + +/* IPS info register bits */ +#define IPS_INFO_CHD420 BIT(10) + +#define IPS_NUM_INPUT_IDS 2 +#define IPS_NUM_OUTPUT_IDS 1 + +/* FT_COEFF block registers */ +#define FT_COEFF0 0x80 +#define GLB_IT_COEFF 0x80 + +/* GLB_SC_COEFF registers */ +#define GLB_SC_COEFF_ADDR 0x0080 +#define GLB_SC_COEFF_DATA 0x0084 +#define GLB_LT_COEFF_DATA 0x0080 + +#define GLB_SC_COEFF_MAX_NUM 1024 +#define GLB_LT_COEFF_NUM 65 +/* GLB_SC_ADDR */ +#define SC_COEFF_R_ADDR BIT(18) +#define SC_COEFF_G_ADDR BIT(17) +#define SC_COEFF_B_ADDR BIT(16) + +#define SC_COEFF_DATA(x, y) (((y) & 0xFFFF) | (((x) & 0xFFFF) << 16)) + +enum d71_blk_type { + D71_BLK_TYPE_GCU = 0x00, + D71_BLK_TYPE_LPU = 0x01, + D71_BLK_TYPE_CU = 0x02, + D71_BLK_TYPE_DOU = 0x03, + D71_BLK_TYPE_AEU = 0x04, + D71_BLK_TYPE_GLB_LT_COEFF = 0x05, + D71_BLK_TYPE_GLB_SCL_COEFF = 0x06, /* SH/SV scaler coeff */ + D71_BLK_TYPE_GLB_SC_COEFF = 0x07, + D71_BLK_TYPE_PERIPH = 0x08, + D71_BLK_TYPE_LPU_TRUSTED = 0x09, + D71_BLK_TYPE_AEU_TRUSTED = 0x0A, + D71_BLK_TYPE_LPU_LAYER = 0x10, + D71_BLK_TYPE_LPU_WB_LAYER = 0x11, + D71_BLK_TYPE_CU_SPLITTER = 0x20, + D71_BLK_TYPE_CU_SCALER = 0x21, + D71_BLK_TYPE_CU_MERGER = 0x22, + D71_BLK_TYPE_DOU_IPS = 0x30, + D71_BLK_TYPE_DOU_BS = 0x31, + D71_BLK_TYPE_DOU_FT_COEFF = 0x32, + D71_BLK_TYPE_AEU_DS = 0x40, + D71_BLK_TYPE_AEU_AES = 0x41, + D71_BLK_TYPE_RESERVED = 0xFF +}; + +/* Constant of components */ +#define D71_MAX_PIPELINE 2 +#define D71_PIPELINE_MAX_SCALERS 2 +#define D71_PIPELINE_MAX_LAYERS 4 + +#define D71_MAX_GLB_IT_COEFF 3 +#define D71_MAX_GLB_SCL_COEFF 4 + +#define D71_MAX_LAYERS_PER_LPU 4 +#define D71_BLOCK_MAX_INPUT 9 +#define D71_BLOCK_MAX_OUTPUT 5 +#define D71_MAX_SC_PER_CU 2 + +#define D71_BLOCK_OFFSET_PERIPH 0xFE00 +#define D71_BLOCK_SIZE 0x0200 + +#define D71_DEFAULT_PREPRETCH_LINE 5 +#define D71_BUS_WIDTH_16_BYTES 16 + +#define D71_SC_MAX_UPSCALING 64 +#define D71_SC_MAX_DOWNSCALING 6 +#define D71_SC_SPLIT_OVERLAP 8 +#define D71_SC_ENH_SPLIT_OVERLAP 1 + +#define D71_MG_MIN_MERGED_SIZE 4 +#define D71_MG_MAX_MERGED_HSIZE 4032 +#define D71_MG_MAX_MERGED_VSIZE 4096 + +#define D71_PALPHA_DEF_MAP 0xFFAA5500 +#define D71_LAYER_CONTROL_DEFAULT 0x30000000 +#define D71_WB_LAYER_CONTROL_DEFAULT 0x3000FF00 +#define D71_BS_CONTROL_DEFAULT 0x00000002 + +struct block_header { + u32 block_info; + u32 pipeline_info; + u32 input_ids[D71_BLOCK_MAX_INPUT]; + u32 output_ids[D71_BLOCK_MAX_OUTPUT]; +}; + +static inline u32 get_block_type(struct block_header *blk) +{ + return BLOCK_INFO_BLK_TYPE(blk->block_info); +} + +#endif /* !_D71_REG_H_ */ -- cgit v1.2.3