// SPDX-License-Identifier: GPL-2.0 /* * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. * Author: James.Qian.Wang * */ #include #include #include "d71_dev.h" #include "malidp_io.h" static u64 get_lpu_event(struct d71_pipeline *d71_pipeline) { u32 __iomem *reg = d71_pipeline->lpu_addr; u32 status, raw_status; u64 evts = 0ULL; raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS); if (raw_status & LPU_IRQ_IBSY) evts |= KOMEDA_EVENT_IBSY; if (raw_status & LPU_IRQ_EOW) evts |= KOMEDA_EVENT_EOW; if (raw_status & LPU_IRQ_OVR) evts |= KOMEDA_EVENT_OVR; if (raw_status & (LPU_IRQ_ERR | LPU_IRQ_IBSY | LPU_IRQ_OVR)) { u32 restore = 0, tbu_status; /* Check error of LPU status */ status = malidp_read32(reg, BLK_STATUS); if (status & LPU_STATUS_AXIE) { restore |= LPU_STATUS_AXIE; evts |= KOMEDA_ERR_AXIE; } if (status & LPU_STATUS_ACE0) { restore |= LPU_STATUS_ACE0; evts |= KOMEDA_ERR_ACE0; } if (status & LPU_STATUS_ACE1) { restore |= LPU_STATUS_ACE1; evts |= KOMEDA_ERR_ACE1; } if (status & LPU_STATUS_ACE2) { restore |= LPU_STATUS_ACE2; evts |= KOMEDA_ERR_ACE2; } if (status & LPU_STATUS_ACE3) { restore |= LPU_STATUS_ACE3; evts |= KOMEDA_ERR_ACE3; } if (status & LPU_STATUS_FEMPTY) { restore |= LPU_STATUS_FEMPTY; evts |= KOMEDA_EVENT_EMPTY; } if (status & LPU_STATUS_FFULL) { restore |= LPU_STATUS_FFULL; evts |= KOMEDA_EVENT_FULL; } if (restore != 0) malidp_write32_mask(reg, BLK_STATUS, restore, 0); restore = 0; /* Check errors of TBU status */ tbu_status = malidp_read32(reg, LPU_TBU_STATUS); if (tbu_status & LPU_TBU_STATUS_TCF) { restore |= LPU_TBU_STATUS_TCF; evts |= KOMEDA_ERR_TCF; } if (tbu_status & LPU_TBU_STATUS_TTNG) { restore |= LPU_TBU_STATUS_TTNG; evts |= KOMEDA_ERR_TTNG; } if (tbu_status & LPU_TBU_STATUS_TITR) { restore |= LPU_TBU_STATUS_TITR; evts |= KOMEDA_ERR_TITR; } if (tbu_status & LPU_TBU_STATUS_TEMR) { restore |= LPU_TBU_STATUS_TEMR; evts |= KOMEDA_ERR_TEMR; } if (tbu_status & LPU_TBU_STATUS_TTF) { restore |= LPU_TBU_STATUS_TTF; evts |= KOMEDA_ERR_TTF; } if (restore != 0) malidp_write32_mask(reg, LPU_TBU_STATUS, restore, 0); } malidp_write32(reg, BLK_IRQ_CLEAR, raw_status); return evts; } static u64 get_cu_event(struct d71_pipeline *d71_pipeline) { u32 __iomem *reg = d71_pipeline->cu_addr; u32 status, raw_status; u64 evts = 0ULL; raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS); if (raw_status & CU_IRQ_OVR) evts |= KOMEDA_EVENT_OVR; if (raw_status & (CU_IRQ_ERR | CU_IRQ_OVR)) { status = malidp_read32(reg, BLK_STATUS) & 0x7FFFFFFF; if (status & CU_STATUS_CPE) evts |= KOMEDA_ERR_CPE; if (status & CU_STATUS_ZME) evts |= KOMEDA_ERR_ZME; if (status & CU_STATUS_CFGE) evts |= KOMEDA_ERR_CFGE; if (status) malidp_write32_mask(reg, BLK_STATUS, status, 0); } malidp_write32(reg, BLK_IRQ_CLEAR, raw_status); return evts; } static u64 get_dou_event(struct d71_pipeline *d71_pipeline) { u32 __iomem *reg = d71_pipeline->dou_addr; u32 status, raw_status; u64 evts = 0ULL; raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS); if (raw_status & DOU_IRQ_PL0) evts |= KOMEDA_EVENT_VSYNC; if (raw_status & DOU_IRQ_UND) evts |= KOMEDA_EVENT_URUN; if (raw_status & (DOU_IRQ_ERR | DOU_IRQ_UND)) { u32 restore = 0; status = malidp_read32(reg, BLK_STATUS); if (status & DOU_STATUS_DRIFTTO) { restore |= DOU_STATUS_DRIFTTO; evts |= KOMEDA_ERR_DRIFTTO; } if (status & DOU_STATUS_FRAMETO) { restore |= DOU_STATUS_FRAMETO; evts |= KOMEDA_ERR_FRAMETO; } if (status & DOU_STATUS_TETO) { restore |= DOU_STATUS_TETO; evts |= KOMEDA_ERR_TETO; } if (status & DOU_STATUS_CSCE) { restore |= DOU_STATUS_CSCE; evts |= KOMEDA_ERR_CSCE; } if (restore != 0) malidp_write32_mask(reg, BLK_STATUS, restore, 0); } malidp_write32(reg, BLK_IRQ_CLEAR, raw_status); return evts; } static u64 get_pipeline_event(struct d71_pipeline *d71_pipeline, u32 gcu_status) { u32 evts = 0ULL; if (gcu_status & (GLB_IRQ_STATUS_LPU0 | GLB_IRQ_STATUS_LPU1)) evts |= get_lpu_event(d71_pipeline); if (gcu_status & (GLB_IRQ_STATUS_CU0 | GLB_IRQ_STATUS_CU1)) evts |= get_cu_event(d71_pipeline); if (gcu_status & (GLB_IRQ_STATUS_DOU0 | GLB_IRQ_STATUS_DOU1)) evts |= get_dou_event(d71_pipeline); return evts; } static irqreturn_t d71_irq_handler(struct komeda_dev *mdev, struct komeda_events *evts) { struct d71_dev *d71 = mdev->chip_data; u32 status, gcu_status, raw_status; gcu_status = malidp_read32(d71->gcu_addr, GLB_IRQ_STATUS); if (gcu_status & GLB_IRQ_STATUS_GCU) { raw_status = malidp_read32(d71->gcu_addr, BLK_IRQ_RAW_STATUS); if (raw_status & GCU_IRQ_CVAL0) evts->pipes[0] |= KOMEDA_EVENT_FLIP; if (raw_status & GCU_IRQ_CVAL1) evts->pipes[1] |= KOMEDA_EVENT_FLIP; if (raw_status & GCU_IRQ_ERR) { status = malidp_read32(d71->gcu_addr, BLK_STATUS); if (status & GCU_STATUS_MERR) { evts->global |= KOMEDA_ERR_MERR; malidp_write32_mask(d71->gcu_addr, BLK_STATUS, GCU_STATUS_MERR, 0); } } malidp_write32(d71->gcu_addr, BLK_IRQ_CLEAR, raw_status); } if (gcu_status & GLB_IRQ_STATUS_PIPE0) evts->pipes[0] |= get_pipeline_event(d71->pipes[0], gcu_status); if (gcu_status & GLB_IRQ_STATUS_PIPE1) evts->pipes[1] |= get_pipeline_event(d71->pipes[1], gcu_status); return IRQ_RETVAL(gcu_status); } #define ENABLED_GCU_IRQS (GCU_IRQ_CVAL0 | GCU_IRQ_CVAL1 | \ GCU_IRQ_MODE | GCU_IRQ_ERR) #define ENABLED_LPU_IRQS (LPU_IRQ_IBSY | LPU_IRQ_ERR | LPU_IRQ_EOW) #define ENABLED_CU_IRQS (CU_IRQ_OVR | CU_IRQ_ERR) #define ENABLED_DOU_IRQS (DOU_IRQ_UND | DOU_IRQ_ERR) static int d71_enable_irq(struct komeda_dev *mdev) { struct d71_dev *d71 = mdev->chip_data; struct d71_pipeline *pipe; u32 i; malidp_write32_mask(d71->gcu_addr, BLK_IRQ_MASK, ENABLED_GCU_IRQS, ENABLED_GCU_IRQS); for (i = 0; i < d71->num_pipelines; i++) { pipe = d71->pipes[i]; malidp_write32_mask(pipe->cu_addr, BLK_IRQ_MASK, ENABLED_CU_IRQS, ENABLED_CU_IRQS); malidp_write32_mask(pipe->lpu_addr, BLK_IRQ_MASK, ENABLED_LPU_IRQS, ENABLED_LPU_IRQS); malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK, ENABLED_DOU_IRQS, ENABLED_DOU_IRQS); } return 0; } static int d71_disable_irq(struct komeda_dev *mdev) { struct d71_dev *d71 = mdev->chip_data; struct d71_pipeline *pipe; u32 i; malidp_write32_mask(d71->gcu_addr, BLK_IRQ_MASK, ENABLED_GCU_IRQS, 0); for (i = 0; i < d71->num_pipelines; i++) { pipe = d71->pipes[i]; malidp_write32_mask(pipe->cu_addr, BLK_IRQ_MASK, ENABLED_CU_IRQS, 0); malidp_write32_mask(pipe->lpu_addr, BLK_IRQ_MASK, ENABLED_LPU_IRQS, 0); malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK, ENABLED_DOU_IRQS, 0); } return 0; } static void d71_on_off_vblank(struct komeda_dev *mdev, int master_pipe, bool on) { struct d71_dev *d71 = mdev->chip_data; struct d71_pipeline *pipe = d71->pipes[master_pipe]; malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK, DOU_IRQ_PL0, on ? DOU_IRQ_PL0 : 0); } static int to_d71_opmode(int core_mode) { switch (core_mode) { case KOMEDA_MODE_DISP0: return DO0_ACTIVE_MODE; case KOMEDA_MODE_DISP1: return DO1_ACTIVE_MODE; case KOMEDA_MODE_DUAL_DISP: return DO01_ACTIVE_MODE; case KOMEDA_MODE_INACTIVE: return INACTIVE_MODE; default: WARN(1, "Unknown operation mode"); return INACTIVE_MODE; } } static int d71_change_opmode(struct komeda_dev *mdev, int new_mode) { struct d71_dev *d71 = mdev->chip_data; u32 opmode = to_d71_opmode(new_mode); int ret; malidp_write32_mask(d71->gcu_addr, BLK_CONTROL, 0x7, opmode); ret = dp_wait_cond(((malidp_read32(d71->gcu_addr, BLK_CONTROL) & 0x7) == opmode), 100, 1000, 10000); return ret; } static void d71_flush(struct komeda_dev *mdev, int master_pipe, u32 active_pipes) { struct d71_dev *d71 = mdev->chip_data; u32 reg_offset = (master_pipe == 0) ? GCU_CONFIG_VALID0 : GCU_CONFIG_VALID1; malidp_write32(d71->gcu_addr, reg_offset, GCU_CONFIG_CVAL); } static int d71_reset(struct d71_dev *d71) { u32 __iomem *gcu = d71->gcu_addr; int ret; malidp_write32_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; } void d71_read_block_header(u32 __iomem *reg, struct block_header *blk) { int i; blk->block_info = malidp_read32(reg, BLK_BLOCK_INFO); if (BLOCK_INFO_BLK_TYPE(blk->block_info) == D71_BLK_TYPE_RESERVED) return; blk->pipeline_info = malidp_read32(reg, BLK_PIPELINE_INFO); /* get valid input and output ids */ for (i = 0; i < PIPELINE_INFO_N_VALID_INPUTS(blk->pipeline_info); i++) blk->input_ids[i] = malidp_read32(reg + i, BLK_VALID_INPUT_ID0); for (i = 0; i < PIPELINE_INFO_N_OUTPUTS(blk->pipeline_info); i++) blk->output_ids[i] = malidp_read32(reg + i, BLK_OUTPUT_ID0); } static void d71_cleanup(struct komeda_dev *mdev) { struct d71_dev *d71 = mdev->chip_data; if (!d71) return; devm_kfree(mdev->dev, d71); mdev->chip_data = NULL; } static int d71_enum_resources(struct komeda_dev *mdev) { struct d71_dev *d71; struct komeda_pipeline *pipe; struct block_header blk; u32 __iomem *blk_base; u32 i, value, offset; int err; d71 = devm_kzalloc(mdev->dev, sizeof(*d71), GFP_KERNEL); if (!d71) return -ENOMEM; mdev->chip_data = d71; d71->mdev = mdev; d71->gcu_addr = mdev->reg_base; d71->periph_addr = mdev->reg_base + (D71_BLOCK_OFFSET_PERIPH >> 2); err = d71_reset(d71); if (err) { DRM_ERROR("Fail to reset d71 device.\n"); goto err_cleanup; } /* probe GCU */ value = malidp_read32(d71->gcu_addr, GLB_CORE_INFO); d71->num_blocks = value & 0xFF; d71->num_pipelines = (value >> 8) & 0x7; if (d71->num_pipelines > D71_MAX_PIPELINE) { DRM_ERROR("d71 supports %d pipelines, but got: %d.\n", D71_MAX_PIPELINE, d71->num_pipelines); err = -EINVAL; goto err_cleanup; } /* Only the legacy HW has the periph block, the newer merges the periph * into GCU */ value = malidp_read32(d71->periph_addr, BLK_BLOCK_INFO); if (BLOCK_INFO_BLK_TYPE(value) != D71_BLK_TYPE_PERIPH) d71->periph_addr = NULL; if (d71->periph_addr) { /* probe PERIPHERAL in legacy HW */ value = malidp_read32(d71->periph_addr, PERIPH_CONFIGURATION_ID); d71->max_line_size = value & PERIPH_MAX_LINE_SIZE ? 4096 : 2048; d71->max_vsize = 4096; d71->num_rich_layers = value & PERIPH_NUM_RICH_LAYERS ? 2 : 1; d71->supports_dual_link = !!(value & PERIPH_SPLIT_EN); d71->integrates_tbu = !!(value & PERIPH_TBU_EN); } else { value = malidp_read32(d71->gcu_addr, GCU_CONFIGURATION_ID0); d71->max_line_size = GCU_MAX_LINE_SIZE(value); d71->max_vsize = GCU_MAX_NUM_LINES(value); value = malidp_read32(d71->gcu_addr, GCU_CONFIGURATION_ID1); d71->num_rich_layers = GCU_NUM_RICH_LAYERS(value); d71->supports_dual_link = GCU_DISPLAY_SPLIT_EN(value); d71->integrates_tbu = GCU_DISPLAY_TBU_EN(value); } for (i = 0; i < d71->num_pipelines; i++) { pipe = komeda_pipeline_add(mdev, sizeof(struct d71_pipeline), &d71_pipeline_funcs); if (IS_ERR(pipe)) { err = PTR_ERR(pipe); goto err_cleanup; } /* D71 HW doesn't update shadow registers when display output * is turning off, so when we disable all pipeline components * together with display output disable by one flush or one * operation, the disable operation updated registers will not * be flush to or valid in HW, which may leads problem. * To workaround this problem, introduce a two phase disable. * Phase1: Disabling components with display is on to make sure * the disable can be flushed to HW. * Phase2: Only turn-off display output. */ value = KOMEDA_PIPELINE_IMPROCS | BIT(KOMEDA_COMPONENT_TIMING_CTRLR); pipe->standalone_disabled_comps = value; d71->pipes[i] = to_d71_pipeline(pipe); } /* loop the register blks and probe. * NOTE: d71->num_blocks includes reserved blocks. * d71->num_blocks = GCU + valid blocks + reserved blocks */ i = 1; /* exclude GCU */ offset = D71_BLOCK_SIZE; /* skip GCU */ while (i < d71->num_blocks) { blk_base = mdev->reg_base + (offset >> 2); d71_read_block_header(blk_base, &blk); if (BLOCK_INFO_BLK_TYPE(blk.block_info) != D71_BLK_TYPE_RESERVED) { err = d71_probe_block(d71, &blk, blk_base); if (err) goto err_cleanup; } i++; offset += D71_BLOCK_SIZE; } DRM_DEBUG("total %d (out of %d) blocks are found.\n", i, d71->num_blocks); return 0; err_cleanup: d71_cleanup(mdev); return err; } #define __HW_ID(__group, __format) \ ((((__group) & 0x7) << 3) | ((__format) & 0x7)) #define RICH KOMEDA_FMT_RICH_LAYER #define SIMPLE KOMEDA_FMT_SIMPLE_LAYER #define RICH_SIMPLE (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_SIMPLE_LAYER) #define RICH_WB (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_WB_LAYER) #define RICH_SIMPLE_WB (RICH_SIMPLE | KOMEDA_FMT_WB_LAYER) #define Rot_0 DRM_MODE_ROTATE_0 #define Flip_H_V (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y | Rot_0) #define Rot_ALL_H_V (DRM_MODE_ROTATE_MASK | Flip_H_V) #define LYT_NM BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16) #define LYT_WB BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) #define LYT_NM_WB (LYT_NM | LYT_WB) #define AFB_TH AFBC(_TILED | _SPARSE) #define AFB_TH_SC_YTR AFBC(_TILED | _SC | _SPARSE | _YTR) #define AFB_TH_SC_YTR_BS AFBC(_TILED | _SC | _SPARSE | _YTR | _SPLIT) static struct komeda_format_caps d71_format_caps_table[] = { /* HW_ID | fourcc | layer_types | rots | afbc_layouts | afbc_features */ /* ABGR_2101010*/ {__HW_ID(0, 0), DRM_FORMAT_ARGB2101010, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ {__HW_ID(0, 2), DRM_FORMAT_RGBA1010102, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(0, 3), DRM_FORMAT_BGRA1010102, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, /* ABGR_8888*/ {__HW_ID(1, 0), DRM_FORMAT_ARGB8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(1, 1), DRM_FORMAT_ABGR8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(1, 1), DRM_FORMAT_ABGR8888, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ {__HW_ID(1, 2), DRM_FORMAT_RGBA8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(1, 3), DRM_FORMAT_BGRA8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, /* XBGB_8888 */ {__HW_ID(2, 0), DRM_FORMAT_XRGB8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(2, 1), DRM_FORMAT_XBGR8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(2, 2), DRM_FORMAT_RGBX8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, {__HW_ID(2, 3), DRM_FORMAT_BGRX8888, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, /* BGR_888 */ /* none-afbc RGB888 doesn't support rotation and flip */ {__HW_ID(3, 0), DRM_FORMAT_RGB888, RICH_SIMPLE_WB, Rot_0, 0, 0}, {__HW_ID(3, 1), DRM_FORMAT_BGR888, RICH_SIMPLE_WB, Rot_0, 0, 0}, {__HW_ID(3, 1), DRM_FORMAT_BGR888, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ /* BGR 16bpp */ {__HW_ID(4, 0), DRM_FORMAT_RGBA5551, RICH_SIMPLE, Flip_H_V, 0, 0}, {__HW_ID(4, 1), DRM_FORMAT_ABGR1555, RICH_SIMPLE, Flip_H_V, 0, 0}, {__HW_ID(4, 1), DRM_FORMAT_ABGR1555, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */ {__HW_ID(4, 2), DRM_FORMAT_RGB565, RICH_SIMPLE, Flip_H_V, 0, 0}, {__HW_ID(4, 3), DRM_FORMAT_BGR565, RICH_SIMPLE, Flip_H_V, 0, 0}, {__HW_ID(4, 3), DRM_FORMAT_BGR565, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */ {__HW_ID(4, 4), DRM_FORMAT_R8, SIMPLE, Rot_0, 0, 0}, /* YUV 444/422/420 8bit */ {__HW_ID(5, 1), DRM_FORMAT_YUYV, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ {__HW_ID(5, 2), DRM_FORMAT_YUYV, RICH, Flip_H_V, 0, 0}, {__HW_ID(5, 3), DRM_FORMAT_UYVY, RICH, Flip_H_V, 0, 0}, {__HW_ID(5, 6), DRM_FORMAT_NV12, RICH, Flip_H_V, 0, 0}, {__HW_ID(5, 6), DRM_FORMAT_YUV420_8BIT, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ {__HW_ID(5, 7), DRM_FORMAT_YUV420, RICH, Flip_H_V, 0, 0}, /* YUV 10bit*/ {__HW_ID(6, 6), DRM_FORMAT_X0L2, RICH, Flip_H_V, 0, 0}, {__HW_ID(6, 7), DRM_FORMAT_P010, RICH, Flip_H_V, 0, 0}, {__HW_ID(6, 7), DRM_FORMAT_YUV420_10BIT, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, }; static bool d71_format_mod_supported(const struct komeda_format_caps *caps, u32 layer_type, u64 modifier, u32 rot) { uint64_t layout = modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK; if ((layout == AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) && drm_rotation_90_or_270(rot)) { DRM_DEBUG_ATOMIC("D71 doesn't support ROT90 for WB-AFBC.\n"); return false; } return true; } static void d71_init_fmt_tbl(struct komeda_dev *mdev) { struct komeda_format_caps_table *table = &mdev->fmt_tbl; table->format_caps = d71_format_caps_table; table->format_mod_supported = d71_format_mod_supported; table->n_formats = ARRAY_SIZE(d71_format_caps_table); } static int d71_connect_iommu(struct komeda_dev *mdev) { struct d71_dev *d71 = mdev->chip_data; u32 __iomem *reg = d71->gcu_addr; u32 check_bits = (d71->num_pipelines == 2) ? GCU_STATUS_TCS0 | GCU_STATUS_TCS1 : GCU_STATUS_TCS0; int i, ret; if (!d71->integrates_tbu) return -1; malidp_write32_mask(reg, BLK_CONTROL, 0x7, TBU_CONNECT_MODE); ret = dp_wait_cond(has_bits(check_bits, malidp_read32(reg, BLK_STATUS)), 100, 1000, 1000); if (ret < 0) { DRM_ERROR("timed out connecting to TCU!\n"); malidp_write32_mask(reg, BLK_CONTROL, 0x7, INACTIVE_MODE); return ret; } for (i = 0; i < d71->num_pipelines; i++) malidp_write32_mask(d71->pipes[i]->lpu_addr, LPU_TBU_CONTROL, LPU_TBU_CTRL_TLBPEN, LPU_TBU_CTRL_TLBPEN); return 0; } static int d71_disconnect_iommu(struct komeda_dev *mdev) { struct d71_dev *d71 = mdev->chip_data; u32 __iomem *reg = d71->gcu_addr; u32 check_bits = (d71->num_pipelines == 2) ? GCU_STATUS_TCS0 | GCU_STATUS_TCS1 : GCU_STATUS_TCS0; int ret; malidp_write32_mask(reg, BLK_CONTROL, 0x7, TBU_DISCONNECT_MODE); ret = dp_wait_cond(((malidp_read32(reg, BLK_STATUS) & check_bits) == 0), 100, 1000, 1000); if (ret < 0) { DRM_ERROR("timed out disconnecting from TCU!\n"); malidp_write32_mask(reg, BLK_CONTROL, 0x7, INACTIVE_MODE); } return ret; } static const struct komeda_dev_funcs d71_chip_funcs = { .init_format_table = d71_init_fmt_tbl, .enum_resources = d71_enum_resources, .cleanup = d71_cleanup, .irq_handler = d71_irq_handler, .enable_irq = d71_enable_irq, .disable_irq = d71_disable_irq, .on_off_vblank = d71_on_off_vblank, .change_opmode = d71_change_opmode, .flush = d71_flush, .connect_iommu = d71_connect_iommu, .disconnect_iommu = d71_disconnect_iommu, .dump_register = d71_dump, }; const struct komeda_dev_funcs * d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip) { const struct komeda_dev_funcs *funcs; u32 product_id; chip->core_id = malidp_read32(reg_base, GLB_CORE_ID); product_id = MALIDP_CORE_ID_PRODUCT_ID(chip->core_id); switch (product_id) { case MALIDP_D71_PRODUCT_ID: case MALIDP_D32_PRODUCT_ID: funcs = &d71_chip_funcs; break; default: DRM_ERROR("Unsupported product: 0x%x\n", product_id); return NULL; } chip->arch_id = malidp_read32(reg_base, GLB_ARCH_ID); chip->core_info = malidp_read32(reg_base, GLB_CORE_INFO); chip->bus_width = D71_BUS_WIDTH_16_BYTES; return funcs; }