aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c')
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c435
1 files changed, 425 insertions, 10 deletions
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..3a7248d42376 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,425 @@
* 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 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 > 0 ? 0 : -ETIMEDOUT;
+}
+
+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 > 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) \
@@ -90,22 +502,25 @@ static void d71_init_fmt_tbl(struct komeda_dev *mdev)
table->n_formats = ARRAY_SIZE(d71_format_caps_table);
}
-static struct komeda_dev_funcs d71_chip_funcs = {
+static const 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,
+ .on_off_vblank = d71_on_off_vblank,
+ .change_opmode = d71_change_opmode,
+ .flush = d71_flush,
};
-#define GLB_ARCH_ID 0x000
-#define GLB_CORE_ID 0x004
-#define GLB_CORE_INFO 0x008
-
-struct komeda_dev_funcs *
+const struct komeda_dev_funcs *
d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip)
{
chip->arch_id = malidp_read32(reg_base, GLB_ARCH_ID);
chip->core_id = malidp_read32(reg_base, GLB_CORE_ID);
chip->core_info = malidp_read32(reg_base, GLB_CORE_INFO);
+ chip->bus_width = D71_BUS_WIDTH_16_BYTES;
return &d71_chip_funcs;
}