diff options
Diffstat (limited to 'drivers/gpu/drm/arm')
20 files changed, 2530 insertions, 117 deletions
diff --git a/drivers/gpu/drm/arm/display/include/malidp_utils.h b/drivers/gpu/drm/arm/display/include/malidp_utils.h index 63cc47cefcf8..8cfd91196e15 100644 --- a/drivers/gpu/drm/arm/display/include/malidp_utils.h +++ b/drivers/gpu/drm/arm/display/include/malidp_utils.h @@ -7,10 +7,41 @@ #ifndef _MALIDP_UTILS_ #define _MALIDP_UTILS_ +#include <linux/delay.h> + #define has_bit(nr, mask) (BIT(nr) & (mask)) #define has_bits(bits, mask) (((bits) & (mask)) == (bits)) #define dp_for_each_set_bit(bit, mask) \ for_each_set_bit((bit), ((unsigned long *)&(mask)), sizeof(mask) * 8) +#define dp_wait_cond(__cond, __tries, __min_range, __max_range) \ +({ \ + int num_tries = __tries; \ + while (!__cond && (num_tries > 0)) { \ + usleep_range(__min_range, __max_range); \ + if (__cond) \ + break; \ + num_tries--; \ + } \ + num_tries; \ +}) + +/* the restriction of range is [start, end] */ +struct malidp_range { + u32 start; + u32 end; +}; + +static inline void set_range(struct malidp_range *rg, u32 start, u32 end) +{ + rg->start = start; + rg->end = end; +} + +static inline bool in_range(struct malidp_range *rg, u32 v) +{ + return (v >= rg->start) && (v <= rg->end); +} + #endif /* _MALIDP_UTILS_ */ diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile index 1b875e5dc0f6..d593125236ae 100644 --- a/drivers/gpu/drm/arm/display/komeda/Makefile +++ b/drivers/gpu/drm/arm/display/komeda/Makefile @@ -16,6 +16,7 @@ komeda-y := \ komeda_private_obj.o komeda-y += \ - d71/d71_dev.o + d71/d71_dev.o \ + d71/d71_component.o obj-$(CONFIG_DRM_KOMEDA) += komeda.o 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 000000000000..c56cfc2de147 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang <james.qian.wang@arm.com> + * + */ + +#include <drm/drm_print.h> +#include "d71_dev.h" +#include "komeda_kms.h" +#include "malidp_io.h" +#include "komeda_framebuffer.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]); +} + +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 inline u32 to_d71_input_id(struct komeda_component_output *output) +{ + struct komeda_component *comp = output->component; + + return comp ? (comp->hw_id + output->output_port) : 0; +} + +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); + int i; + + for (i = 0; i < fb->format->num_planes; i++) { + malidp_write32(reg, + BLK_P0_PTR_LOW + i * LAYER_PER_PLANE_REGS * 4, + lower_32_bits(st->addr[i])); + malidp_write32(reg, + BLK_P0_PTR_HIGH + i * LAYER_PER_PLANE_REGS * 4, + upper_32_bits(st->addr[i])); + if (i >= 2) + break; + + malidp_write32(reg, + BLK_P0_STRIDE + i * LAYER_PER_PLANE_REGS * 4, + fb->pitches[i] & 0xFFFF); + } + + malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id); + malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize)); + + 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 struct komeda_component_funcs d71_layer_funcs = { + .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; + + set_range(&layer->hsize_in, 4, d71->max_line_size); + 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 int d71_wb_layer_init(struct d71_dev *d71, + struct block_header *blk, u32 __iomem *reg) +{ + DRM_DEBUG("Detect D71_Wb_Layer.\n"); + + 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); +} + +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, input_hw_id; + + for_each_changed_input(state, index) { + id_reg = reg + index; + cfg_reg = reg + index * CU_PER_INPUT_REGS; + input_hw_id = to_d71_input_id(&state->inputs[index]); + if (state->active_inputs & BIT(index)) { + compiz_enable_input(id_reg, cfg_reg, + input_hw_id, &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]); +} + +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, D71_MIN_LINE_SIZE, d71->max_line_size); + set_range(&compiz->vsize, D71_MIN_VERTICAL_SIZE, d71->max_vsize); + + return 0; +} + +static void d71_improc_update(struct komeda_component *c, + struct komeda_component_state *state) +{ + struct komeda_improc_state *st = to_improc_st(state); + u32 __iomem *reg = c->reg; + u32 index, input_hw_id; + + for_each_changed_input(state, index) { + input_hw_id = state->active_inputs & BIT(index) ? + to_d71_input_id(&state->inputs[index]) : 0; + malidp_write32(reg, BLK_INPUT_ID0 + index * 4, input_hw_id); + } + + malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize)); +} + +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]); +} + +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_YCRCB444 | + DRM_COLOR_FORMAT_YCRCB422; + value = malidp_read32(reg, BLK_INFO); + if (value & IPS_INFO_CHD420) + improc->supported_color_formats |= DRM_COLOR_FORMAT_YCRCB420; + + 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; + u32 __iomem *reg = c->reg; + struct videomode vm; + u32 value; + + drm_display_mode_to_videomode(&crtc_st->adjusted_mode, &vm); + + malidp_write32(reg, BS_ACTIVESIZE, HV_SIZE(vm.hactive, vm.vactive)); + malidp_write32(reg, BS_HINTERVALS, BS_H_INTVALS(vm.hfront_porch, + vm.hback_porch)); + malidp_write32(reg, BS_VINTERVALS, BS_V_INTVALS(vm.vfront_porch, + vm.vback_porch)); + + value = BS_SYNC_VSW(vm.vsync_len) | BS_SYNC_HSW(vm.hsync_len); + value |= vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ? BS_SYNC_VSP : 0; + value |= vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ? 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; + + malidp_write32(reg, BLK_CONTROL, value); +} + +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]); +} + +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 = true; + + 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_SPLITTER: + case D71_BLK_TYPE_CU_SCALER: + case D71_BLK_TYPE_CU_MERGER: + 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; +} diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c index edbf9daa1545..72631d673f85 100644 --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c @@ -4,13 +4,375 @@ * Author: James.Qian.Wang <james.qian.wang@arm.com> * */ + +#include <drm/drm_print.h> +#include "d71_dev.h" #include "malidp_io.h" -#include "komeda_dev.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_ERR | LPU_IRQ_IBSY)) { + 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 (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 gcu_status ? IRQ_HANDLED : IRQ_NONE; +} + +#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 int d71_reset(struct d71_dev *d71) +{ + u32 __iomem *gcu = d71->gcu_addr; + int ret; + + malidp_write32_mask(gcu, BLK_CONTROL, + GCU_CONTROL_SRST, GCU_CONTROL_SRST); + + ret = dp_wait_cond(!(malidp_read32(gcu, BLK_CONTROL) & GCU_CONTROL_SRST), + 100, 1000, 10000); + + return ret > 0 ? 0 : -ETIMEDOUT; +} + +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) { - /* TODO add enum resources */ - return -1; + 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; + } + + /* probe PERIPH */ + value = malidp_read32(d71->periph_addr, BLK_BLOCK_INFO); + if (BLOCK_INFO_BLK_TYPE(value) != D71_BLK_TYPE_PERIPH) { + DRM_ERROR("access blk periph but got blk: %d.\n", + BLOCK_INFO_BLK_TYPE(value)); + err = -EINVAL; + goto err_cleanup; + } + + 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 ? true : false; + d71->integrates_tbu = value & PERIPH_TBU_EN ? true : false; + + for (i = 0; i < d71->num_pipelines; i++) { + pipe = komeda_pipeline_add(mdev, sizeof(struct d71_pipeline), + NULL); + if (IS_ERR(pipe)) { + err = PTR_ERR(pipe); + goto err_cleanup; + } + d71->pipes[i] = to_d71_pipeline(pipe); + } + + /* loop the register blks and probe */ + i = 2; /* exclude GCU and PERIPH */ + 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) \ @@ -93,13 +455,12 @@ static void d71_init_fmt_tbl(struct komeda_dev *mdev) static struct komeda_dev_funcs d71_chip_funcs = { .init_format_table = d71_init_fmt_tbl, .enum_resources = d71_enum_resources, - .cleanup = NULL, + .cleanup = d71_cleanup, + .irq_handler = d71_irq_handler, + .enable_irq = d71_enable_irq, + .disable_irq = d71_disable_irq, }; -#define GLB_ARCH_ID 0x000 -#define GLB_CORE_ID 0x004 -#define GLB_CORE_INFO 0x008 - struct komeda_dev_funcs * d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip) { 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 000000000000..7465c57d9774 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang <james.qian.wang@arm.com> + * + */ +#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) + +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); + +#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 000000000000..2d5e6d00b42c --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_regs.h @@ -0,0 +1,530 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. + * Author: James.Qian.Wang <james.qian.wang@arm.com> + * + */ +#ifndef _D71_REG_H_ +#define _D71_REG_H_ + +/* Common block registers offset */ +#define BLK_BLOCK_INFO 0x000 +#define BLK_PIPELINE_INFO 0x004 +#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 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_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_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) + +/* 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_MIN_LINE_SIZE 64 +#define D71_MIN_VERTICAL_SIZE 64 +#define D71_SC_MIN_LIN_SIZE 4 +#define D71_SC_MIN_VERTICAL_SIZE 4 +#define D71_SC_MAX_LIN_SIZE 2048 +#define D71_SC_MAX_VERTICAL_SIZE 4096 + +#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_ */ diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index 3ca5718aa0c2..f88a14927be9 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -18,6 +18,24 @@ #include "komeda_dev.h" #include "komeda_kms.h" +void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, + struct komeda_events *evts) +{ + struct drm_crtc *crtc = &kcrtc->base; + u32 events = evts->pipes[kcrtc->master->id]; + + if (events & KOMEDA_EVENT_VSYNC) + drm_crtc_handle_vblank(crtc); + + /* will handle it together with the write back support */ + if (events & KOMEDA_EVENT_EOW) + DRM_DEBUG("EOW.\n"); + + /* will handle it with crtc->flush */ + if (events & KOMEDA_EVENT_FLIP) + DRM_DEBUG("FLIP Done.\n"); +} + struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = { }; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c index 70e9bb7fa30c..24548b87e182 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c @@ -8,11 +8,57 @@ #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/platform_device.h> +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#endif #include <drm/drm_print.h> #include "komeda_dev.h" +static int komeda_register_show(struct seq_file *sf, void *x) +{ + struct komeda_dev *mdev = sf->private; + int i; + + if (mdev->funcs->dump_register) + mdev->funcs->dump_register(mdev, sf); + + for (i = 0; i < mdev->n_pipelines; i++) + komeda_pipeline_dump_register(mdev->pipelines[i], sf); + + return 0; +} + +static int komeda_register_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, komeda_register_show, inode->i_private); +} + +static const struct file_operations komeda_register_fops = { + .owner = THIS_MODULE, + .open = komeda_register_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#ifdef CONFIG_DEBUG_FS +static void komeda_debugfs_init(struct komeda_dev *mdev) +{ + if (!debugfs_initialized()) + return; + + mdev->debugfs_root = debugfs_create_dir("komeda", NULL); + if (IS_ERR_OR_NULL(mdev->debugfs_root)) + return; + + debugfs_create_file("register", 0444, mdev->debugfs_root, + mdev, &komeda_register_fops); +} +#endif + static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) { struct komeda_pipeline *pipe; @@ -53,6 +99,7 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev) { + struct platform_device *pdev = to_platform_device(dev); struct device_node *child, *np = dev->of_node; struct clk *clk; int ret; @@ -62,6 +109,11 @@ static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev) return PTR_ERR(clk); mdev->mclk = clk; + mdev->irq = platform_get_irq(pdev, 0); + if (mdev->irq < 0) { + DRM_ERROR("could not get IRQ number.\n"); + return mdev->irq; + } for_each_available_child_of_node(np, child) { if (of_node_cmp(child->name, "pipeline") == 0) { @@ -147,6 +199,16 @@ struct komeda_dev *komeda_dev_create(struct device *dev) goto err_cleanup; } + err = komeda_assemble_pipelines(mdev); + if (err) { + DRM_ERROR("assemble display pipelines failed.\n"); + goto err_cleanup; + } + +#ifdef CONFIG_DEBUG_FS + komeda_debugfs_init(mdev); +#endif + return mdev; err_cleanup: @@ -160,6 +222,10 @@ void komeda_dev_destroy(struct komeda_dev *mdev) struct komeda_dev_funcs *funcs = mdev->funcs; int i; +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(mdev->debugfs_root); +#endif + for (i = 0; i < mdev->n_pipelines; i++) { komeda_pipeline_destroy(mdev, mdev->pipelines[i]); mdev->pipelines[i] = NULL; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h index 0f77dead6a23..8eae2620ce77 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h @@ -13,6 +13,33 @@ #include "malidp_product.h" #include "komeda_format_caps.h" +#define KOMEDA_EVENT_VSYNC BIT_ULL(0) +#define KOMEDA_EVENT_FLIP BIT_ULL(1) +#define KOMEDA_EVENT_URUN BIT_ULL(2) +#define KOMEDA_EVENT_IBSY BIT_ULL(3) +#define KOMEDA_EVENT_OVR BIT_ULL(4) +#define KOMEDA_EVENT_EOW BIT_ULL(5) +#define KOMEDA_EVENT_MODE BIT_ULL(6) + +#define KOMEDA_ERR_TETO BIT_ULL(14) +#define KOMEDA_ERR_TEMR BIT_ULL(15) +#define KOMEDA_ERR_TITR BIT_ULL(16) +#define KOMEDA_ERR_CPE BIT_ULL(17) +#define KOMEDA_ERR_CFGE BIT_ULL(18) +#define KOMEDA_ERR_AXIE BIT_ULL(19) +#define KOMEDA_ERR_ACE0 BIT_ULL(20) +#define KOMEDA_ERR_ACE1 BIT_ULL(21) +#define KOMEDA_ERR_ACE2 BIT_ULL(22) +#define KOMEDA_ERR_ACE3 BIT_ULL(23) +#define KOMEDA_ERR_DRIFTTO BIT_ULL(24) +#define KOMEDA_ERR_FRAMETO BIT_ULL(25) +#define KOMEDA_ERR_CSCE BIT_ULL(26) +#define KOMEDA_ERR_ZME BIT_ULL(27) +#define KOMEDA_ERR_MERR BIT_ULL(28) +#define KOMEDA_ERR_TCF BIT_ULL(29) +#define KOMEDA_ERR_TTNG BIT_ULL(30) +#define KOMEDA_ERR_TTF BIT_ULL(31) + /* malidp device id */ enum { MALI_D71 = 0, @@ -39,6 +66,11 @@ struct komeda_product_data { struct komeda_dev; +struct komeda_events { + u64 global; + u64 pipes[KOMEDA_MAX_PIPELINES]; +}; + /** * struct komeda_dev_funcs * @@ -60,6 +92,20 @@ struct komeda_dev_funcs { int (*enum_resources)(struct komeda_dev *mdev); /** @cleanup: call to chip to cleanup komeda_dev->chip data */ void (*cleanup)(struct komeda_dev *mdev); + /** + * @irq_handler: + * + * for CORE to get the HW event from the CHIP when interrupt happened. + */ + irqreturn_t (*irq_handler)(struct komeda_dev *mdev, + struct komeda_events *events); + /** @enable_irq: enable irq */ + int (*enable_irq)(struct komeda_dev *mdev); + /** @disable_irq: disable irq */ + int (*disable_irq)(struct komeda_dev *mdev); + + /** @dump_register: Optional, dump registers to seq_file */ + void (*dump_register)(struct komeda_dev *mdev, struct seq_file *seq); }; /** @@ -81,6 +127,9 @@ struct komeda_dev { /** @mck: HW main engine clk */ struct clk *mclk; + /** @irq: irq number */ + int irq; + int n_pipelines; struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES]; @@ -93,6 +142,8 @@ struct komeda_dev { * destroyed by &komeda_dev_funcs.cleanup() */ void *chip_data; + + struct dentry *debugfs_root; }; static inline bool diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 47a58ab20434..b214edbfbbc6 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -13,6 +13,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_irq.h> #include <drm/drm_vblank.h> #include "komeda_dev.h" @@ -33,10 +34,31 @@ static int komeda_gem_cma_dumb_create(struct drm_file *file, return drm_gem_cma_dumb_create_internal(file, dev, args); } +static irqreturn_t komeda_kms_irq_handler(int irq, void *data) +{ + struct drm_device *drm = data; + struct komeda_dev *mdev = drm->dev_private; + struct komeda_kms_dev *kms = to_kdev(drm); + struct komeda_events evts; + irqreturn_t status; + u32 i; + + /* Call into the CHIP to recognize events */ + memset(&evts, 0, sizeof(evts)); + status = mdev->funcs->irq_handler(mdev, &evts); + + /* Notify the crtc to handle the events */ + for (i = 0; i < kms->n_crtcs; i++) + komeda_crtc_handle_event(&kms->crtcs[i], &evts); + + return status; +} + static struct drm_driver komeda_kms_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | - DRIVER_PRIME, + DRIVER_PRIME | DRIVER_HAVE_IRQ, .lastclose = drm_fb_helper_lastclose, + .irq_handler = komeda_kms_irq_handler, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = komeda_gem_cma_dumb_create, @@ -144,12 +166,22 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) drm_mode_config_reset(drm); - err = drm_dev_register(drm, 0); + err = drm_irq_install(drm, mdev->irq); if (err) goto cleanup_mode_config; + err = mdev->funcs->enable_irq(mdev); + if (err) + goto uninstall_irq; + + err = drm_dev_register(drm, 0); + if (err) + goto uninstall_irq; + return kms; +uninstall_irq: + drm_irq_uninstall(drm); cleanup_mode_config: drm_mode_config_cleanup(drm); free_kms: @@ -162,7 +194,9 @@ void komeda_kms_detach(struct komeda_kms_dev *kms) struct drm_device *drm = &kms->base; struct komeda_dev *mdev = drm->dev_private; + mdev->funcs->disable_irq(mdev); drm_dev_unregister(drm); + drm_irq_uninstall(drm); component_unbind_all(mdev->dev, drm); komeda_kms_cleanup_private_objs(mdev); drm_mode_config_cleanup(drm); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h index 874e9c9f0749..15ac8b85506c 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h @@ -12,6 +12,8 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_device.h> #include <drm/drm_writeback.h> +#include <video/videomode.h> +#include <video/display_timing.h> /** struct komeda_plane - komeda instance of drm_plane */ struct komeda_plane { @@ -108,6 +110,9 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms, struct komeda_dev *mdev); void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev); +void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, + struct komeda_events *evts); + struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev); void komeda_kms_detach(struct komeda_kms_dev *kms); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c index f1908e9ef128..07398efc40f5 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c @@ -19,17 +19,17 @@ komeda_pipeline_add(struct komeda_dev *mdev, size_t size, if (mdev->n_pipelines + 1 > KOMEDA_MAX_PIPELINES) { DRM_ERROR("Exceed max support %d pipelines.\n", KOMEDA_MAX_PIPELINES); - return NULL; + return ERR_PTR(-ENOSPC); } if (size < sizeof(*pipe)) { DRM_ERROR("Request pipeline size too small.\n"); - return NULL; + return ERR_PTR(-EINVAL); } pipe = devm_kzalloc(mdev->dev, size, GFP_KERNEL); if (!pipe) - return NULL; + return ERR_PTR(-ENOMEM); pipe->mdev = mdev; pipe->id = mdev->n_pipelines; @@ -142,32 +142,32 @@ komeda_component_add(struct komeda_pipeline *pipe, if (max_active_inputs > KOMEDA_COMPONENT_N_INPUTS) { WARN(1, "please large KOMEDA_COMPONENT_N_INPUTS to %d.\n", max_active_inputs); - return NULL; + return ERR_PTR(-ENOSPC); } pos = komeda_pipeline_get_component_pos(pipe, id); if (!pos || (*pos)) - return NULL; + return ERR_PTR(-EINVAL); if (has_bit(id, KOMEDA_PIPELINE_LAYERS)) { idx = id - KOMEDA_COMPONENT_LAYER0; num = &pipe->n_layers; if (idx != pipe->n_layers) { DRM_ERROR("please add Layer by id sequence.\n"); - return NULL; + return ERR_PTR(-EINVAL); } } else if (has_bit(id, KOMEDA_PIPELINE_SCALERS)) { idx = id - KOMEDA_COMPONENT_SCALER0; num = &pipe->n_scalers; if (idx != pipe->n_scalers) { DRM_ERROR("please add Scaler by id sequence.\n"); - return NULL; + return ERR_PTR(-EINVAL); } } c = devm_kzalloc(pipe->mdev->dev, comp_sz, GFP_KERNEL); if (!c) - return NULL; + return ERR_PTR(-ENOMEM); c->id = id; c->hw_id = hw_id; @@ -200,3 +200,98 @@ void komeda_component_destroy(struct komeda_dev *mdev, { devm_kfree(mdev->dev, c); } + +static void komeda_component_dump(struct komeda_component *c) +{ + if (!c) + return; + + DRM_DEBUG(" %s: ID %d-0x%08lx.\n", + c->name, c->id, BIT(c->id)); + DRM_DEBUG(" max_active_inputs:%d, supported_inputs: 0x%08x.\n", + c->max_active_inputs, c->supported_inputs); + DRM_DEBUG(" max_active_outputs:%d, supported_outputs: 0x%08x.\n", + c->max_active_outputs, c->supported_outputs); +} + +static void komeda_pipeline_dump(struct komeda_pipeline *pipe) +{ + struct komeda_component *c; + int id; + + DRM_INFO("Pipeline-%d: n_layers: %d, n_scalers: %d, output: %s\n", + pipe->id, pipe->n_layers, pipe->n_scalers, + pipe->of_output_dev ? pipe->of_output_dev->full_name : "none"); + + dp_for_each_set_bit(id, pipe->avail_comps) { + c = komeda_pipeline_get_component(pipe, id); + + komeda_component_dump(c); + } +} + +static void komeda_component_verify_inputs(struct komeda_component *c) +{ + struct komeda_pipeline *pipe = c->pipeline; + struct komeda_component *input; + int id; + + dp_for_each_set_bit(id, c->supported_inputs) { + input = komeda_pipeline_get_component(pipe, id); + if (!input) { + c->supported_inputs &= ~(BIT(id)); + DRM_WARN("Can not find input(ID-%d) for component: %s.\n", + id, c->name); + continue; + } + + input->supported_outputs |= BIT(c->id); + } +} + +static void komeda_pipeline_assemble(struct komeda_pipeline *pipe) +{ + struct komeda_component *c; + int id; + + dp_for_each_set_bit(id, pipe->avail_comps) { + c = komeda_pipeline_get_component(pipe, id); + + komeda_component_verify_inputs(c); + } +} + +int komeda_assemble_pipelines(struct komeda_dev *mdev) +{ + struct komeda_pipeline *pipe; + int i; + + for (i = 0; i < mdev->n_pipelines; i++) { + pipe = mdev->pipelines[i]; + + komeda_pipeline_assemble(pipe); + komeda_pipeline_dump(pipe); + } + + return 0; +} + +void komeda_pipeline_dump_register(struct komeda_pipeline *pipe, + struct seq_file *sf) +{ + struct komeda_component *c; + u32 id; + + seq_printf(sf, "\n======== Pipeline-%d ==========\n", pipe->id); + + if (pipe->funcs && pipe->funcs->dump_register) + pipe->funcs->dump_register(pipe, sf); + + dp_for_each_set_bit(id, pipe->avail_comps) { + c = komeda_pipeline_get_component(pipe, id); + + seq_printf(sf, "\n------%s------\n", c->name); + if (c->funcs->dump_register) + c->funcs->dump_register(c, sf); + } +} diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h index 8c950bc8ae96..c30a790d0712 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h @@ -204,51 +204,74 @@ static inline u16 component_changed_inputs(struct komeda_component_state *st) return component_disabling_inputs(st) | st->changed_active_inputs; } +#define for_each_changed_input(st, i) \ + for ((i) = 0; (i) < (st)->component->max_active_inputs; (i)++) \ + if (has_bit((i), component_changed_inputs(st))) + #define to_comp(__c) (((__c) == NULL) ? NULL : &((__c)->base)) #define to_cpos(__c) ((struct komeda_component **)&(__c)) -/* these structures are going to be filled in in uture patches */ struct komeda_layer { struct komeda_component base; - /* layer specific features and caps */ - int layer_type; /* RICH, SIMPLE or WB */ + /* accepted h/v input range before rotation */ + struct malidp_range hsize_in, vsize_in; + u32 layer_type; /* RICH, SIMPLE or WB */ + u32 supported_rots; }; struct komeda_layer_state { struct komeda_component_state base; /* layer specific configuration state */ + u16 hsize, vsize; + u32 rot; + dma_addr_t addr[3]; }; -struct komeda_compiz { +struct komeda_scaler { struct komeda_component base; - /* compiz specific features and caps */ + /* scaler features and caps */ }; -struct komeda_compiz_state { +struct komeda_scaler_state { struct komeda_component_state base; - /* compiz specific configuration state */ }; -struct komeda_scaler { +struct komeda_compiz { struct komeda_component base; - /* scaler features and caps */ + struct malidp_range hsize, vsize; }; -struct komeda_scaler_state { +struct komeda_compiz_input_cfg { + u16 hsize, vsize; + u16 hoffset, voffset; + u8 pixel_blend_mode, layer_alpha; +}; + +struct komeda_compiz_state { struct komeda_component_state base; + /* composition size */ + u16 hsize, vsize; + struct komeda_compiz_input_cfg cins[KOMEDA_COMPONENT_N_INPUTS]; }; struct komeda_improc { struct komeda_component base; + u32 supported_color_formats; /* DRM_RGB/YUV444/YUV420*/ + u32 supported_color_depths; /* BIT(8) | BIT(10)*/ + u8 supports_degamma : 1; + u8 supports_csc : 1; + u8 supports_gamma : 1; }; struct komeda_improc_state { struct komeda_component_state base; + u16 hsize, vsize; }; /* display timing controller */ struct komeda_timing_ctrlr { struct komeda_component base; + u8 supports_dual_link : 1; }; struct komeda_timing_ctrlr_state { @@ -340,10 +363,13 @@ komeda_pipeline_add(struct komeda_dev *mdev, size_t size, struct komeda_pipeline_funcs *funcs); void komeda_pipeline_destroy(struct komeda_dev *mdev, struct komeda_pipeline *pipe); - +int komeda_assemble_pipelines(struct komeda_dev *mdev); struct komeda_component * komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id); +void komeda_pipeline_dump_register(struct komeda_pipeline *pipe, + struct seq_file *sf); + /* component APIs */ struct komeda_component * komeda_component_add(struct komeda_pipeline *pipe, diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index ab50ad06e271..21725c9b9f5e 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -264,37 +264,17 @@ static bool malidp_verify_afbc_framebuffer_caps(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd) { - const struct drm_format_info *info; - - if ((mode_cmd->modifier[0] >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) { - DRM_DEBUG_KMS("Unknown modifier (not Arm)\n"); - return false; - } - - if (mode_cmd->modifier[0] & - ~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) { - DRM_DEBUG_KMS("Unsupported modifiers\n"); - return false; - } - - info = drm_get_format_info(dev, mode_cmd); - if (!info) { - DRM_DEBUG_KMS("Unable to get the format information\n"); + if (malidp_format_mod_supported(dev, mode_cmd->pixel_format, + mode_cmd->modifier[0]) == false) return false; - } - - if (info->num_planes != 1) { - DRM_DEBUG_KMS("AFBC buffers expect one plane\n"); - return false; - } if (mode_cmd->offsets[0] != 0) { DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n"); return false; } - switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) { - case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16: + switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { + case AFBC_SIZE_16X16: if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) { DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n"); return false; @@ -318,9 +298,10 @@ malidp_verify_afbc_framebuffer_size(struct drm_device *dev, struct drm_gem_object *objs = NULL; u32 afbc_superblock_size = 0, afbc_superblock_height = 0; u32 afbc_superblock_width = 0, afbc_size = 0; + int bpp = 0; - switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) { - case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16: + switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { + case AFBC_SIZE_16X16: afbc_superblock_height = 16; afbc_superblock_width = 16; break; @@ -334,15 +315,19 @@ malidp_verify_afbc_framebuffer_size(struct drm_device *dev, n_superblocks = (mode_cmd->width / afbc_superblock_width) * (mode_cmd->height / afbc_superblock_height); - afbc_superblock_size = info->cpp[0] * afbc_superblock_width * - afbc_superblock_height; + bpp = malidp_format_get_bpp(info->format); + + afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height) + / BITS_PER_BYTE; afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT); afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT); - if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) { - DRM_DEBUG_KMS("Invalid value of pitch (=%u) should be same as width (=%u) * cpp (=%u)\n", - mode_cmd->pitches[0], mode_cmd->width, info->cpp[0]); + if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) { + DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) " + "should be same as width (=%u) * bpp (=%u)\n", + (mode_cmd->pitches[0] * BITS_PER_BYTE), + mode_cmd->width, bpp); return false; } @@ -406,6 +391,7 @@ static int malidp_init(struct drm_device *drm) drm->mode_config.max_height = hwdev->max_line_size; drm->mode_config.funcs = &malidp_mode_config_funcs; drm->mode_config.helper_private = &malidp_mode_config_helpers; + drm->mode_config.allow_fb_modifiers = true; ret = malidp_crtc_init(drm); if (ret) diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h index b76c86f18a56..019a682b2716 100644 --- a/drivers/gpu/drm/arm/malidp_drv.h +++ b/drivers/gpu/drm/arm/malidp_drv.h @@ -90,6 +90,12 @@ struct malidp_crtc_state { int malidp_de_planes_init(struct drm_device *drm); int malidp_crtc_init(struct drm_device *drm); +bool malidp_hw_format_is_linear_only(u32 format); +bool malidp_hw_format_is_afbc_only(u32 format); + +bool malidp_format_mod_supported(struct drm_device *drm, + u32 format, u64 modifier); + #ifdef CONFIG_DEBUG_FS void malidp_error(struct malidp_drm *malidp, struct malidp_error_stats *error_stats, u32 status, diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index b9bed1138fa3..8df12e9a33bb 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -49,11 +49,19 @@ static const struct malidp_format_id malidp500_de_formats[] = { { DRM_FORMAT_YUYV, DE_VIDEO1, 13 }, { DRM_FORMAT_NV12, DE_VIDEO1 | SE_MEMWRITE, 14 }, { DRM_FORMAT_YUV420, DE_VIDEO1, 15 }, + { DRM_FORMAT_XYUV8888, DE_VIDEO1, 16 }, + /* These are supported with AFBC only */ + { DRM_FORMAT_YUV420_8BIT, DE_VIDEO1, 14 }, + { DRM_FORMAT_VUY888, DE_VIDEO1, 16 }, + { DRM_FORMAT_VUY101010, DE_VIDEO1, 17 }, + { DRM_FORMAT_YUV420_10BIT, DE_VIDEO1, 18 } }; #define MALIDP_ID(__group, __format) \ ((((__group) & 0x7) << 3) | ((__format) & 0x7)) +#define AFBC_YUV_422_FORMAT_ID MALIDP_ID(5, 1) + #define MALIDP_COMMON_FORMATS \ /* fourcc, layers supporting the format, internal id */ \ { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 0) }, \ @@ -74,11 +82,25 @@ static const struct malidp_format_id malidp500_de_formats[] = { { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \ { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \ { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \ + /* This is only supported with linear modifier */ \ + { DRM_FORMAT_XYUV8888, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 0) },\ + /* This is only supported with AFBC modifier */ \ + { DRM_FORMAT_VUY888, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 0) }, \ { DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \ + /* This is only supported with linear modifier */ \ { DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \ { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) }, \ + /* This is only supported with AFBC modifier */ \ + { DRM_FORMAT_YUV420_8BIT, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \ { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }, \ - { DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)} + /* This is only supported with linear modifier */ \ + { DRM_FORMAT_XVYU2101010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 0)}, \ + /* This is only supported with AFBC modifier */ \ + { DRM_FORMAT_VUY101010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 0)}, \ + { DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)}, \ + /* This is only supported with AFBC modifier */ \ + { DRM_FORMAT_YUV420_10BIT, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 7)}, \ + { DRM_FORMAT_P010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 7)} static const struct malidp_format_id malidp550_de_formats[] = { MALIDP_COMMON_FORMATS, @@ -94,11 +116,14 @@ static const struct malidp_layer malidp500_layers[] = { * yuv2rgb matrix offset, mmu control register offset, rotation_features */ { DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE, - MALIDP_DE_LV_STRIDE0, MALIDP500_LV_YUV2RGB, 0, ROTATE_ANY }, + MALIDP_DE_LV_STRIDE0, MALIDP500_LV_YUV2RGB, 0, ROTATE_ANY, + MALIDP500_DE_LV_AD_CTRL }, { DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE, - MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY }, + MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY, + MALIDP500_DE_LG1_AD_CTRL }, { DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE, - MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY }, + MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY, + MALIDP500_DE_LG2_AD_CTRL }, }; static const struct malidp_layer malidp550_layers[] = { @@ -106,13 +131,16 @@ static const struct malidp_layer malidp550_layers[] = { * yuv2rgb matrix offset, mmu control register offset, rotation_features */ { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE, - MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY }, + MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY, + MALIDP550_DE_LV1_AD_CTRL }, { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE, - MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY }, + MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY, + MALIDP550_DE_LG_AD_CTRL }, { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE, - MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY }, + MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY, + MALIDP550_DE_LV2_AD_CTRL }, { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE, - MALIDP550_DE_LS_R1_STRIDE, 0, 0, ROTATE_NONE }, + MALIDP550_DE_LS_R1_STRIDE, 0, 0, ROTATE_NONE, 0 }, }; static const struct malidp_layer malidp650_layers[] = { @@ -122,16 +150,44 @@ static const struct malidp_layer malidp650_layers[] = { */ { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE, MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, - MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY }, + MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY, + MALIDP550_DE_LV1_AD_CTRL }, { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE, MALIDP_DE_LG_STRIDE, 0, MALIDP650_DE_LG_MMU_CTRL, - ROTATE_COMPRESSED }, + ROTATE_COMPRESSED, MALIDP550_DE_LG_AD_CTRL }, { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE, MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, - MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY }, + MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY, + MALIDP550_DE_LV2_AD_CTRL }, { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE, MALIDP550_DE_LS_R1_STRIDE, 0, MALIDP650_DE_LS_MMU_CTRL, - ROTATE_NONE }, + ROTATE_NONE, 0 }, +}; + +const u64 malidp_format_modifiers[] = { + /* All RGB formats (except XRGB, RGBX, XBGR, BGRX) */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR | AFBC_SPARSE), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR), + + /* All RGB formats > 16bpp (except XRGB, RGBX, XBGR, BGRX) */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR | AFBC_SPARSE | AFBC_SPLIT), + + /* All 8 or 10 bit YUV 444 formats. */ + /* In DP550, 10 bit YUV 420 format also supported */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_SPARSE | AFBC_SPLIT), + + /* YUV 420, 422 P1 8 bit and YUV 444 8 bit/10 bit formats */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_SPARSE), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16), + + /* YUV 420, 422 P1 8, 10 bit formats */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_CBR | AFBC_SPARSE), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_CBR), + + /* All formats */ + DRM_FORMAT_MOD_LINEAR, + + DRM_FORMAT_MOD_INVALID }; #define SE_N_SCALING_COEFFS 96 @@ -324,14 +380,39 @@ static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode * malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC); } -static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt) +int malidp_format_get_bpp(u32 fmt) +{ + int bpp = drm_format_plane_cpp(fmt, 0) * 8; + + if (bpp == 0) { + switch (fmt) { + case DRM_FORMAT_VUY101010: + bpp = 30; + case DRM_FORMAT_YUV420_10BIT: + bpp = 15; + break; + case DRM_FORMAT_YUV420_8BIT: + bpp = 12; + break; + default: + bpp = 0; + } + } + + return bpp; +} + +static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, + u16 h, u32 fmt, bool has_modifier) { /* * Each layer needs enough rotation memory to fit 8 lines * worth of pixel data. Required size is then: * size = rotated_width * (bpp / 8) * 8; */ - return w * drm_format_plane_cpp(fmt, 0) * 8; + int bpp = malidp_format_get_bpp(fmt); + + return w * bpp; } static void malidp500_se_write_pp_coefftab(struct malidp_hw_device *hwdev, @@ -609,9 +690,9 @@ static void malidp550_modeset(struct malidp_hw_device *hwdev, struct videomode * malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC); } -static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt) +static int malidpx50_get_bytes_per_column(u32 fmt) { - u32 bytes_per_col; + u32 bytes_per_column; switch (fmt) { /* 8 lines at 4 bytes per pixel */ @@ -637,19 +718,77 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 case DRM_FORMAT_UYVY: case DRM_FORMAT_YUYV: case DRM_FORMAT_X0L0: - case DRM_FORMAT_X0L2: - bytes_per_col = 32; + bytes_per_column = 32; break; /* 16 lines at 1.5 bytes per pixel */ case DRM_FORMAT_NV12: case DRM_FORMAT_YUV420: - bytes_per_col = 24; + /* 8 lines at 3 bytes per pixel */ + case DRM_FORMAT_VUY888: + /* 16 lines at 12 bits per pixel */ + case DRM_FORMAT_YUV420_8BIT: + /* 8 lines at 3 bytes per pixel */ + case DRM_FORMAT_P010: + bytes_per_column = 24; + break; + /* 8 lines at 30 bits per pixel */ + case DRM_FORMAT_VUY101010: + /* 16 lines at 15 bits per pixel */ + case DRM_FORMAT_YUV420_10BIT: + bytes_per_column = 30; break; default: return -EINVAL; } - return w * bytes_per_col; + return bytes_per_column; +} + +static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, + u16 h, u32 fmt, bool has_modifier) +{ + int bytes_per_column = 0; + + switch (fmt) { + /* 8 lines at 15 bits per pixel */ + case DRM_FORMAT_YUV420_10BIT: + bytes_per_column = 15; + break; + /* Uncompressed YUV 420 10 bit single plane cannot be rotated */ + case DRM_FORMAT_X0L2: + if (has_modifier) + bytes_per_column = 8; + else + return -EINVAL; + break; + default: + bytes_per_column = malidpx50_get_bytes_per_column(fmt); + } + + if (bytes_per_column == -EINVAL) + return bytes_per_column; + + return w * bytes_per_column; +} + +static int malidp650_rotmem_required(struct malidp_hw_device *hwdev, u16 w, + u16 h, u32 fmt, bool has_modifier) +{ + int bytes_per_column = 0; + + switch (fmt) { + /* 16 lines at 2 bytes per pixel */ + case DRM_FORMAT_X0L2: + bytes_per_column = 32; + break; + default: + bytes_per_column = malidpx50_get_bytes_per_column(fmt); + } + + if (bytes_per_column == -EINVAL) + return bytes_per_column; + + return w * bytes_per_column; } static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev, @@ -838,7 +977,10 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .se_base = MALIDP550_SE_BASE, .dc_base = MALIDP550_DC_BASE, .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH, - .features = MALIDP_REGMAP_HAS_CLEARIRQ, + .features = MALIDP_REGMAP_HAS_CLEARIRQ | + MALIDP_DEVICE_AFBC_SUPPORT_SPLIT | + MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT | + MALIDP_DEVICE_AFBC_YUYV_USE_422_P2, .n_layers = ARRAY_SIZE(malidp550_layers), .layers = malidp550_layers, .de_irq_map = { @@ -884,7 +1026,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .se_base = MALIDP550_SE_BASE, .dc_base = MALIDP550_DC_BASE, .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH, - .features = MALIDP_REGMAP_HAS_CLEARIRQ, + .features = MALIDP_REGMAP_HAS_CLEARIRQ | + MALIDP_DEVICE_AFBC_SUPPORT_SPLIT | + MALIDP_DEVICE_AFBC_YUYV_USE_422_P2, .n_layers = ARRAY_SIZE(malidp650_layers), .layers = malidp650_layers, .de_irq_map = { @@ -923,7 +1067,7 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .in_config_mode = malidp550_in_config_mode, .set_config_valid = malidp550_set_config_valid, .modeset = malidp550_modeset, - .rotmem_required = malidp550_rotmem_required, + .rotmem_required = malidp650_rotmem_required, .se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs, .se_calc_mclk = malidp550_se_calc_mclk, .enable_memwrite = malidp550_enable_memwrite, @@ -933,19 +1077,72 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { }; u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, - u8 layer_id, u32 format) + u8 layer_id, u32 format, bool has_modifier) { unsigned int i; for (i = 0; i < map->n_pixel_formats; i++) { if (((map->pixel_formats[i].layer & layer_id) == layer_id) && - (map->pixel_formats[i].format == format)) - return map->pixel_formats[i].id; + (map->pixel_formats[i].format == format)) { + /* + * In some DP550 and DP650, DRM_FORMAT_YUYV + AFBC modifier + * is supported by a different h/w format id than + * DRM_FORMAT_YUYV (only). + */ + if (format == DRM_FORMAT_YUYV && + (has_modifier) && + (map->features & MALIDP_DEVICE_AFBC_YUYV_USE_422_P2)) + return AFBC_YUV_422_FORMAT_ID; + else + return map->pixel_formats[i].id; + } } return MALIDP_INVALID_FORMAT_ID; } +bool malidp_hw_format_is_linear_only(u32 format) +{ + switch (format) { + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_XYUV8888: + case DRM_FORMAT_XVYU2101010: + case DRM_FORMAT_X0L2: + case DRM_FORMAT_X0L0: + return true; + default: + return false; + } +} + +bool malidp_hw_format_is_afbc_only(u32 format) +{ + switch (format) { + case DRM_FORMAT_VUY888: + case DRM_FORMAT_VUY101010: + case DRM_FORMAT_YUV420_8BIT: + case DRM_FORMAT_YUV420_10BIT: + return true; + default: + return false; + } +} + static void malidp_hw_clear_irq(struct malidp_hw_device *hwdev, u8 block, u32 irq) { u32 base = malidp_get_block_base(hwdev, block); diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h index 40155e2ea9d9..207c3ce52f1a 100644 --- a/drivers/gpu/drm/arm/malidp_hw.h +++ b/drivers/gpu/drm/arm/malidp_hw.h @@ -70,6 +70,8 @@ struct malidp_layer { s16 yuv2rgb_offset; /* offset to the YUV->RGB matrix entries */ u16 mmu_ctrl_offset; /* offset to the MMU control register */ enum rotation_features rot; /* type of rotation supported */ + /* address offset for the AFBC decoder registers */ + u16 afbc_decoder_offset; }; enum malidp_scaling_coeff_set { @@ -93,7 +95,10 @@ struct malidp_se_config { }; /* regmap features */ -#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0) +#define MALIDP_REGMAP_HAS_CLEARIRQ BIT(0) +#define MALIDP_DEVICE_AFBC_SUPPORT_SPLIT BIT(1) +#define MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT BIT(2) +#define MALIDP_DEVICE_AFBC_YUYV_USE_422_P2 BIT(3) struct malidp_hw_regmap { /* address offset of the DE register bank */ @@ -179,7 +184,8 @@ struct malidp_hw { * Calculate the required rotation memory given the active area * and the buffer format. */ - int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt); + int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, + u32 fmt, bool has_modifier); int (*se_set_scaling_coeffs)(struct malidp_hw_device *hwdev, struct malidp_se_config *se_config, @@ -319,7 +325,9 @@ int malidp_se_irq_init(struct drm_device *drm, int irq); void malidp_se_irq_fini(struct malidp_hw_device *hwdev); u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, - u8 layer_id, u32 format); + u8 layer_id, u32 format, bool has_modifier); + +int malidp_format_get_bpp(u32 fmt); static inline u8 malidp_hw_get_pitch_align(struct malidp_hw_device *hwdev, bool rotated) { @@ -388,9 +396,18 @@ static inline void malidp_se_set_enh_coeffs(struct malidp_hw_device *hwdev) #define MALIDP_GAMMA_LUT_SIZE 4096 -#define AFBC_MOD_VALID_BITS (AFBC_FORMAT_MOD_BLOCK_SIZE_MASK | \ - AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPLIT | \ - AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_CBR | \ - AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SC) +#define AFBC_SIZE_MASK AFBC_FORMAT_MOD_BLOCK_SIZE_MASK +#define AFBC_SIZE_16X16 AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 +#define AFBC_YTR AFBC_FORMAT_MOD_YTR +#define AFBC_SPARSE AFBC_FORMAT_MOD_SPARSE +#define AFBC_CBR AFBC_FORMAT_MOD_CBR +#define AFBC_SPLIT AFBC_FORMAT_MOD_SPLIT +#define AFBC_TILED AFBC_FORMAT_MOD_TILED +#define AFBC_SC AFBC_FORMAT_MOD_SC + +#define AFBC_MOD_VALID_BITS (AFBC_SIZE_MASK | AFBC_YTR | AFBC_SPLIT | \ + AFBC_SPARSE | AFBC_CBR | AFBC_TILED | AFBC_SC) + +extern const u64 malidp_format_modifiers[]; #endif /* __MALIDP_HW_H__ */ diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c index 041a64dc7167..5f102bdaf841 100644 --- a/drivers/gpu/drm/arm/malidp_mw.c +++ b/drivers/gpu/drm/arm/malidp_mw.c @@ -141,9 +141,14 @@ malidp_mw_encoder_atomic_check(struct drm_encoder *encoder, return -EINVAL; } + if (fb->modifier) { + DRM_DEBUG_KMS("Writeback framebuffer does not support modifiers\n"); + return -EINVAL; + } + mw_state->format = malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE, - fb->format->format); + fb->format->format, !!fb->modifier); if (mw_state->format == MALIDP_INVALID_FORMAT_ID) { struct drm_format_name_buf format_name; @@ -252,8 +257,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm, &mw_state->addrs[0], mw_state->format); - drm_writeback_queue_job(mw_conn, conn_state->writeback_job); - conn_state->writeback_job = NULL; + drm_writeback_queue_job(mw_conn, conn_state); hwdev->hw->enable_memwrite(hwdev, mw_state->addrs, mw_state->pitches, mw_state->n_planes, fb->width, fb->height, mw_state->format, diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index c9a6d3e0cada..d42e0ea9a303 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -52,6 +52,8 @@ #define MALIDP550_LS_ENABLE 0x01c #define MALIDP550_LS_R1_IN_SIZE 0x020 +#define MODIFIERS_COUNT_MAX 15 + /* * This 4-entry look-up-table is used to determine the full 8-bit alpha value * for formats with 1- or 2-bit alpha channels. @@ -145,6 +147,119 @@ static void malidp_plane_atomic_print_state(struct drm_printer *p, drm_printf(p, "\tmmu_prefetch_pgsize=%d\n", ms->mmu_prefetch_pgsize); } +bool malidp_format_mod_supported(struct drm_device *drm, + u32 format, u64 modifier) +{ + const struct drm_format_info *info; + const u64 *modifiers; + struct malidp_drm *malidp = drm->dev_private; + const struct malidp_hw_regmap *map = &malidp->dev->hw->map; + + if (WARN_ON(modifier == DRM_FORMAT_MOD_INVALID)) + return false; + + /* Some pixel formats are supported without any modifier */ + if (modifier == DRM_FORMAT_MOD_LINEAR) { + /* + * However these pixel formats need to be supported with + * modifiers only + */ + return !malidp_hw_format_is_afbc_only(format); + } + + if ((modifier >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) { + DRM_ERROR("Unknown modifier (not Arm)\n"); + return false; + } + + if (modifier & + ~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) { + DRM_DEBUG_KMS("Unsupported modifiers\n"); + return false; + } + + modifiers = malidp_format_modifiers; + + /* SPLIT buffers must use SPARSE layout */ + if (WARN_ON_ONCE((modifier & AFBC_SPLIT) && !(modifier & AFBC_SPARSE))) + return false; + + /* CBR only applies to YUV formats, where YTR should be always 0 */ + if (WARN_ON_ONCE((modifier & AFBC_CBR) && (modifier & AFBC_YTR))) + return false; + + while (*modifiers != DRM_FORMAT_MOD_INVALID) { + if (*modifiers == modifier) + break; + + modifiers++; + } + + /* return false, if the modifier was not found */ + if (*modifiers == DRM_FORMAT_MOD_INVALID) { + DRM_DEBUG_KMS("Unsupported modifier\n"); + return false; + } + + info = drm_format_info(format); + + if (info->num_planes != 1) { + DRM_DEBUG_KMS("AFBC buffers expect one plane\n"); + return false; + } + + if (malidp_hw_format_is_linear_only(format) == true) { + DRM_DEBUG_KMS("Given format (0x%x) is supported is linear mode only\n", + format); + return false; + } + + /* + * RGB formats need to provide YTR modifier and YUV formats should not + * provide YTR modifier. + */ + if (!(info->is_yuv) != !!(modifier & AFBC_FORMAT_MOD_YTR)) { + DRM_DEBUG_KMS("AFBC_FORMAT_MOD_YTR is %s for %s formats\n", + info->is_yuv ? "disallowed" : "mandatory", + info->is_yuv ? "YUV" : "RGB"); + return false; + } + + if (modifier & AFBC_SPLIT) { + if (!info->is_yuv) { + if (drm_format_plane_cpp(format, 0) <= 2) { + DRM_DEBUG_KMS("RGB formats <= 16bpp are not supported with SPLIT\n"); + return false; + } + } + + if ((drm_format_horz_chroma_subsampling(format) != 1) || + (drm_format_vert_chroma_subsampling(format) != 1)) { + if (!(format == DRM_FORMAT_YUV420_10BIT && + (map->features & MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT))) { + DRM_DEBUG_KMS("Formats which are sub-sampled should never be split\n"); + return false; + } + } + } + + if (modifier & AFBC_CBR) { + if ((drm_format_horz_chroma_subsampling(format) == 1) || + (drm_format_vert_chroma_subsampling(format) == 1)) { + DRM_DEBUG_KMS("Formats which are not sub-sampled should not have CBR set\n"); + return false; + } + } + + return true; +} + +static bool malidp_format_mod_supported_per_plane(struct drm_plane *plane, + u32 format, u64 modifier) +{ + return malidp_format_mod_supported(plane->dev, format, modifier); +} + static const struct drm_plane_funcs malidp_de_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -153,6 +268,7 @@ static const struct drm_plane_funcs malidp_de_plane_funcs = { .atomic_duplicate_state = malidp_duplicate_plane_state, .atomic_destroy_state = malidp_destroy_plane_state, .atomic_print_state = malidp_plane_atomic_print_state, + .format_mod_supported = malidp_format_mod_supported_per_plane, }; static int malidp_se_check_scaling(struct malidp_plane *mp, @@ -406,8 +522,8 @@ static int malidp_de_plane_check(struct drm_plane *plane, fb = state->fb; ms->format = malidp_hw_get_format_id(&mp->hwdev->hw->map, - mp->layer->id, - fb->format->format); + mp->layer->id, fb->format->format, + !!fb->modifier); if (ms->format == MALIDP_INVALID_FORMAT_ID) return -EINVAL; @@ -415,8 +531,8 @@ static int malidp_de_plane_check(struct drm_plane *plane, for (i = 0; i < ms->n_planes; i++) { u8 alignment = malidp_hw_get_pitch_align(mp->hwdev, rotated); - if ((fb->pitches[i] * drm_format_info_block_height(fb->format, i)) - & (alignment - 1)) { + if (((fb->pitches[i] * drm_format_info_block_height(fb->format, i)) + & (alignment - 1)) && !(fb->modifier)) { DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n", fb->pitches[i], i); return -EINVAL; @@ -469,13 +585,20 @@ static int malidp_de_plane_check(struct drm_plane *plane, return -EINVAL; } + /* SMART layer does not support AFBC */ + if (mp->layer->id == DE_SMART && fb->modifier) { + DRM_ERROR("AFBC framebuffer not supported in SMART layer"); + return -EINVAL; + } + ms->rotmem_size = 0; if (state->rotation & MALIDP_ROTATED_MASK) { int val; val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_w, state->crtc_h, - fb->format->format); + fb->format->format, + !!(fb->modifier)); if (val < 0) return val; @@ -592,6 +715,83 @@ static void malidp_de_set_mmu_control(struct malidp_plane *mp, mp->layer->base + mp->layer->mmu_ctrl_offset); } +static void malidp_set_plane_base_addr(struct drm_framebuffer *fb, + struct malidp_plane *mp, + int plane_index) +{ + dma_addr_t paddr; + u16 ptr; + struct drm_plane *plane = &mp->base; + bool afbc = fb->modifier ? true : false; + + ptr = mp->layer->ptr + (plane_index << 4); + + /* + * drm_fb_cma_get_gem_addr() alters the physical base address of the + * framebuffer as per the plane's src_x, src_y co-ordinates (ie to + * take care of source cropping). + * For AFBC, this is not needed as the cropping is handled by _AD_CROP_H + * and _AD_CROP_V registers. + */ + if (!afbc) { + paddr = drm_fb_cma_get_gem_addr(fb, plane->state, + plane_index); + } else { + struct drm_gem_cma_object *obj; + + obj = drm_fb_cma_get_gem_obj(fb, plane_index); + + if (WARN_ON(!obj)) + return; + paddr = obj->paddr; + } + + malidp_hw_write(mp->hwdev, lower_32_bits(paddr), ptr); + malidp_hw_write(mp->hwdev, upper_32_bits(paddr), ptr + 4); +} + +static void malidp_de_set_plane_afbc(struct drm_plane *plane) +{ + struct malidp_plane *mp; + u32 src_w, src_h, val = 0, src_x, src_y; + struct drm_framebuffer *fb = plane->state->fb; + + mp = to_malidp_plane(plane); + + /* no afbc_decoder_offset means AFBC is not supported on this plane */ + if (!mp->layer->afbc_decoder_offset) + return; + + if (!fb->modifier) { + malidp_hw_write(mp->hwdev, 0, mp->layer->afbc_decoder_offset); + return; + } + + /* convert src values from Q16 fixed point to integer */ + src_w = plane->state->src_w >> 16; + src_h = plane->state->src_h >> 16; + src_x = plane->state->src_x >> 16; + src_y = plane->state->src_y >> 16; + + val = ((fb->width - (src_x + src_w)) << MALIDP_AD_CROP_RIGHT_OFFSET) | + src_x; + malidp_hw_write(mp->hwdev, val, + mp->layer->afbc_decoder_offset + MALIDP_AD_CROP_H); + + val = ((fb->height - (src_y + src_h)) << MALIDP_AD_CROP_BOTTOM_OFFSET) | + src_y; + malidp_hw_write(mp->hwdev, val, + mp->layer->afbc_decoder_offset + MALIDP_AD_CROP_V); + + val = MALIDP_AD_EN; + if (fb->modifier & AFBC_FORMAT_MOD_SPLIT) + val |= MALIDP_AD_BS; + if (fb->modifier & AFBC_FORMAT_MOD_YTR) + val |= MALIDP_AD_YTR; + + malidp_hw_write(mp->hwdev, val, mp->layer->afbc_decoder_offset); +} + static void malidp_de_plane_update(struct drm_plane *plane, struct drm_plane_state *old_state) { @@ -602,12 +802,23 @@ static void malidp_de_plane_update(struct drm_plane *plane, u8 plane_alpha = state->alpha >> 8; u32 src_w, src_h, dest_w, dest_h, val; int i; + struct drm_framebuffer *fb = plane->state->fb; mp = to_malidp_plane(plane); - /* convert src values from Q16 fixed point to integer */ - src_w = state->src_w >> 16; - src_h = state->src_h >> 16; + /* + * For AFBC framebuffer, use the framebuffer width and height for + * configuring layer input size register. + */ + if (fb->modifier) { + src_w = fb->width; + src_h = fb->height; + } else { + /* convert src values from Q16 fixed point to integer */ + src_w = state->src_w >> 16; + src_h = state->src_h >> 16; + } + dest_w = state->crtc_w; dest_h = state->crtc_h; @@ -615,15 +826,8 @@ static void malidp_de_plane_update(struct drm_plane *plane, val = (val & ~LAYER_FORMAT_MASK) | ms->format; malidp_hw_write(mp->hwdev, val, mp->layer->base); - for (i = 0; i < ms->n_planes; i++) { - /* calculate the offset for the layer's plane registers */ - u16 ptr = mp->layer->ptr + (i << 4); - dma_addr_t fb_addr = drm_fb_cma_get_gem_addr(state->fb, - state, i); - - malidp_hw_write(mp->hwdev, lower_32_bits(fb_addr), ptr); - malidp_hw_write(mp->hwdev, upper_32_bits(fb_addr), ptr + 4); - } + for (i = 0; i < ms->n_planes; i++) + malidp_set_plane_base_addr(fb, mp, i); malidp_de_set_mmu_control(mp, ms); @@ -657,6 +861,8 @@ static void malidp_de_plane_update(struct drm_plane *plane, mp->layer->base + MALIDP550_LS_R1_IN_SIZE); } + malidp_de_set_plane_afbc(plane); + /* first clear the rotation bits */ val = malidp_hw_read(mp->hwdev, mp->layer->base + MALIDP_LAYER_CONTROL); val &= ~LAYER_ROT_MASK; @@ -733,7 +939,26 @@ int malidp_de_planes_init(struct drm_device *drm) BIT(DRM_MODE_BLEND_PREMULTI) | BIT(DRM_MODE_BLEND_COVERAGE); u32 *formats; - int ret, i, j, n; + int ret, i = 0, j = 0, n; + u64 supported_modifiers[MODIFIERS_COUNT_MAX]; + const u64 *modifiers; + + modifiers = malidp_format_modifiers; + + if (!(map->features & MALIDP_DEVICE_AFBC_SUPPORT_SPLIT)) { + /* + * Since our hardware does not support SPLIT, so build the list + * of supported modifiers excluding SPLIT ones. + */ + while (*modifiers != DRM_FORMAT_MOD_INVALID) { + if (!(*modifiers & AFBC_SPLIT)) + supported_modifiers[j++] = *modifiers; + + modifiers++; + } + supported_modifiers[j++] = DRM_FORMAT_MOD_INVALID; + modifiers = supported_modifiers; + } formats = kcalloc(map->n_pixel_formats, sizeof(*formats), GFP_KERNEL); if (!formats) { @@ -758,9 +983,15 @@ int malidp_de_planes_init(struct drm_device *drm) plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + + /* + * All the layers except smart layer supports AFBC modifiers. + */ ret = drm_universal_plane_init(drm, &plane->base, crtcs, - &malidp_de_plane_funcs, formats, - n, NULL, plane_type, NULL); + &malidp_de_plane_funcs, formats, n, + (id == DE_SMART) ? NULL : modifiers, plane_type, + NULL); + if (ret < 0) goto cleanup; diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h index 7ce3e141464d..a0dd6e1676a8 100644 --- a/drivers/gpu/drm/arm/malidp_regs.h +++ b/drivers/gpu/drm/arm/malidp_regs.h @@ -198,10 +198,13 @@ #define MALIDP500_LV_YUV2RGB ((s16)(-0xB8)) #define MALIDP500_DE_LV_BASE 0x00100 #define MALIDP500_DE_LV_PTR_BASE 0x00124 +#define MALIDP500_DE_LV_AD_CTRL 0x00400 #define MALIDP500_DE_LG1_BASE 0x00200 #define MALIDP500_DE_LG1_PTR_BASE 0x0021c +#define MALIDP500_DE_LG1_AD_CTRL 0x0040c #define MALIDP500_DE_LG2_BASE 0x00300 #define MALIDP500_DE_LG2_PTR_BASE 0x0031c +#define MALIDP500_DE_LG2_AD_CTRL 0x00418 #define MALIDP500_SE_BASE 0x00c00 #define MALIDP500_SE_CONTROL 0x00c0c #define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c @@ -228,10 +231,13 @@ #define MALIDP550_LV_YUV2RGB 0x00084 #define MALIDP550_DE_LV1_BASE 0x00100 #define MALIDP550_DE_LV1_PTR_BASE 0x00124 +#define MALIDP550_DE_LV1_AD_CTRL 0x001B8 #define MALIDP550_DE_LV2_BASE 0x00200 #define MALIDP550_DE_LV2_PTR_BASE 0x00224 +#define MALIDP550_DE_LV2_AD_CTRL 0x002B8 #define MALIDP550_DE_LG_BASE 0x00300 #define MALIDP550_DE_LG_PTR_BASE 0x0031c +#define MALIDP550_DE_LG_AD_CTRL 0x00330 #define MALIDP550_DE_LS_BASE 0x00400 #define MALIDP550_DE_LS_PTR_BASE 0x0042c #define MALIDP550_DE_PERF_BASE 0x00500 @@ -258,6 +264,20 @@ #define MALIDP_MMU_CTRL_PX_PS(x) (1 << (8 + (x))) #define MALIDP_MMU_CTRL_PP_NUM_REQ(x) (((x) & 0x7f) << 12) +/* AFBC register offsets relative to MALIDPXXX_DE_LX_AD_CTRL */ +/* The following register offsets are common for DP500, DP550 and DP650 */ +#define MALIDP_AD_CROP_H 0x4 +#define MALIDP_AD_CROP_V 0x8 +#define MALIDP_AD_END_PTR_LOW 0xc +#define MALIDP_AD_END_PTR_HIGH 0x10 + +/* AFBC decoder Registers */ +#define MALIDP_AD_EN BIT(0) +#define MALIDP_AD_YTR BIT(4) +#define MALIDP_AD_BS BIT(8) +#define MALIDP_AD_CROP_RIGHT_OFFSET 16 +#define MALIDP_AD_CROP_BOTTOM_OFFSET 16 + /* * Starting with DP550 the register map blocks has been standardised to the * following layout: |