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 12eda3b99e4c..fd43177895c4 100644 --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c @@ -243,6 +243,37 @@ static int d71_disable_irq(struct komeda_dev *mdev) return 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) { @@ -469,6 +500,7 @@ static struct komeda_dev_funcs d71_chip_funcs = { .irq_handler = d71_irq_handler, .enable_irq = d71_enable_irq, .disable_irq = d71_disable_irq, + .change_opmode = d71_change_opmode, .flush = d71_flush, }; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index 902f3992083e..ef902bf3bba4 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -44,6 +44,110 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc, return 0; } +u32 komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st) +{ + unsigned long mclk = kcrtc_st->base.adjusted_mode.clock * 1000; + + return mclk; +} + +/* For active a crtc, mainly need two parts of preparation + * 1. adjust display operation mode. + * 2. enable needed clk + */ +int +komeda_crtc_prepare(struct komeda_crtc *kcrtc) +{ + struct komeda_dev *mdev = kcrtc->base.dev->dev_private; + struct komeda_pipeline *master = kcrtc->master; + struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(kcrtc->base.state); + unsigned long pxlclk_rate = kcrtc_st->base.adjusted_mode.clock * 1000; + u32 new_mode; + int err; + + mutex_lock(&mdev->lock); + + new_mode = mdev->dpmode | BIT(master->id); + if (WARN_ON(new_mode == mdev->dpmode)) { + err = 0; + goto unlock; + } + + err = mdev->funcs->change_opmode(mdev, new_mode); + if (err) { + DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,", + mdev->dpmode, new_mode); + goto unlock; + } + + mdev->dpmode = new_mode; + /* Only need to enable mclk on single display mode, but no need to + * enable mclk it on dual display mode, since the dual mode always + * switch from single display mode, the mclk already enabled, no need + * to enable it again. + */ + if (new_mode != KOMEDA_MODE_DUAL_DISP) { + err = clk_set_rate(mdev->mclk, komeda_calc_mclk(kcrtc_st)); + if (err) + DRM_ERROR("failed to set mclk.\n"); + err = clk_prepare_enable(mdev->mclk); + if (err) + DRM_ERROR("failed to enable mclk.\n"); + } + + err = clk_prepare_enable(master->aclk); + if (err) + DRM_ERROR("failed to enable axi clk for pipe%d.\n", master->id); + err = clk_set_rate(master->pxlclk, pxlclk_rate); + if (err) + DRM_ERROR("failed to set pxlclk for pipe%d\n", master->id); + err = clk_prepare_enable(master->pxlclk); + if (err) + DRM_ERROR("failed to enable pxl clk for pipe%d.\n", master->id); + +unlock: + mutex_unlock(&mdev->lock); + + return err; +} + +int +komeda_crtc_unprepare(struct komeda_crtc *kcrtc) +{ + struct komeda_dev *mdev = kcrtc->base.dev->dev_private; + struct komeda_pipeline *master = kcrtc->master; + u32 new_mode; + int err; + + mutex_lock(&mdev->lock); + + new_mode = mdev->dpmode & (~BIT(master->id)); + + if (WARN_ON(new_mode == mdev->dpmode)) { + err = 0; + goto unlock; + } + + err = mdev->funcs->change_opmode(mdev, new_mode); + if (err) { + DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,", + mdev->dpmode, new_mode); + goto unlock; + } + + mdev->dpmode = new_mode; + + clk_disable_unprepare(master->pxlclk); + clk_disable_unprepare(master->aclk); + if (new_mode == KOMEDA_MODE_INACTIVE) + clk_disable_unprepare(mdev->mclk); + +unlock: + mutex_unlock(&mdev->lock); + + return err; +} + void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, struct komeda_events *evts) { diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c index 24548b87e182..131f26692a3d 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c @@ -151,6 +151,8 @@ struct komeda_dev *komeda_dev_create(struct device *dev) if (!mdev) return ERR_PTR(-ENOMEM); + mutex_init(&mdev->lock); + mdev->dev = dev; mdev->reg_base = devm_ioremap_resource(dev, io_res); if (IS_ERR(mdev->reg_base)) { diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h index 0bd38bdf0518..1ad1f6e49854 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h @@ -106,11 +106,34 @@ struct komeda_dev_funcs { /** @dump_register: Optional, dump registers to seq_file */ void (*dump_register)(struct komeda_dev *mdev, struct seq_file *seq); + /** + * @change_opmode: + * + * Notify HW to switch to a new display operation mode. + */ + int (*change_opmode)(struct komeda_dev *mdev, int new_mode); /** @flush: Notify the HW to flush or kickoff the update */ void (*flush)(struct komeda_dev *mdev, int master_pipe, u32 active_pipes); }; +/** + * DISPLAY_MODE describes how many display been enabled, and which will be + * passed to CHIP by &komeda_dev_funcs->change_opmode(), then CHIP can do the + * pipeline resources assignment according to this usage hint. + * - KOMEDA_MODE_DISP0: Only one display enabled, pipeline-0 work as master. + * - KOMEDA_MODE_DISP1: Only one display enabled, pipeline-0 work as master. + * - KOMEDA_MODE_DUAL_DISP: Dual display mode, both display has been enabled. + * And D71 supports assign two pipelines to one single display on mode + * KOMEDA_MODE_DISP0/DISP1 + */ +enum { + KOMEDA_MODE_INACTIVE = 0, + KOMEDA_MODE_DISP0 = BIT(0), + KOMEDA_MODE_DISP1 = BIT(1), + KOMEDA_MODE_DUAL_DISP = KOMEDA_MODE_DISP0 | KOMEDA_MODE_DISP1, +}; + /** * struct komeda_dev * @@ -133,6 +156,9 @@ struct komeda_dev { /** @irq: irq number */ int irq; + struct mutex lock; /* used to protect dpmode */ + u32 dpmode; /* current display mode */ + int n_pipelines; struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES];