diff options
Diffstat (limited to 'drivers/gpu/drm')
49 files changed, 1898 insertions, 149 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index f8c58c425eb9..fdd0ca4b0f0b 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -23,7 +23,7 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -FULL_AMD_PATH=$(src)/.. +FULL_AMD_PATH=$(srctree)/$(src)/.. DISPLAY_FOLDER_NAME=display FULL_AMD_DISPLAY_PATH = $(FULL_AMD_PATH)/$(DISPLAY_FOLDER_NAME) diff --git a/drivers/gpu/drm/arm/display/include/malidp_product.h b/drivers/gpu/drm/arm/display/include/malidp_product.h index b35fc5db866b..1053b11352eb 100644 --- a/drivers/gpu/drm/arm/display/include/malidp_product.h +++ b/drivers/gpu/drm/arm/display/include/malidp_product.h @@ -20,4 +20,16 @@ /* Mali-display product IDs */ #define MALIDP_D71_PRODUCT_ID 0x0071 +union komeda_config_id { + struct { + __u32 max_line_sz:16, + n_pipelines:2, + n_scalers:2, /* number of scalers per pipeline */ + n_layers:3, /* number of layers per pipeline */ + n_richs:3, /* number of rich layers per pipeline */ + reserved_bits:6; + }; + __u32 value; +}; + #endif /* _MALIDP_PRODUCT_H_ */ diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile index d593125236ae..412eeba8c39f 100644 --- a/drivers/gpu/drm/arm/display/komeda/Makefile +++ b/drivers/gpu/drm/arm/display/komeda/Makefile @@ -1,14 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 ccflags-y := \ - -I$(src)/../include \ - -I$(src) + -I $(srctree)/$(src)/../include \ + -I $(srctree)/$(src) komeda-y := \ komeda_drv.o \ komeda_dev.o \ komeda_format_caps.o \ komeda_pipeline.o \ + komeda_pipeline_state.o \ komeda_framebuffer.o \ komeda_kms.o \ komeda_crtc.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 index c56cfc2de147..031e5f305a3c 100644 --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c @@ -391,7 +391,7 @@ static void d71_compiz_dump(struct komeda_component *c, struct seq_file *sf) seq_printf(sf, "CU_USER_HIGH:\t\t0x%X\n", v[1]); } -struct komeda_component_funcs d71_compiz_funcs = { +static struct komeda_component_funcs d71_compiz_funcs = { .update = d71_compiz_update, .disable = d71_component_disable, .dump_register = d71_compiz_dump, @@ -467,7 +467,7 @@ static void d71_improc_dump(struct komeda_component *c, struct seq_file *sf) seq_printf(sf, "IPS_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]); } -struct komeda_component_funcs d71_improc_funcs = { +static struct komeda_component_funcs d71_improc_funcs = { .update = d71_improc_update, .disable = d71_component_disable, .dump_register = d71_improc_dump, @@ -543,7 +543,8 @@ static void d71_timing_ctrlr_update(struct komeda_component *c, malidp_write32(reg, BLK_CONTROL, value); } -void d71_timing_ctrlr_dump(struct komeda_component *c, struct seq_file *sf) +static void d71_timing_ctrlr_dump(struct komeda_component *c, + struct seq_file *sf) { u32 v[8], i; @@ -579,7 +580,7 @@ void d71_timing_ctrlr_dump(struct komeda_component *c, struct seq_file *sf) seq_printf(sf, "BS_USER:\t\t0x%X\n", v[4]); } -struct komeda_component_funcs d71_timing_ctrlr_funcs = { +static struct komeda_component_funcs d71_timing_ctrlr_funcs = { .update = d71_timing_ctrlr_update, .disable = d71_timing_ctrlr_disable, .dump_register = d71_timing_ctrlr_dump, 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 72631d673f85..34506ef7ad40 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,56 @@ static int d71_disable_irq(struct komeda_dev *mdev) 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; @@ -459,6 +509,9 @@ static struct komeda_dev_funcs d71_chip_funcs = { .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, }; struct komeda_dev_funcs * @@ -467,6 +520,7 @@ 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; } diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index f88a14927be9..62fad59f5a6a 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -18,6 +18,144 @@ #include "komeda_dev.h" #include "komeda_kms.h" +/** + * komeda_crtc_atomic_check - build display output data flow + * @crtc: DRM crtc + * @state: the crtc state object + * + * crtc_atomic_check is the final check stage, so beside build a display data + * pipeline according to the crtc_state, but still needs to release or disable + * the unclaimed pipeline resources. + * + * RETURNS: + * Zero for success or -errno + */ +static int +komeda_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state); + int err; + + if (state->active) { + err = komeda_build_display_data_flow(kcrtc, kcrtc_st); + if (err) + return err; + } + + /* release unclaimed pipeline resources */ + err = komeda_release_unclaimed_resources(kcrtc->master, kcrtc_st); + if (err) + return err; + + return 0; +} + +static 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 + */ +static 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; +} + +static 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) { @@ -31,15 +169,264 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, 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"); + if (events & KOMEDA_EVENT_FLIP) { + unsigned long flags; + struct drm_pending_vblank_event *event; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (kcrtc->disable_done) { + complete_all(kcrtc->disable_done); + kcrtc->disable_done = NULL; + } else if (crtc->state->event) { + event = crtc->state->event; + /* + * Consume event before notifying drm core that flip + * happened. + */ + crtc->state->event = NULL; + drm_crtc_send_vblank_event(crtc, event); + } else { + DRM_WARN("CRTC[%d]: FLIP happen but no pending commit.\n", + drm_crtc_index(&kcrtc->base)); + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + } +} + +static void +komeda_crtc_do_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old) +{ + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state); + struct komeda_dev *mdev = kcrtc->base.dev->dev_private; + struct komeda_pipeline *master = kcrtc->master; + + DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n", + drm_crtc_index(crtc), + kcrtc_st->active_pipes, kcrtc_st->affected_pipes); + + /* step 1: update the pipeline/component state to HW */ + if (has_bit(master->id, kcrtc_st->affected_pipes)) + komeda_pipeline_update(master, old->state); + + /* step 2: notify the HW to kickoff the update */ + mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes); } -struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = { +static void +komeda_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old) +{ + komeda_crtc_prepare(to_kcrtc(crtc)); + drm_crtc_vblank_on(crtc); + komeda_crtc_do_flush(crtc, old); +} + +static void +komeda_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old) +{ + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + struct komeda_crtc_state *old_st = to_kcrtc_st(old); + struct komeda_dev *mdev = crtc->dev->dev_private; + struct komeda_pipeline *master = kcrtc->master; + struct completion *disable_done = &crtc->state->commit->flip_done; + struct completion temp; + int timeout; + + DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x.\n", + drm_crtc_index(crtc), + old_st->active_pipes, old_st->affected_pipes); + + if (has_bit(master->id, old_st->active_pipes)) + komeda_pipeline_disable(master, old->state); + + /* crtc_disable has two scenarios according to the state->active switch. + * 1. active -> inactive + * this commit is a disable commit. and the commit will be finished + * or done after the disable operation. on this case we can directly + * use the crtc->state->event to tracking the HW disable operation. + * 2. active -> active + * the crtc->commit is not for disable, but a modeset operation when + * crtc is active, such commit actually has been completed by 3 + * DRM operations: + * crtc_disable, update_planes(crtc_flush), crtc_enable + * so on this case the crtc->commit is for the whole process. + * we can not use it for tracing the disable, we need a temporary + * flip_done for tracing the disable. and crtc->state->event for + * the crtc_enable operation. + * That's also the reason why skip modeset commit in + * komeda_crtc_atomic_flush() + */ + if (crtc->state->active) { + struct komeda_pipeline_state *pipe_st; + /* clear the old active_comps to zero */ + pipe_st = komeda_pipeline_get_old_state(master, old->state); + pipe_st->active_comps = 0; + + init_completion(&temp); + kcrtc->disable_done = &temp; + disable_done = &temp; + } + + mdev->funcs->flush(mdev, master->id, 0); + + /* wait the disable take affect.*/ + timeout = wait_for_completion_timeout(disable_done, HZ); + if (timeout == 0) { + DRM_ERROR("disable pipeline%d timeout.\n", kcrtc->master->id); + if (crtc->state->active) { + unsigned long flags; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + kcrtc->disable_done = NULL; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + } + } + + drm_crtc_vblank_off(crtc); + komeda_crtc_unprepare(kcrtc); +} + +static void +komeda_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old) +{ + /* commit with modeset will be handled in enable/disable */ + if (drm_atomic_crtc_needs_modeset(crtc->state)) + return; + + komeda_crtc_do_flush(crtc, old); +} + +static enum drm_mode_status +komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m) +{ + struct komeda_dev *mdev = crtc->dev->dev_private; + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + struct komeda_pipeline *master = kcrtc->master; + long mode_clk, pxlclk; + + if (m->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + /* main clock/AXI clk must be faster than pxlclk*/ + mode_clk = m->clock * 1000; + pxlclk = clk_round_rate(master->pxlclk, mode_clk); + if (pxlclk != mode_clk) { + DRM_DEBUG_ATOMIC("pxlclk doesn't support %ld Hz\n", mode_clk); + + return MODE_NOCLOCK; + } + + if (clk_round_rate(mdev->mclk, mode_clk) < pxlclk) { + DRM_DEBUG_ATOMIC("mclk can't satisfy the requirement of %s-clk: %ld.\n", + m->name, pxlclk); + + return MODE_CLOCK_HIGH; + } + + if (clk_round_rate(master->aclk, mode_clk) < pxlclk) { + DRM_DEBUG_ATOMIC("aclk can't satisfy the requirement of %s-clk: %ld.\n", + m->name, pxlclk); + + return MODE_CLOCK_HIGH; + } + + return MODE_OK; +} + +static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *m, + struct drm_display_mode *adjusted_mode) +{ + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + struct komeda_pipeline *master = kcrtc->master; + long mode_clk = m->clock * 1000; + + adjusted_mode->clock = clk_round_rate(master->pxlclk, mode_clk) / 1000; + + return true; +} + +static struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = { + .atomic_check = komeda_crtc_atomic_check, + .atomic_flush = komeda_crtc_atomic_flush, + .atomic_enable = komeda_crtc_atomic_enable, + .atomic_disable = komeda_crtc_atomic_disable, + .mode_valid = komeda_crtc_mode_valid, + .mode_fixup = komeda_crtc_mode_fixup, }; +static void komeda_crtc_reset(struct drm_crtc *crtc) +{ + struct komeda_crtc_state *state; + + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(to_kcrtc_st(crtc->state)); + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + crtc->state = &state->base; + crtc->state->crtc = crtc; + } +} + +static struct drm_crtc_state * +komeda_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct komeda_crtc_state *old = to_kcrtc_st(crtc->state); + struct komeda_crtc_state *new; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &new->base); + + new->affected_pipes = old->active_pipes; + + return &new->base; +} + +static void komeda_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + __drm_atomic_helper_crtc_destroy_state(state); + kfree(to_kcrtc_st(state)); +} + +static int komeda_crtc_vblank_enable(struct drm_crtc *crtc) +{ + struct komeda_dev *mdev = crtc->dev->dev_private; + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + + mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, true); + return 0; +} + +static void komeda_crtc_vblank_disable(struct drm_crtc *crtc) +{ + struct komeda_dev *mdev = crtc->dev->dev_private; + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + + mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, false); +} + static const struct drm_crtc_funcs komeda_crtc_funcs = { + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = komeda_crtc_reset, + .atomic_duplicate_state = komeda_crtc_atomic_duplicate_state, + .atomic_destroy_state = komeda_crtc_atomic_destroy_state, + .enable_vblank = komeda_crtc_vblank_enable, + .disable_vblank = komeda_crtc_vblank_disable, }; int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c index 24548b87e182..ca3599e4a4d3 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c @@ -59,6 +59,48 @@ static void komeda_debugfs_init(struct komeda_dev *mdev) } #endif +static ssize_t +core_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct komeda_dev *mdev = dev_to_mdev(dev); + + return snprintf(buf, PAGE_SIZE, "0x%08x\n", mdev->chip.core_id); +} +static DEVICE_ATTR_RO(core_id); + +static ssize_t +config_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct komeda_dev *mdev = dev_to_mdev(dev); + struct komeda_pipeline *pipe = mdev->pipelines[0]; + union komeda_config_id config_id; + int i; + + memset(&config_id, 0, sizeof(config_id)); + + config_id.max_line_sz = pipe->layers[0]->hsize_in.end; + config_id.n_pipelines = mdev->n_pipelines; + config_id.n_scalers = pipe->n_scalers; + config_id.n_layers = pipe->n_layers; + config_id.n_richs = 0; + for (i = 0; i < pipe->n_layers; i++) { + if (pipe->layers[i]->layer_type == KOMEDA_FMT_RICH_LAYER) + config_id.n_richs++; + } + return snprintf(buf, PAGE_SIZE, "0x%08x\n", config_id.value); +} +static DEVICE_ATTR_RO(config_id); + +static struct attribute *komeda_sysfs_entries[] = { + &dev_attr_core_id.attr, + &dev_attr_config_id.attr, + NULL, +}; + +static struct attribute_group komeda_sysfs_attr_group = { + .attrs = komeda_sysfs_entries, +}; + static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) { struct komeda_pipeline *pipe; @@ -151,6 +193,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)) { @@ -205,6 +249,12 @@ struct komeda_dev *komeda_dev_create(struct device *dev) goto err_cleanup; } + err = sysfs_create_group(&dev->kobj, &komeda_sysfs_attr_group); + if (err) { + DRM_ERROR("create sysfs group failed.\n"); + goto err_cleanup; + } + #ifdef CONFIG_DEBUG_FS komeda_debugfs_init(mdev); #endif @@ -222,6 +272,8 @@ void komeda_dev_destroy(struct komeda_dev *mdev) struct komeda_dev_funcs *funcs = mdev->funcs; int i; + sysfs_remove_group(&dev->kobj, &komeda_sysfs_attr_group); + #ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(mdev->debugfs_root); #endif diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h index 8eae2620ce77..29e03c4e1ffc 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h @@ -103,9 +103,38 @@ struct komeda_dev_funcs { int (*enable_irq)(struct komeda_dev *mdev); /** @disable_irq: disable irq */ int (*disable_irq)(struct komeda_dev *mdev); + /** @on_off_vblank: notify HW to on/off vblank */ + void (*on_off_vblank)(struct komeda_dev *mdev, + int master_pipe, bool on); /** @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, }; /** @@ -116,21 +145,31 @@ struct komeda_dev_funcs { * control-abilites of device. */ struct komeda_dev { + /** @dev: the base device structure */ struct device *dev; + /** @reg_base: the base address of komeda io space */ u32 __iomem *reg_base; + /** @chip: the basic chip information */ struct komeda_chip_info chip; /** @fmt_tbl: initialized by &komeda_dev_funcs->init_format_table */ struct komeda_format_caps_table fmt_tbl; /** @pclk: APB clock for register access */ struct clk *pclk; - /** @mck: HW main engine clk */ + /** @mclk: HW main engine clk */ struct clk *mclk; /** @irq: irq number */ int irq; + /** @lock: used to protect dpmode */ + struct mutex lock; + /** @dpmode: current display mode */ + u32 dpmode; + + /** @n_pipelines: the number of pipe in @pipelines */ int n_pipelines; + /** @pipelines: the komeda pipelines */ struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES]; /** @funcs: chip funcs to access to HW */ @@ -143,6 +182,7 @@ struct komeda_dev { */ void *chip_data; + /** @debugfs_root: root directory of komeda debugfs */ struct dentry *debugfs_root; }; @@ -158,4 +198,6 @@ d71_identify(u32 __iomem *reg, struct komeda_chip_info *chip); struct komeda_dev *komeda_dev_create(struct device *dev); void komeda_dev_destroy(struct komeda_dev *mdev); +struct komeda_dev *dev_to_mdev(struct device *dev); + #endif /*_KOMEDA_DEV_H_*/ diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c index 2bdd189b041d..cfa5068d9d1e 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c @@ -17,6 +17,13 @@ struct komeda_drv { struct komeda_kms_dev *kms; }; +struct komeda_dev *dev_to_mdev(struct device *dev) +{ + struct komeda_drv *mdrv = dev_get_drvdata(dev); + + return mdrv ? mdrv->mdev : NULL; +} + static void komeda_unbind(struct device *dev) { struct komeda_drv *mdrv = dev_get_drvdata(dev); @@ -120,7 +127,7 @@ static const struct komeda_product_data komeda_products[] = { }, }; -const struct of_device_id komeda_of_match[] = { +static const struct of_device_id komeda_of_match[] = { { .compatible = "arm,mali-d71", .data = &komeda_products[MALI_D71], }, {}, }; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h index 0de2e4a2afd2..ea2fe190c1e3 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h @@ -10,11 +10,16 @@ #include <drm/drm_framebuffer.h> #include "komeda_format_caps.h" -/** struct komeda_fb - entend drm_framebuffer with komeda attribute */ +/** + * struct komeda_fb - Entending drm_framebuffer with komeda attribute + */ struct komeda_fb { /** @base: &drm_framebuffer */ struct drm_framebuffer base; - /* @format_caps: &komeda_format_caps */ + /** + * @format_caps: + * extends drm_format_info for komeda specific information + */ const struct komeda_format_caps *format_caps; /** @aligned_w: aligned frame buffer width */ u32 aligned_w; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index b214edbfbbc6..86f6542afb40 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -26,10 +26,10 @@ static int komeda_gem_cma_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - u32 alignment = 16; /* TODO get alignment from dev */ + struct komeda_dev *mdev = dev->dev_private; + u32 pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), - alignment); + args->pitch = ALIGN(pitch, mdev->chip.bus_width); return drm_gem_cma_dumb_create_internal(file, dev, args); } @@ -100,9 +100,37 @@ static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = { .atomic_commit_tail = komeda_kms_commit_tail, }; +static int komeda_kms_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_st, *new_crtc_st; + int i, err; + + err = drm_atomic_helper_check_modeset(dev, state); + if (err) + return err; + + /* komeda need to re-calculate resource assumption in every commit + * so need to add all affected_planes (even unchanged) to + * drm_atomic_state. + */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_st, new_crtc_st, i) { + err = drm_atomic_add_affected_planes(state, crtc); + if (err) + return err; + } + + err = drm_atomic_helper_check_planes(dev, state); + if (err) + return err; + + return 0; +} + static const struct drm_mode_config_funcs komeda_mode_config_funcs = { .fb_create = komeda_fb_create, - .atomic_check = drm_atomic_helper_check, + .atomic_check = komeda_kms_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -184,6 +212,7 @@ uninstall_irq: drm_irq_uninstall(drm); cleanup_mode_config: drm_mode_config_cleanup(drm); + komeda_kms_cleanup_private_objs(kms); free_kms: kfree(kms); return ERR_PTR(err); @@ -198,7 +227,7 @@ void komeda_kms_detach(struct komeda_kms_dev *kms) drm_dev_unregister(drm); drm_irq_uninstall(drm); component_unbind_all(mdev->dev, drm); - komeda_kms_cleanup_private_objs(mdev); + komeda_kms_cleanup_private_objs(kms); drm_mode_config_cleanup(drm); drm->dev_private = NULL; drm_dev_put(drm); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h index 15ac8b85506c..ac3d9209b4d9 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h @@ -15,7 +15,9 @@ #include <video/videomode.h> #include <video/display_timing.h> -/** struct komeda_plane - komeda instance of drm_plane */ +/** + * struct komeda_plane - komeda instance of drm_plane + */ struct komeda_plane { /** @base: &drm_plane */ struct drm_plane base; @@ -70,9 +72,14 @@ struct komeda_crtc { * merge into the master. */ struct komeda_pipeline *slave; + + /** @disable_done: this flip_done is for tracing the disable */ + struct completion *disable_done; }; -/** struct komeda_crtc_state */ +/** + * struct komeda_crtc_state + */ struct komeda_crtc_state { /** @base: &drm_crtc_state */ struct drm_crtc_state base; @@ -80,7 +87,15 @@ struct komeda_crtc_state { /* private properties */ /* computed state which are used by validate/check */ + /** + * @affected_pipes: + * the affected pipelines in once display instance + */ u32 affected_pipes; + /** + * @active_pipes: + * the active pipelines in once display instance + */ u32 active_pipes; }; @@ -108,7 +123,7 @@ int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev); int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev); 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_kms_cleanup_private_objs(struct komeda_kms_dev *kms); void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, struct komeda_events *evts); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c index 07398efc40f5..c379439c6194 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c @@ -62,7 +62,7 @@ void komeda_pipeline_destroy(struct komeda_dev *mdev, devm_kfree(mdev->dev, pipe); } -struct komeda_component ** +static struct komeda_component ** komeda_pipeline_get_component_pos(struct komeda_pipeline *pipe, int id) { struct komeda_dev *mdev = pipe->mdev; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h index c30a790d0712..b1f813a349a4 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h @@ -90,32 +90,35 @@ struct komeda_component { u32 __iomem *reg; /** @id: component id */ u32 id; - /** @hw_ic: component hw id, - * which is initialized by chip and used by chip only + /** + * @hw_id: component hw id, + * which is initialized by chip and used by chip only */ u32 hw_id; /** * @max_active_inputs: - * @max_active_outpus: + * @max_active_outputs: * - * maximum number of inputs/outputs that can be active in the same time + * maximum number of inputs/outputs that can be active at the same time * Note: * the number isn't the bit number of @supported_inputs or * @supported_outputs, but may be less than it, since component may not * support enabling all @supported_inputs/outputs at the same time. */ u8 max_active_inputs; + /** @max_active_outputs: maximum number of outputs */ u8 max_active_outputs; /** * @supported_inputs: * @supported_outputs: * - * bitmask of BIT(component->id) for the supported inputs/outputs + * bitmask of BIT(component->id) for the supported inputs/outputs, * describes the possibilities of how a component is linked into a * pipeline. */ u32 supported_inputs; + /** @supported_outputs: bitmask of supported output componenet ids */ u32 supported_outputs; /** @@ -134,7 +137,8 @@ struct komeda_component { struct komeda_component_output { /** @component: indicate which component the data comes from */ struct komeda_component *component; - /** @output_port: + /** + * @output_port: * the output port of the &komeda_component_output.component */ u8 output_port; @@ -150,11 +154,12 @@ struct komeda_component_output { struct komeda_component_state { /** @obj: tracking component_state by drm_atomic_state */ struct drm_private_state obj; + /** @component: backpointer to the component */ struct komeda_component *component; /** * @binding_user: - * currently bound user, the user can be crtc/plane/wb_conn, which is - * valid decided by @component and @inputs + * currently bound user, the user can be @crtc, @plane or @wb_conn, + * which is valid decided by @component and @inputs * * - Layer: its user always is plane. * - compiz/improc/timing_ctrlr: the user is crtc. @@ -162,20 +167,24 @@ struct komeda_component_state { * - scaler: plane when input is layer, wb_conn if input is compiz. */ union { + /** @crtc: backpointer for user crtc */ struct drm_crtc *crtc; + /** @plane: backpointer for user plane */ struct drm_plane *plane; + /** @wb_conn: backpointer for user wb_connector */ struct drm_connector *wb_conn; void *binding_user; }; + /** * @active_inputs: * * active_inputs is bitmask of @inputs index * - * - active_inputs = changed_active_inputs + unchanged_active_inputs - * - affected_inputs = old->active_inputs + new->active_inputs; + * - active_inputs = changed_active_inputs | unchanged_active_inputs + * - affected_inputs = old->active_inputs | new->active_inputs; * - disabling_inputs = affected_inputs ^ active_inputs; - * - changed_inputs = disabling_inputs + changed_active_inputs; + * - changed_inputs = disabling_inputs | changed_active_inputs; * * NOTE: * changed_inputs doesn't include all active_input but only @@ -183,7 +192,9 @@ struct komeda_component_state { * level for dirty update. */ u16 active_inputs; + /** @changed_active_inputs: bitmask of the changed @active_inputs */ u16 changed_active_inputs; + /** @affected_inputs: bitmask for affected @inputs */ u16 affected_inputs; /** * @inputs: @@ -278,6 +289,22 @@ struct komeda_timing_ctrlr_state { struct komeda_component_state base; }; +/* Why define A separated structure but not use plane_state directly ? + * 1. Komeda supports layer_split which means a plane_state can be split and + * handled by two layers, one layer only handle half of plane image. + * 2. Fix up the user properties according to HW's capabilities, like user + * set rotation to R180, but HW only supports REFLECT_X+Y. the rot here is + * after drm_rotation_simplify() + */ +struct komeda_data_flow_cfg { + struct komeda_component_output input; + u16 in_x, in_y, in_w, in_h; + u32 out_x, out_y, out_w, out_h; + u32 rot; + int blending_zorder; + u8 pixel_blend_mode, layer_alpha; +}; + /** struct komeda_pipeline_funcs */ struct komeda_pipeline_funcs { /* dump_register: Optional, dump registers to seq_file */ @@ -303,14 +330,23 @@ struct komeda_pipeline { int id; /** @avail_comps: available components mask of pipeline */ u32 avail_comps; + /** @n_layers: the number of layer on @layers */ int n_layers; + /** @layers: the pipeline layers */ struct komeda_layer *layers[KOMEDA_PIPELINE_MAX_LAYERS]; + /** @n_scalers: the number of scaler on @scalers */ int n_scalers; + /** @scalers: the pipeline scalers */ struct komeda_scaler *scalers[KOMEDA_PIPELINE_MAX_SCALERS]; + /** @compiz: compositor */ struct komeda_compiz *compiz; + /** @wb_layer: writeback layer */ struct komeda_layer *wb_layer; + /** @improc: post image processor */ struct komeda_improc *improc; + /** @ctrlr: timing controller */ struct komeda_timing_ctrlr *ctrlr; + /** @funcs: chip pipeline functions */ struct komeda_pipeline_funcs *funcs; /* private pipeline functions */ /** @of_node: pipeline dt node */ @@ -331,6 +367,7 @@ struct komeda_pipeline { struct komeda_pipeline_state { /** @obj: tracking pipeline_state by drm_atomic_state */ struct drm_private_state obj; + /** @pipe: backpointer to the pipeline */ struct komeda_pipeline *pipe; /** @crtc: currently bound crtc */ struct drm_crtc *crtc; @@ -382,4 +419,26 @@ komeda_component_add(struct komeda_pipeline *pipe, void komeda_component_destroy(struct komeda_dev *mdev, struct komeda_component *c); +struct komeda_plane_state; +struct komeda_crtc_state; +struct komeda_crtc; + +int komeda_build_layer_data_flow(struct komeda_layer *layer, + struct komeda_plane_state *kplane_st, + struct komeda_crtc_state *kcrtc_st, + struct komeda_data_flow_cfg *dflow); +int komeda_build_display_data_flow(struct komeda_crtc *kcrtc, + struct komeda_crtc_state *kcrtc_st); + +int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe, + struct komeda_crtc_state *kcrtc_st); + +struct komeda_pipeline_state * +komeda_pipeline_get_old_state(struct komeda_pipeline *pipe, + struct drm_atomic_state *state); +void komeda_pipeline_disable(struct komeda_pipeline *pipe, + struct drm_atomic_state *old_state); +void komeda_pipeline_update(struct komeda_pipeline *pipe, + struct drm_atomic_state *old_state); + #endif /* _KOMEDA_PIPELINE_H_*/ diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c new file mode 100644 index 000000000000..36570d7dad61 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c @@ -0,0 +1,610 @@ +// 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 <linux/clk.h> +#include "komeda_dev.h" +#include "komeda_kms.h" +#include "komeda_pipeline.h" +#include "komeda_framebuffer.h" + +static inline bool is_switching_user(void *old, void *new) +{ + if (!old || !new) + return false; + + return old != new; +} + +static struct komeda_pipeline_state * +komeda_pipeline_get_state(struct komeda_pipeline *pipe, + struct drm_atomic_state *state) +{ + struct drm_private_state *priv_st; + + priv_st = drm_atomic_get_private_obj_state(state, &pipe->obj); + if (IS_ERR(priv_st)) + return ERR_CAST(priv_st); + + return priv_to_pipe_st(priv_st); +} + +struct komeda_pipeline_state * +komeda_pipeline_get_old_state(struct komeda_pipeline *pipe, + struct drm_atomic_state *state) +{ + struct drm_private_state *priv_st; + + priv_st = drm_atomic_get_old_private_obj_state(state, &pipe->obj); + if (priv_st) + return priv_to_pipe_st(priv_st); + return NULL; +} + +static struct komeda_pipeline_state * +komeda_pipeline_get_new_state(struct komeda_pipeline *pipe, + struct drm_atomic_state *state) +{ + struct drm_private_state *priv_st; + + priv_st = drm_atomic_get_new_private_obj_state(state, &pipe->obj); + if (priv_st) + return priv_to_pipe_st(priv_st); + return NULL; +} + +/* Assign pipeline for crtc */ +static struct komeda_pipeline_state * +komeda_pipeline_get_state_and_set_crtc(struct komeda_pipeline *pipe, + struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + struct komeda_pipeline_state *st; + + st = komeda_pipeline_get_state(pipe, state); + if (IS_ERR(st)) + return st; + + if (is_switching_user(crtc, st->crtc)) { + DRM_DEBUG_ATOMIC("CRTC%d required pipeline%d is busy.\n", + drm_crtc_index(crtc), pipe->id); + return ERR_PTR(-EBUSY); + } + + /* pipeline only can be disabled when the it is free or unused */ + if (!crtc && st->active_comps) { + DRM_DEBUG_ATOMIC("Disabling a busy pipeline:%d.\n", pipe->id); + return ERR_PTR(-EBUSY); + } + + st->crtc = crtc; + + if (crtc) { + struct komeda_crtc_state *kcrtc_st; + + kcrtc_st = to_kcrtc_st(drm_atomic_get_new_crtc_state(state, + crtc)); + + kcrtc_st->active_pipes |= BIT(pipe->id); + kcrtc_st->affected_pipes |= BIT(pipe->id); + } + return st; +} + +static struct komeda_component_state * +komeda_component_get_state(struct komeda_component *c, + struct drm_atomic_state *state) +{ + struct drm_private_state *priv_st; + + WARN_ON(!drm_modeset_is_locked(&c->pipeline->obj.lock)); + + priv_st = drm_atomic_get_private_obj_state(state, &c->obj); + if (IS_ERR(priv_st)) + return ERR_CAST(priv_st); + + return priv_to_comp_st(priv_st); +} + +static struct komeda_component_state * +komeda_component_get_old_state(struct komeda_component *c, + struct drm_atomic_state *state) +{ + struct drm_private_state *priv_st; + + priv_st = drm_atomic_get_old_private_obj_state(state, &c->obj); + if (priv_st) + return priv_to_comp_st(priv_st); + return NULL; +} + +/** + * komeda_component_get_state_and_set_user() + * + * @c: component to get state and set user + * @state: global atomic state + * @user: direct user, the binding user + * @crtc: the CRTC user, the big boss :) + * + * This function accepts two users: + * - The direct user: can be plane/crtc/wb_connector depends on component + * - The big boss (CRTC) + * CRTC is the big boss (the final user), because all component resources + * eventually will be assigned to CRTC, like the layer will be binding to + * kms_plane, but kms plane will be binding to a CRTC eventually. + * + * The big boss (CRTC) is for pipeline assignment, since &komeda_component isn't + * independent and can be assigned to CRTC freely, but belongs to a specific + * pipeline, only pipeline can be shared between crtc, and pipeline as a whole + * (include all the internal components) assigned to a specific CRTC. + * + * So when set a user to komeda_component, need first to check the status of + * component->pipeline to see if the pipeline is available on this specific + * CRTC. if the pipeline is busy (assigned to another CRTC), even the required + * component is free, the component still cannot be assigned to the direct user. + */ +static struct komeda_component_state * +komeda_component_get_state_and_set_user(struct komeda_component *c, + struct drm_atomic_state *state, + void *user, + struct drm_crtc *crtc) +{ + struct komeda_pipeline_state *pipe_st; + struct komeda_component_state *st; + + /* First check if the pipeline is available */ + pipe_st = komeda_pipeline_get_state_and_set_crtc(c->pipeline, + state, crtc); + if (IS_ERR(pipe_st)) + return ERR_CAST(pipe_st); + + st = komeda_component_get_state(c, state); + if (IS_ERR(st)) + return st; + + /* check if the component has been occupied */ + if (is_switching_user(user, st->binding_user)) { + DRM_DEBUG_ATOMIC("required %s is busy.\n", c->name); + return ERR_PTR(-EBUSY); + } + + st->binding_user = user; + /* mark the component as active if user is valid */ + if (st->binding_user) + pipe_st->active_comps |= BIT(c->id); + + return st; +} + +static void +komeda_component_add_input(struct komeda_component_state *state, + struct komeda_component_output *input, + int idx) +{ + struct komeda_component *c = state->component; + + WARN_ON((idx < 0 || idx >= c->max_active_inputs)); + + /* since the inputs[i] is only valid when it is active. So if a input[i] + * is a newly enabled input which switches from disable to enable, then + * the old inputs[i] is undefined (NOT zeroed), we can not rely on + * memcmp, but directly mark it changed + */ + if (!has_bit(idx, state->affected_inputs) || + memcmp(&state->inputs[idx], input, sizeof(*input))) { + memcpy(&state->inputs[idx], input, sizeof(*input)); + state->changed_active_inputs |= BIT(idx); + } + state->active_inputs |= BIT(idx); + state->affected_inputs |= BIT(idx); +} + +static int +komeda_component_check_input(struct komeda_component_state *state, + struct komeda_component_output *input, + int idx) +{ + struct komeda_component *c = state->component; + + if ((idx < 0) || (idx >= c->max_active_inputs)) { + DRM_DEBUG_ATOMIC("%s invalid input id: %d.\n", c->name, idx); + return -EINVAL; + } + + if (has_bit(idx, state->active_inputs)) { + DRM_DEBUG_ATOMIC("%s required input_id: %d has been occupied already.\n", + c->name, idx); + return -EINVAL; + } + + return 0; +} + +static void +komeda_component_set_output(struct komeda_component_output *output, + struct komeda_component *comp, + u8 output_port) +{ + output->component = comp; + output->output_port = output_port; +} + +static int +komeda_component_validate_private(struct komeda_component *c, + struct komeda_component_state *st) +{ + int err; + + if (!c->funcs->validate) + return 0; + + err = c->funcs->validate(c, st); + if (err) + DRM_DEBUG_ATOMIC("%s validate private failed.\n", c->name); + + return err; +} + +static int +komeda_layer_check_cfg(struct komeda_layer *layer, + struct komeda_plane_state *kplane_st, + struct komeda_data_flow_cfg *dflow) +{ + if (!in_range(&layer->hsize_in, dflow->in_w)) { + DRM_DEBUG_ATOMIC("src_w: %d is out of range.\n", dflow->in_w); + return -EINVAL; + } + + if (!in_range(&layer->vsize_in, dflow->in_h)) { + DRM_DEBUG_ATOMIC("src_h: %d is out of range.\n", dflow->in_h); + return -EINVAL; + } + + return 0; +} + +static int +komeda_layer_validate(struct komeda_layer *layer, + struct komeda_plane_state *kplane_st, + struct komeda_data_flow_cfg *dflow) +{ + struct drm_plane_state *plane_st = &kplane_st->base; + struct drm_framebuffer *fb = plane_st->fb; + struct komeda_fb *kfb = to_kfb(fb); + struct komeda_component_state *c_st; + struct komeda_layer_state *st; + int i, err; + + err = komeda_layer_check_cfg(layer, kplane_st, dflow); + if (err) + return err; + + c_st = komeda_component_get_state_and_set_user(&layer->base, + plane_st->state, plane_st->plane, plane_st->crtc); + if (IS_ERR(c_st)) + return PTR_ERR(c_st); + + st = to_layer_st(c_st); + + st->rot = dflow->rot; + st->hsize = kfb->aligned_w; + st->vsize = kfb->aligned_h; + + for (i = 0; i < fb->format->num_planes; i++) + st->addr[i] = komeda_fb_get_pixel_addr(kfb, dflow->in_x, + dflow->in_y, i); + + err = komeda_component_validate_private(&layer->base, c_st); + if (err) + return err; + + /* update the data flow for the next stage */ + komeda_component_set_output(&dflow->input, &layer->base, 0); + + return 0; +} + +static void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st, + u16 *hsize, u16 *vsize) +{ + struct drm_display_mode *m = &kcrtc_st->base.adjusted_mode; + + if (hsize) + *hsize = m->hdisplay; + if (vsize) + *vsize = m->vdisplay; +} + +static int +komeda_compiz_set_input(struct komeda_compiz *compiz, + struct komeda_crtc_state *kcrtc_st, + struct komeda_data_flow_cfg *dflow) +{ + struct drm_atomic_state *drm_st = kcrtc_st->base.state; + struct komeda_component_state *c_st, *old_st; + struct komeda_compiz_input_cfg *cin; + u16 compiz_w, compiz_h; + int idx = dflow->blending_zorder; + + pipeline_composition_size(kcrtc_st, &compiz_w, &compiz_h); + /* check display rect */ + if ((dflow->out_x + dflow->out_w > compiz_w) || + (dflow->out_y + dflow->out_h > compiz_h) || + dflow->out_w == 0 || dflow->out_h == 0) { + DRM_DEBUG_ATOMIC("invalid disp rect [x=%d, y=%d, w=%d, h=%d]\n", + dflow->out_x, dflow->out_y, + dflow->out_w, dflow->out_h); + return -EINVAL; + } + + c_st = komeda_component_get_state_and_set_user(&compiz->base, drm_st, + kcrtc_st->base.crtc, kcrtc_st->base.crtc); + if (IS_ERR(c_st)) + return PTR_ERR(c_st); + + if (komeda_component_check_input(c_st, &dflow->input, idx)) + return -EINVAL; + + cin = &(to_compiz_st(c_st)->cins[idx]); + + cin->hsize = dflow->out_w; + cin->vsize = dflow->out_h; + cin->hoffset = dflow->out_x; + cin->voffset = dflow->out_y; + cin->pixel_blend_mode = dflow->pixel_blend_mode; + cin->layer_alpha = dflow->layer_alpha; + + old_st = komeda_component_get_old_state(&compiz->base, drm_st); + WARN_ON(!old_st); + + /* compare with old to check if this input has been changed */ + if (memcmp(&(to_compiz_st(old_st)->cins[idx]), cin, sizeof(*cin))) + c_st->changed_active_inputs |= BIT(idx); + + komeda_component_add_input(c_st, &dflow->input, idx); + + return 0; +} + +static int +komeda_compiz_validate(struct komeda_compiz *compiz, + struct komeda_crtc_state *state, + struct komeda_data_flow_cfg *dflow) +{ + struct komeda_component_state *c_st; + struct komeda_compiz_state *st; + + c_st = komeda_component_get_state_and_set_user(&compiz->base, + state->base.state, state->base.crtc, state->base.crtc); + if (IS_ERR(c_st)) + return PTR_ERR(c_st); + + st = to_compiz_st(c_st); + + pipeline_composition_size(state, &st->hsize, &st->vsize); + + komeda_component_set_output(&dflow->input, &compiz->base, 0); + + /* compiz output dflow will be fed to the next pipeline stage, prepare + * the data flow configuration for the next stage + */ + if (dflow) { + dflow->in_w = st->hsize; + dflow->in_h = st->vsize; + dflow->out_w = dflow->in_w; + dflow->out_h = dflow->in_h; + /* the output data of compiz doesn't have alpha, it only can be + * used as bottom layer when blend it with master layers + */ + dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE; + dflow->layer_alpha = 0xFF; + dflow->blending_zorder = 0; + } + + return 0; +} + +static int +komeda_improc_validate(struct komeda_improc *improc, + struct komeda_crtc_state *kcrtc_st, + struct komeda_data_flow_cfg *dflow) +{ + struct drm_crtc *crtc = kcrtc_st->base.crtc; + struct komeda_component_state *c_st; + struct komeda_improc_state *st; + + c_st = komeda_component_get_state_and_set_user(&improc->base, + kcrtc_st->base.state, crtc, crtc); + if (IS_ERR(c_st)) + return PTR_ERR(c_st); + + st = to_improc_st(c_st); + + st->hsize = dflow->in_w; + st->vsize = dflow->in_h; + + komeda_component_add_input(&st->base, &dflow->input, 0); + komeda_component_set_output(&dflow->input, &improc->base, 0); + + return 0; +} + +static int +komeda_timing_ctrlr_validate(struct komeda_timing_ctrlr *ctrlr, + struct komeda_crtc_state *kcrtc_st, + struct komeda_data_flow_cfg *dflow) +{ + struct drm_crtc *crtc = kcrtc_st->base.crtc; + struct komeda_timing_ctrlr_state *st; + struct komeda_component_state *c_st; + + c_st = komeda_component_get_state_and_set_user(&ctrlr->base, + kcrtc_st->base.state, crtc, crtc); + if (IS_ERR(c_st)) + return PTR_ERR(c_st); + + st = to_ctrlr_st(c_st); + + komeda_component_add_input(&st->base, &dflow->input, 0); + komeda_component_set_output(&dflow->input, &ctrlr->base, 0); + + return 0; +} + +int komeda_build_layer_data_flow(struct komeda_layer *layer, + struct komeda_plane_state *kplane_st, + struct komeda_crtc_state *kcrtc_st, + struct komeda_data_flow_cfg *dflow) +{ + struct drm_plane *plane = kplane_st->base.plane; + struct komeda_pipeline *pipe = layer->base.pipeline; + int err; + + DRM_DEBUG_ATOMIC("%s handling [PLANE:%d:%s]: src[x/y:%d/%d, w/h:%d/%d] disp[x/y:%d/%d, w/h:%d/%d]", + layer->base.name, plane->base.id, plane->name, + dflow->in_x, dflow->in_y, dflow->in_w, dflow->in_h, + dflow->out_x, dflow->out_y, dflow->out_w, dflow->out_h); + + err = komeda_layer_validate(layer, kplane_st, dflow); + if (err) + return err; + + err = komeda_compiz_set_input(pipe->compiz, kcrtc_st, dflow); + + return err; +} + +/* build display output data flow, the data path is: + * compiz -> improc -> timing_ctrlr + */ +int komeda_build_display_data_flow(struct komeda_crtc *kcrtc, + struct komeda_crtc_state *kcrtc_st) +{ + struct komeda_pipeline *master = kcrtc->master; + struct komeda_data_flow_cfg m_dflow; /* master data flow */ + int err; + + memset(&m_dflow, 0, sizeof(m_dflow)); + + err = komeda_compiz_validate(master->compiz, kcrtc_st, &m_dflow); + if (err) + return err; + + err = komeda_improc_validate(master->improc, kcrtc_st, &m_dflow); + if (err) + return err; + + err = komeda_timing_ctrlr_validate(master->ctrlr, kcrtc_st, &m_dflow); + if (err) + return err; + + return 0; +} + +static void +komeda_pipeline_unbound_components(struct komeda_pipeline *pipe, + struct komeda_pipeline_state *new) +{ + struct drm_atomic_state *drm_st = new->obj.state; + struct komeda_pipeline_state *old = priv_to_pipe_st(pipe->obj.state); + struct komeda_component_state *c_st; + struct komeda_component *c; + u32 disabling_comps, id; + + WARN_ON(!old); + + disabling_comps = (~new->active_comps) & old->active_comps; + + /* unbound all disabling component */ + dp_for_each_set_bit(id, disabling_comps) { + c = komeda_pipeline_get_component(pipe, id); + c_st = komeda_component_get_state_and_set_user(c, + drm_st, NULL, new->crtc); + WARN_ON(IS_ERR(c_st)); + } +} + +/* release unclaimed pipeline resource */ +int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe, + struct komeda_crtc_state *kcrtc_st) +{ + struct drm_atomic_state *drm_st = kcrtc_st->base.state; + struct komeda_pipeline_state *st; + + /* ignore the pipeline which is not affected */ + if (!pipe || !has_bit(pipe->id, kcrtc_st->affected_pipes)) + return 0; + + if (has_bit(pipe->id, kcrtc_st->active_pipes)) + st = komeda_pipeline_get_new_state(pipe, drm_st); + else + st = komeda_pipeline_get_state_and_set_crtc(pipe, drm_st, NULL); + + if (WARN_ON(IS_ERR_OR_NULL(st))) + return -EINVAL; + + komeda_pipeline_unbound_components(pipe, st); + + return 0; +} + +void komeda_pipeline_disable(struct komeda_pipeline *pipe, + struct drm_atomic_state *old_state) +{ + struct komeda_pipeline_state *old; + struct komeda_component *c; + struct komeda_component_state *c_st; + u32 id, disabling_comps = 0; + + old = komeda_pipeline_get_old_state(pipe, old_state); + + disabling_comps = old->active_comps; + DRM_DEBUG_ATOMIC("PIPE%d: disabling_comps: 0x%x.\n", + pipe->id, disabling_comps); + + dp_for_each_set_bit(id, disabling_comps) { + c = komeda_pipeline_get_component(pipe, id); + c_st = priv_to_comp_st(c->obj.state); + + /* + * If we disabled a component then all active_inputs should be + * put in the list of changed_active_inputs, so they get + * re-enabled. + * This usually happens during a modeset when the pipeline is + * first disabled and then the actual state gets committed + * again. + */ + c_st->changed_active_inputs |= c_st->active_inputs; + + c->funcs->disable(c); + } +} + +void komeda_pipeline_update(struct komeda_pipeline *pipe, + struct drm_atomic_state *old_state) +{ + struct komeda_pipeline_state *new = priv_to_pipe_st(pipe->obj.state); + struct komeda_pipeline_state *old; + struct komeda_component *c; + u32 id, changed_comps = 0; + + old = komeda_pipeline_get_old_state(pipe, old_state); + + changed_comps = new->active_comps | old->active_comps; + + DRM_DEBUG_ATOMIC("PIPE%d: active_comps: 0x%x, changed: 0x%x.\n", + pipe->id, new->active_comps, changed_comps); + + dp_for_each_set_bit(id, changed_comps) { + c = komeda_pipeline_get_component(pipe, id); + + if (new->active_comps & BIT(c->id)) + c->funcs->update(c, priv_to_comp_st(c->obj.state)); + else + c->funcs->disable(c); + } +} diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c index 0a4953a9a909..07ed0cc1bc44 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c @@ -7,10 +7,96 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_print.h> #include "komeda_dev.h" #include "komeda_kms.h" +static int +komeda_plane_init_data_flow(struct drm_plane_state *st, + struct komeda_data_flow_cfg *dflow) +{ + struct drm_framebuffer *fb = st->fb; + + memset(dflow, 0, sizeof(*dflow)); + + dflow->blending_zorder = st->zpos; + + /* if format doesn't have alpha, fix blend mode to PIXEL_NONE */ + dflow->pixel_blend_mode = fb->format->has_alpha ? + st->pixel_blend_mode : DRM_MODE_BLEND_PIXEL_NONE; + dflow->layer_alpha = st->alpha >> 8; + + dflow->out_x = st->crtc_x; + dflow->out_y = st->crtc_y; + dflow->out_w = st->crtc_w; + dflow->out_h = st->crtc_h; + + dflow->in_x = st->src_x >> 16; + dflow->in_y = st->src_y >> 16; + dflow->in_w = st->src_w >> 16; + dflow->in_h = st->src_h >> 16; + + return 0; +} + +/** + * komeda_plane_atomic_check - build input data flow + * @plane: DRM plane + * @state: the plane state object + * + * RETURNS: + * Zero for success or -errno + */ +static int +komeda_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct komeda_plane *kplane = to_kplane(plane); + struct komeda_plane_state *kplane_st = to_kplane_st(state); + struct komeda_layer *layer = kplane->layer; + struct drm_crtc_state *crtc_st; + struct komeda_crtc *kcrtc; + struct komeda_crtc_state *kcrtc_st; + struct komeda_data_flow_cfg dflow; + int err; + + if (!state->crtc || !state->fb) + return 0; + + crtc_st = drm_atomic_get_crtc_state(state->state, state->crtc); + if (!crtc_st->enable) { + DRM_DEBUG_ATOMIC("Cannot update plane on a disabled CRTC.\n"); + return -EINVAL; + } + + /* crtc is inactive, skip the resource assignment */ + if (!crtc_st->active) + return 0; + + kcrtc = to_kcrtc(state->crtc); + kcrtc_st = to_kcrtc_st(crtc_st); + + err = komeda_plane_init_data_flow(state, &dflow); + if (err) + return err; + + err = komeda_build_layer_data_flow(layer, kplane_st, kcrtc_st, &dflow); + + return err; +} + +/* plane doesn't represent a real HW, so there is no HW update for plane. + * komeda handles all the HW update in crtc->atomic_flush + */ +static void +komeda_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + static const struct drm_plane_helper_funcs komeda_plane_helper_funcs = { + .atomic_check = komeda_plane_atomic_check, + .atomic_update = komeda_plane_atomic_update, }; static void komeda_plane_destroy(struct drm_plane *plane) @@ -20,7 +106,60 @@ static void komeda_plane_destroy(struct drm_plane *plane) kfree(to_kplane(plane)); } +static void komeda_plane_reset(struct drm_plane *plane) +{ + struct komeda_plane_state *state; + struct komeda_plane *kplane = to_kplane(plane); + + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane->state); + + kfree(plane->state); + plane->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + state->base.rotation = DRM_MODE_ROTATE_0; + state->base.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; + state->base.alpha = DRM_BLEND_ALPHA_OPAQUE; + state->base.zpos = kplane->layer->base.id; + plane->state = &state->base; + plane->state->plane = plane; + } +} + +static struct drm_plane_state * +komeda_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct komeda_plane_state *new; + + if (WARN_ON(!plane->state)) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &new->base); + + return &new->base; +} + +static void +komeda_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + __drm_atomic_helper_plane_destroy_state(state); + kfree(to_kplane_st(state)); +} + static const struct drm_plane_funcs komeda_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = komeda_plane_destroy, + .reset = komeda_plane_reset, + .atomic_duplicate_state = komeda_plane_atomic_duplicate_state, + .atomic_destroy_state = komeda_plane_atomic_destroy_state, }; /* for komeda, which is pipeline can be share between crtcs */ diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c index f1c9e3fefa86..a54878cbd6e4 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c @@ -7,6 +7,188 @@ #include "komeda_dev.h" #include "komeda_kms.h" +static void +komeda_component_state_reset(struct komeda_component_state *st) +{ + st->binding_user = NULL; + st->affected_inputs = st->active_inputs; + st->active_inputs = 0; + st->changed_active_inputs = 0; +} + +static struct drm_private_state * +komeda_layer_atomic_duplicate_state(struct drm_private_obj *obj) +{ + struct komeda_layer_state *st; + + st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL); + if (!st) + return NULL; + + komeda_component_state_reset(&st->base); + __drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj); + + return &st->base.obj; +} + +static void +komeda_layer_atomic_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + struct komeda_layer_state *st = to_layer_st(priv_to_comp_st(state)); + + kfree(st); +} + +static const struct drm_private_state_funcs komeda_layer_obj_funcs = { + .atomic_duplicate_state = komeda_layer_atomic_duplicate_state, + .atomic_destroy_state = komeda_layer_atomic_destroy_state, +}; + +static int komeda_layer_obj_add(struct komeda_kms_dev *kms, + struct komeda_layer *layer) +{ + struct komeda_layer_state *st; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->base.component = &layer->base; + drm_atomic_private_obj_init(&kms->base, &layer->base.obj, &st->base.obj, + &komeda_layer_obj_funcs); + return 0; +} + +static struct drm_private_state * +komeda_compiz_atomic_duplicate_state(struct drm_private_obj *obj) +{ + struct komeda_compiz_state *st; + + st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL); + if (!st) + return NULL; + + komeda_component_state_reset(&st->base); + __drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj); + + return &st->base.obj; +} + +static void +komeda_compiz_atomic_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + kfree(to_compiz_st(priv_to_comp_st(state))); +} + +static const struct drm_private_state_funcs komeda_compiz_obj_funcs = { + .atomic_duplicate_state = komeda_compiz_atomic_duplicate_state, + .atomic_destroy_state = komeda_compiz_atomic_destroy_state, +}; + +static int komeda_compiz_obj_add(struct komeda_kms_dev *kms, + struct komeda_compiz *compiz) +{ + struct komeda_compiz_state *st; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->base.component = &compiz->base; + drm_atomic_private_obj_init(&kms->base, &compiz->base.obj, &st->base.obj, + &komeda_compiz_obj_funcs); + + return 0; +} + +static struct drm_private_state * +komeda_improc_atomic_duplicate_state(struct drm_private_obj *obj) +{ + struct komeda_improc_state *st; + + st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL); + if (!st) + return NULL; + + komeda_component_state_reset(&st->base); + __drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj); + + return &st->base.obj; +} + +static void +komeda_improc_atomic_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + kfree(to_improc_st(priv_to_comp_st(state))); +} + +static const struct drm_private_state_funcs komeda_improc_obj_funcs = { + .atomic_duplicate_state = komeda_improc_atomic_duplicate_state, + .atomic_destroy_state = komeda_improc_atomic_destroy_state, +}; + +static int komeda_improc_obj_add(struct komeda_kms_dev *kms, + struct komeda_improc *improc) +{ + struct komeda_improc_state *st; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->base.component = &improc->base; + drm_atomic_private_obj_init(&kms->base, &improc->base.obj, &st->base.obj, + &komeda_improc_obj_funcs); + + return 0; +} + +static struct drm_private_state * +komeda_timing_ctrlr_atomic_duplicate_state(struct drm_private_obj *obj) +{ + struct komeda_timing_ctrlr_state *st; + + st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL); + if (!st) + return NULL; + + komeda_component_state_reset(&st->base); + __drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj); + + return &st->base.obj; +} + +static void +komeda_timing_ctrlr_atomic_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + kfree(to_ctrlr_st(priv_to_comp_st(state))); +} + +static const struct drm_private_state_funcs komeda_timing_ctrlr_obj_funcs = { + .atomic_duplicate_state = komeda_timing_ctrlr_atomic_duplicate_state, + .atomic_destroy_state = komeda_timing_ctrlr_atomic_destroy_state, +}; + +static int komeda_timing_ctrlr_obj_add(struct komeda_kms_dev *kms, + struct komeda_timing_ctrlr *ctrlr) +{ + struct komeda_compiz_state *st; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->base.component = &ctrlr->base; + drm_atomic_private_obj_init(&kms->base, &ctrlr->base.obj, &st->base.obj, + &komeda_timing_ctrlr_obj_funcs); + + return 0; +} + static struct drm_private_state * komeda_pipeline_atomic_duplicate_state(struct drm_private_obj *obj) { @@ -55,7 +237,7 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms, struct komeda_dev *mdev) { struct komeda_pipeline *pipe; - int i, err; + int i, j, err; for (i = 0; i < mdev->n_pipelines; i++) { pipe = mdev->pipelines[i]; @@ -64,25 +246,33 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms, if (err) return err; - /* Add component */ + for (j = 0; j < pipe->n_layers; j++) { + err = komeda_layer_obj_add(kms, pipe->layers[j]); + if (err) + return err; + } + + err = komeda_compiz_obj_add(kms, pipe->compiz); + if (err) + return err; + + err = komeda_improc_obj_add(kms, pipe->improc); + if (err) + return err; + + err = komeda_timing_ctrlr_obj_add(kms, pipe->ctrlr); + if (err) + return err; } return 0; } -void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev) +void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms) { - struct komeda_pipeline *pipe; - struct komeda_component *c; - int i, id; + struct drm_mode_config *config = &kms->base.mode_config; + struct drm_private_obj *obj, *next; - for (i = 0; i < mdev->n_pipelines; i++) { - pipe = mdev->pipelines[i]; - dp_for_each_set_bit(id, pipe->avail_comps) { - c = komeda_pipeline_get_component(pipe, id); - - drm_atomic_private_obj_fini(&c->obj); - } - drm_atomic_private_obj_fini(&pipe->obj); - } + list_for_each_entry_safe(obj, next, &config->privobj_list, head) + drm_atomic_private_obj_fini(obj); } diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 5eb40130fafb..f4924cb7f495 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -798,6 +798,50 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_private_obj_state); /** + * drm_atomic_get_old_private_obj_state + * @state: global atomic state object + * @obj: private_obj to grab + * + * This function returns the old private object state for the given private_obj, + * or NULL if the private_obj is not part of the global atomic state. + */ +struct drm_private_state * +drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state, + struct drm_private_obj *obj) +{ + int i; + + for (i = 0; i < state->num_private_objs; i++) + if (obj == state->private_objs[i].ptr) + return state->private_objs[i].old_state; + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_get_old_private_obj_state); + +/** + * drm_atomic_get_new_private_obj_state + * @state: global atomic state object + * @obj: private_obj to grab + * + * This function returns the new private object state for the given private_obj, + * or NULL if the private_obj is not part of the global atomic state. + */ +struct drm_private_state * +drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, + struct drm_private_obj *obj) +{ + int i; + + for (i = 0; i < state->num_private_objs; i++) + if (obj == state->private_objs[i].ptr) + return state->private_objs[i].new_state; + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state); + +/** * drm_atomic_get_connector_state - get connector state * @state: global atomic state object * @connector: connector to get state object for @@ -1236,4 +1280,3 @@ int drm_atomic_debugfs_init(struct drm_minor *minor) minor->debugfs_root, minor); } #endif - diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 2355124849db..b34c3d38bf15 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1416,12 +1416,6 @@ EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); * * The driver may place further restrictions within these minimum * and maximum bounds. - * - * The semantics for the vertical blank timestamp differ when - * variable refresh rate is active. The vertical blank timestamp - * is defined to be an estimate using the current mode's fixed - * refresh rate timings. The semantics for the page-flip event - * timestamp remain the same. */ /** diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 2c22ea446075..649cfd8b4200 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -162,6 +162,25 @@ static const struct edid_quirk { /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/ { "ETR", 13896, EDID_QUIRK_FORCE_8BPC }, + /* Valve Index Headset */ + { "VLV", 0x91a8, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b0, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b1, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b2, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b3, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b4, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b5, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b6, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b7, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b8, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91b9, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91ba, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91bb, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91bc, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91bd, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91be, EDID_QUIRK_NON_DESKTOP }, + { "VLV", 0x91bf, EDID_QUIRK_NON_DESKTOP }, + /* HTC Vive and Vive Pro VR Headsets */ { "HVR", 0xaa01, EDID_QUIRK_NON_DESKTOP }, { "HVR", 0xaa02, EDID_QUIRK_NON_DESKTOP }, diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index cc26625b4b33..e01ceed09e67 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -186,13 +186,13 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) cma_obj = to_drm_gem_cma_obj(gem_obj); - if (cma_obj->vaddr) { - dma_free_wc(gem_obj->dev->dev, cma_obj->base.size, - cma_obj->vaddr, cma_obj->paddr); - } else if (gem_obj->import_attach) { + if (gem_obj->import_attach) { if (cma_obj->vaddr) dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr); drm_prime_gem_destroy(gem_obj, cma_obj->sgt); + } else if (cma_obj->vaddr) { + dma_free_wc(gem_obj->dev->dev, cma_obj->base.size, + cma_obj->vaddr, cma_obj->paddr); } drm_gem_object_release(gem_obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 9f42f7538236..7eb7cf9c3fa8 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -514,6 +514,9 @@ static int etnaviv_bind(struct device *dev) } drm->dev_private = priv; + dev->dma_parms = &priv->dma_parms; + dma_set_max_seg_size(dev, SZ_2G); + mutex_init(&priv->gem_lock); INIT_LIST_HEAD(&priv->gem_list); priv->num_gpus = 0; @@ -551,6 +554,8 @@ static void etnaviv_unbind(struct device *dev) component_unbind_all(dev, drm); + dev->dma_parms = NULL; + drm->dev_private = NULL; kfree(priv); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 6044ace6bb3e..8798423705e1 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -42,6 +42,7 @@ struct etnaviv_file_private { struct etnaviv_drm_private { int num_gpus; + struct device_dma_parameters dma_parms; struct etnaviv_gpu *gpu[ETNA_MAX_PIPES]; /* list of GEM objects: */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index c60752ef7324..e8778ebb72e6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -622,24 +622,18 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, lockdep_set_class(&to_etnaviv_bo(obj)->lock, &etnaviv_shm_lock_class); ret = drm_gem_object_init(dev, obj, size); - if (ret == 0) { - struct address_space *mapping; - - /* - * Our buffers are kept pinned, so allocating them - * from the MOVABLE zone is a really bad idea, and - * conflicts with CMA. See comments above new_inode() - * why this is required _and_ expected if you're - * going to pin these pages. - */ - mapping = obj->filp->f_mapping; - mapping_set_gfp_mask(mapping, GFP_HIGHUSER | - __GFP_RETRY_MAYFAIL | __GFP_NOWARN); - } - if (ret) goto fail; + /* + * Our buffers are kept pinned, so allocating them from the MOVABLE + * zone is a really bad idea, and conflicts with CMA. See comments + * above new_inode() why this is required _and_ expected if you're + * going to pin these pages. + */ + mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER | + __GFP_RETRY_MAYFAIL | __GFP_NOWARN); + etnaviv_gem_obj_add(dev, obj); ret = drm_gem_handle_create(file, obj, handle); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 6904535475de..72d01e873160 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -365,6 +365,7 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) dev_info(gpu->dev, "model: GC%x, revision: %x\n", gpu->identity.model, gpu->identity.revision); + gpu->idle_mask = ~VIVS_HI_IDLE_STATE_AXI_LP; /* * If there is a match in the HWDB, we aren't interested in the * remaining register values, as they might be wrong. @@ -412,7 +413,7 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) } /* GC600 idle register reports zero bits where modules aren't present */ - if (gpu->identity.model == chipModel_GC600) { + if (gpu->identity.model == chipModel_GC600) gpu->idle_mask = VIVS_HI_IDLE_STATE_TX | VIVS_HI_IDLE_STATE_RA | VIVS_HI_IDLE_STATE_SE | @@ -421,9 +422,6 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) VIVS_HI_IDLE_STATE_PE | VIVS_HI_IDLE_STATE_DE | VIVS_HI_IDLE_STATE_FE; - } else { - gpu->idle_mask = ~VIVS_HI_IDLE_STATE_AXI_LP; - } etnaviv_hw_specs(gpu); } diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile index 271fb46d4dd0..ea8324abc784 100644 --- a/drivers/gpu/drm/i915/gvt/Makefile +++ b/drivers/gpu/drm/i915/gvt/Makefile @@ -5,5 +5,5 @@ GVT_SOURCE := gvt.o aperture_gm.o handlers.o vgpu.o trace_points.o firmware.o \ execlist.o scheduler.o sched_policy.o mmio_context.o cmd_parser.o debugfs.o \ fb_decoder.o dmabuf.o page_track.o -ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) +ccflags-y += -I $(srctree)/$(src) -I $(srctree)/$(src)/$(GVT_DIR)/ i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE)) diff --git a/drivers/gpu/drm/i915/gvt/debugfs.c b/drivers/gpu/drm/i915/gvt/debugfs.c index 2ec89bcb59f1..8a9606f91e68 100644 --- a/drivers/gpu/drm/i915/gvt/debugfs.c +++ b/drivers/gpu/drm/i915/gvt/debugfs.c @@ -196,9 +196,9 @@ DEFINE_SIMPLE_ATTRIBUTE(vgpu_scan_nonprivbb_fops, int intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu) { struct dentry *ent; - char name[10] = ""; + char name[16] = ""; - sprintf(name, "vgpu%d", vgpu->id); + snprintf(name, 16, "vgpu%d", vgpu->id); vgpu->debugfs = debugfs_create_dir(name, vgpu->gvt->debugfs_root); if (!vgpu->debugfs) return -ENOMEM; diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c index 4e1e425189ba..41c8ebc60c63 100644 --- a/drivers/gpu/drm/i915/gvt/dmabuf.c +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c @@ -45,6 +45,7 @@ static int vgpu_gem_get_pages( int i, ret; gen8_pte_t __iomem *gtt_entries; struct intel_vgpu_fb_info *fb_info; + u32 page_num; fb_info = (struct intel_vgpu_fb_info *)obj->gvt_info; if (WARN_ON(!fb_info)) @@ -54,14 +55,15 @@ static int vgpu_gem_get_pages( if (unlikely(!st)) return -ENOMEM; - ret = sg_alloc_table(st, fb_info->size, GFP_KERNEL); + page_num = obj->base.size >> PAGE_SHIFT; + ret = sg_alloc_table(st, page_num, GFP_KERNEL); if (ret) { kfree(st); return ret; } gtt_entries = (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + (fb_info->start >> PAGE_SHIFT); - for_each_sg(st->sgl, sg, fb_info->size, i) { + for_each_sg(st->sgl, sg, page_num, i) { sg->offset = 0; sg->length = PAGE_SIZE; sg_dma_address(sg) = @@ -158,7 +160,7 @@ static struct drm_i915_gem_object *vgpu_create_gem(struct drm_device *dev, return NULL; drm_gem_private_object_init(dev, &obj->base, - info->size << PAGE_SHIFT); + roundup(info->size, PAGE_SIZE)); i915_gem_object_init(obj, &intel_vgpu_gem_ops); obj->read_domains = I915_GEM_DOMAIN_GTT; @@ -206,11 +208,12 @@ static int vgpu_get_plane_info(struct drm_device *dev, struct intel_vgpu_fb_info *info, int plane_id) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_vgpu_primary_plane_format p; struct intel_vgpu_cursor_plane_format c; int ret, tile_height = 1; + memset(info, 0, sizeof(*info)); + if (plane_id == DRM_PLANE_TYPE_PRIMARY) { ret = intel_vgpu_decode_primary_plane(vgpu, &p); if (ret) @@ -267,8 +270,7 @@ static int vgpu_get_plane_info(struct drm_device *dev, return -EINVAL; } - info->size = (info->stride * roundup(info->height, tile_height) - + PAGE_SIZE - 1) >> PAGE_SHIFT; + info->size = info->stride * roundup(info->height, tile_height); if (info->size == 0) { gvt_vgpu_err("fb size is zero\n"); return -EINVAL; @@ -278,11 +280,6 @@ static int vgpu_get_plane_info(struct drm_device *dev, gvt_vgpu_err("Not aligned fb address:0x%llx\n", info->start); return -EFAULT; } - if (((info->start >> PAGE_SHIFT) + info->size) > - ggtt_total_entries(&dev_priv->ggtt)) { - gvt_vgpu_err("Invalid GTT offset or size\n"); - return -EFAULT; - } if (!intel_gvt_ggtt_validate_range(vgpu, info->start, info->size)) { gvt_vgpu_err("invalid gma addr\n"); diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index c2f7d20f6346..08c74e65836b 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -811,7 +811,7 @@ static int reclaim_one_ppgtt_mm(struct intel_gvt *gvt); /* Allocate shadow page table without guest page. */ static struct intel_vgpu_ppgtt_spt *ppgtt_alloc_spt( - struct intel_vgpu *vgpu, intel_gvt_gtt_type_t type) + struct intel_vgpu *vgpu, enum intel_gvt_gtt_type type) { struct device *kdev = &vgpu->gvt->dev_priv->drm.pdev->dev; struct intel_vgpu_ppgtt_spt *spt = NULL; @@ -861,7 +861,7 @@ err_free_spt: /* Allocate shadow page table associated with specific gfn. */ static struct intel_vgpu_ppgtt_spt *ppgtt_alloc_spt_gfn( - struct intel_vgpu *vgpu, intel_gvt_gtt_type_t type, + struct intel_vgpu *vgpu, enum intel_gvt_gtt_type type, unsigned long gfn, bool guest_pde_ips) { struct intel_vgpu_ppgtt_spt *spt; @@ -936,7 +936,7 @@ static int ppgtt_invalidate_spt_by_shadow_entry(struct intel_vgpu *vgpu, { struct intel_gvt_gtt_pte_ops *ops = vgpu->gvt->gtt.pte_ops; struct intel_vgpu_ppgtt_spt *s; - intel_gvt_gtt_type_t cur_pt_type; + enum intel_gvt_gtt_type cur_pt_type; GEM_BUG_ON(!gtt_type_is_pt(get_next_pt_type(e->type))); @@ -1076,6 +1076,9 @@ static struct intel_vgpu_ppgtt_spt *ppgtt_populate_spt_by_guest_entry( } else { int type = get_next_pt_type(we->type); + if (!gtt_type_is_pt(type)) + goto err; + spt = ppgtt_alloc_spt_gfn(vgpu, type, ops->get_pfn(we), ips); if (IS_ERR(spt)) { ret = PTR_ERR(spt); @@ -1855,7 +1858,7 @@ static void vgpu_free_mm(struct intel_vgpu_mm *mm) * Zero on success, negative error code in pointer if failed. */ struct intel_vgpu_mm *intel_vgpu_create_ppgtt_mm(struct intel_vgpu *vgpu, - intel_gvt_gtt_type_t root_entry_type, u64 pdps[]) + enum intel_gvt_gtt_type root_entry_type, u64 pdps[]) { struct intel_gvt *gvt = vgpu->gvt; struct intel_vgpu_mm *mm; @@ -2309,7 +2312,7 @@ int intel_vgpu_emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, } static int alloc_scratch_pages(struct intel_vgpu *vgpu, - intel_gvt_gtt_type_t type) + enum intel_gvt_gtt_type type) { struct intel_vgpu_gtt *gtt = &vgpu->gtt; struct intel_gvt_gtt_pte_ops *ops = vgpu->gvt->gtt.pte_ops; @@ -2594,7 +2597,7 @@ struct intel_vgpu_mm *intel_vgpu_find_ppgtt_mm(struct intel_vgpu *vgpu, * Zero on success, negative error code if failed. */ struct intel_vgpu_mm *intel_vgpu_get_ppgtt_mm(struct intel_vgpu *vgpu, - intel_gvt_gtt_type_t root_entry_type, u64 pdps[]) + enum intel_gvt_gtt_type root_entry_type, u64 pdps[]) { struct intel_vgpu_mm *mm; diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 32c573aea494..42d0394f0de2 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -95,8 +95,8 @@ struct intel_gvt_gtt { unsigned long scratch_mfn; }; -typedef enum { - GTT_TYPE_INVALID = -1, +enum intel_gvt_gtt_type { + GTT_TYPE_INVALID = 0, GTT_TYPE_GGTT_PTE, @@ -124,7 +124,7 @@ typedef enum { GTT_TYPE_PPGTT_PML4_PT, GTT_TYPE_MAX, -} intel_gvt_gtt_type_t; +}; enum intel_gvt_mm_type { INTEL_GVT_MM_GGTT, @@ -148,7 +148,7 @@ struct intel_vgpu_mm { union { struct { - intel_gvt_gtt_type_t root_entry_type; + enum intel_gvt_gtt_type root_entry_type; /* * The 4 PDPs in ring context. For 48bit addressing, * only PDP0 is valid and point to PML4. For 32it @@ -169,7 +169,7 @@ struct intel_vgpu_mm { }; struct intel_vgpu_mm *intel_vgpu_create_ppgtt_mm(struct intel_vgpu *vgpu, - intel_gvt_gtt_type_t root_entry_type, u64 pdps[]); + enum intel_gvt_gtt_type root_entry_type, u64 pdps[]); static inline void intel_vgpu_mm_get(struct intel_vgpu_mm *mm) { @@ -233,7 +233,7 @@ struct intel_vgpu_ppgtt_spt { struct intel_vgpu *vgpu; struct { - intel_gvt_gtt_type_t type; + enum intel_gvt_gtt_type type; bool pde_ips; /* for 64KB PTEs */ void *vaddr; struct page *page; @@ -241,7 +241,7 @@ struct intel_vgpu_ppgtt_spt { } shadow_page; struct { - intel_gvt_gtt_type_t type; + enum intel_gvt_gtt_type type; bool pde_ips; /* for 64KB PTEs */ unsigned long gfn; unsigned long write_cnt; @@ -267,7 +267,7 @@ struct intel_vgpu_mm *intel_vgpu_find_ppgtt_mm(struct intel_vgpu *vgpu, u64 pdps[]); struct intel_vgpu_mm *intel_vgpu_get_ppgtt_mm(struct intel_vgpu *vgpu, - intel_gvt_gtt_type_t root_entry_type, u64 pdps[]); + enum intel_gvt_gtt_type root_entry_type, u64 pdps[]); int intel_vgpu_put_ppgtt_mm(struct intel_vgpu *vgpu, u64 pdps[]); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 18f01eeb2510..90673fca792f 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1206,7 +1206,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) { - intel_gvt_gtt_type_t root_entry_type = GTT_TYPE_PPGTT_ROOT_L4_ENTRY; + enum intel_gvt_gtt_type root_entry_type = GTT_TYPE_PPGTT_ROOT_L4_ENTRY; struct intel_vgpu_mm *mm; u64 *pdps; @@ -3303,7 +3303,7 @@ void intel_gvt_clean_mmio_info(struct intel_gvt *gvt) /* Special MMIO blocks. */ static struct gvt_mmio_block mmio_blocks[] = { {D_SKL_PLUS, _MMIO(CSR_MMIO_START_RANGE), 0x3000, NULL, NULL}, - {D_ALL, MCHBAR_MIRROR_REG_BASE, 0x4000, NULL, NULL}, + {D_ALL, _MMIO(MCHBAR_MIRROR_BASE_SNB), 0x40000, NULL, NULL}, {D_ALL, _MMIO(VGT_PVINFO_PAGE), VGT_PVINFO_SIZE, pvinfo_mmio_read, pvinfo_mmio_write}, {D_ALL, LGC_PALETTE(PIPE_A, 0), 1024, NULL, NULL}, diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index e7e14c842be4..edf6d646eb25 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -132,6 +132,7 @@ static struct engine_mmio gen9_engine_mmio_list[] __cacheline_aligned = { {RCS0, GEN9_GAMT_ECO_REG_RW_IA, 0x0, false}, /* 0x4ab0 */ {RCS0, GEN9_CSFE_CHICKEN1_RCS, 0xffff, false}, /* 0x20d4 */ + {RCS0, _MMIO(0x20D8), 0xffff, true}, /* 0x20d8 */ {RCS0, GEN8_GARBCNTL, 0x0, false}, /* 0xb004 */ {RCS0, GEN7_FF_THREAD_MODE, 0x0, false}, /* 0x20a0 */ diff --git a/drivers/gpu/drm/i915/gvt/reg.h b/drivers/gpu/drm/i915/gvt/reg.h index 3de5b643b266..33aaa14bfdde 100644 --- a/drivers/gpu/drm/i915/gvt/reg.h +++ b/drivers/gpu/drm/i915/gvt/reg.h @@ -126,7 +126,4 @@ #define RING_GFX_MODE(base) _MMIO((base) + 0x29c) #define VF_GUARDBAND _MMIO(0x83a4) -/* define the effective range of MCHBAR register on Sandybridge+ */ -#define MCHBAR_MIRROR_REG_BASE _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x4000) - #endif diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 8998fa5ab198..7c99bbc3e2b8 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -1343,7 +1343,7 @@ static int prepare_mm(struct intel_vgpu_workload *workload) struct execlist_ctx_descriptor_format *desc = &workload->ctx_desc; struct intel_vgpu_mm *mm; struct intel_vgpu *vgpu = workload->vgpu; - intel_gvt_gtt_type_t root_entry_type; + enum intel_gvt_gtt_type root_entry_type; u64 pdps[GVT_RING_CTX_NR_PDPS]; switch (desc->addressing_mode) { diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index b836721d3b13..ce342f7f7ddb 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -425,6 +425,26 @@ void __i915_request_submit(struct i915_request *request) if (i915_gem_context_is_banned(request->gem_context)) i915_request_skip(request, -EIO); + /* + * Are we using semaphores when the gpu is already saturated? + * + * Using semaphores incurs a cost in having the GPU poll a + * memory location, busywaiting for it to change. The continual + * memory reads can have a noticeable impact on the rest of the + * system with the extra bus traffic, stalling the cpu as it too + * tries to access memory across the bus (perf stat -e bus-cycles). + * + * If we installed a semaphore on this request and we only submit + * the request after the signaler completed, that indicates the + * system is overloaded and using semaphores at this time only + * increases the amount of work we are doing. If so, we disable + * further use of semaphores until we are idle again, whence we + * optimistically try again. + */ + if (request->sched.semaphores && + i915_sw_fence_signaled(&request->semaphore)) + request->hw_context->saturated |= request->sched.semaphores; + /* We may be recursing from the signal callback of another i915 fence */ spin_lock_nested(&request->lock, SINGLE_DEPTH_NESTING); @@ -799,6 +819,39 @@ err_unreserve: } static int +i915_request_await_start(struct i915_request *rq, struct i915_request *signal) +{ + if (list_is_first(&signal->ring_link, &signal->ring->request_list)) + return 0; + + signal = list_prev_entry(signal, ring_link); + if (i915_timeline_sync_is_later(rq->timeline, &signal->fence)) + return 0; + + return i915_sw_fence_await_dma_fence(&rq->submit, + &signal->fence, 0, + I915_FENCE_GFP); +} + +static intel_engine_mask_t +already_busywaiting(struct i915_request *rq) +{ + /* + * Polling a semaphore causes bus traffic, delaying other users of + * both the GPU and CPU. We want to limit the impact on others, + * while taking advantage of early submission to reduce GPU + * latency. Therefore we restrict ourselves to not using more + * than one semaphore from each source, and not using a semaphore + * if we have detected the engine is saturated (i.e. would not be + * submitted early and cause bus traffic reading an already passed + * semaphore). + * + * See the are-we-too-late? check in __i915_request_submit(). + */ + return rq->sched.semaphores | rq->hw_context->saturated; +} + +static int emit_semaphore_wait(struct i915_request *to, struct i915_request *from, gfp_t gfp) @@ -811,11 +864,15 @@ emit_semaphore_wait(struct i915_request *to, GEM_BUG_ON(INTEL_GEN(to->i915) < 8); /* Just emit the first semaphore we see as request space is limited. */ - if (to->sched.semaphores & from->engine->mask) + if (already_busywaiting(to) & from->engine->mask) return i915_sw_fence_await_dma_fence(&to->submit, &from->fence, 0, I915_FENCE_GFP); + err = i915_request_await_start(to, from); + if (err < 0) + return err; + err = i915_sw_fence_await_dma_fence(&to->semaphore, &from->fence, 0, I915_FENCE_GFP); diff --git a/drivers/gpu/drm/i915/intel_context.c b/drivers/gpu/drm/i915/intel_context.c index 8931e0fee873..924cc556223a 100644 --- a/drivers/gpu/drm/i915/intel_context.c +++ b/drivers/gpu/drm/i915/intel_context.c @@ -230,6 +230,7 @@ intel_context_init(struct intel_context *ce, ce->gem_context = ctx; ce->engine = engine; ce->ops = engine->cops; + ce->saturated = 0; INIT_LIST_HEAD(&ce->signal_link); INIT_LIST_HEAD(&ce->signals); diff --git a/drivers/gpu/drm/i915/intel_context_types.h b/drivers/gpu/drm/i915/intel_context_types.h index 68b4ca1611e0..339c7437fe82 100644 --- a/drivers/gpu/drm/i915/intel_context_types.h +++ b/drivers/gpu/drm/i915/intel_context_types.h @@ -14,6 +14,7 @@ #include <linux/types.h> #include "i915_active_types.h" +#include "intel_engine_types.h" struct i915_gem_context; struct i915_vma; @@ -58,6 +59,8 @@ struct intel_context { atomic_t pin_count; struct mutex pin_mutex; /* guards pinning and associated on-gpuing */ + intel_engine_mask_t saturated; /* submitting semaphores too late? */ + /** * active_tracker: Active tracker for the external rq activity * on this intel_context object. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 72d1bfcaab7a..7a05cbf2f820 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y := -Idrivers/gpu/drm/msm -ccflags-y += -Idrivers/gpu/drm/msm/disp/dpu1 -ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi +ccflags-y := -I $(srctree)/$(src) +ccflags-y += -I $(srctree)/$(src)/disp/dpu1 +ccflags-$(CONFIG_DRM_MSM_DSI) += -I $(srctree)/$(src)/dsi msm-y := \ adreno/adreno_device.o \ diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index ec24508b9d68..e74dce474250 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -527,6 +527,7 @@ static int a6xx_hw_init(struct msm_gpu *gpu) dev_warn_once(gpu->dev->dev, "Zap shader not enabled - using SECVID_TRUST_CNTL instead\n"); gpu_write(gpu, REG_A6XX_RBBM_SECVID_TRUST_CNTL, 0x0); + ret = 0; } out: diff --git a/drivers/gpu/drm/nouveau/Kbuild b/drivers/gpu/drm/nouveau/Kbuild index 581404e6544d..378c5dd692b0 100644 --- a/drivers/gpu/drm/nouveau/Kbuild +++ b/drivers/gpu/drm/nouveau/Kbuild @@ -1,7 +1,7 @@ -ccflags-y += -I$(src)/include -ccflags-y += -I$(src)/include/nvkm -ccflags-y += -I$(src)/nvkm -ccflags-y += -I$(src) +ccflags-y += -I $(srctree)/$(src)/include +ccflags-y += -I $(srctree)/$(src)/include/nvkm +ccflags-y += -I $(srctree)/$(src)/nvkm +ccflags-y += -I $(srctree)/$(src) # NVKM - HW resource manager #- code also used by various userspace tools/tests diff --git a/drivers/gpu/drm/panfrost/Kconfig b/drivers/gpu/drm/panfrost/Kconfig index 7f5e572daa2d..591611dc4e34 100644 --- a/drivers/gpu/drm/panfrost/Kconfig +++ b/drivers/gpu/drm/panfrost/Kconfig @@ -3,7 +3,7 @@ config DRM_PANFROST tristate "Panfrost (DRM support for ARM Mali Midgard/Bifrost GPUs)" depends on DRM - depends on ARM || ARM64 || COMPILE_TEST + depends on ARM || ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64) depends on MMU select DRM_SCHED select IOMMU_SUPPORT diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index a8121ae67ee3..238bd1d89d43 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -7,6 +7,7 @@ #include <linux/regulator/consumer.h> #include "panfrost_device.h" +#include "panfrost_devfreq.h" #include "panfrost_features.h" #include "panfrost_issues.h" #include "panfrost_gpu.h" diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 91e8fb0f2b25..970f669c6d29 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -98,6 +98,7 @@ int panfrost_device_init(struct panfrost_device *pfdev) struct resource *res; mutex_init(&pfdev->sched_lock); + mutex_init(&pfdev->reset_lock); INIT_LIST_HEAD(&pfdev->scheduled_jobs); spin_lock_init(&pfdev->hwaccess_lock); diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 1ba48d105763..56f452dfb490 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -78,6 +78,7 @@ struct panfrost_device { struct list_head scheduled_jobs; struct mutex sched_lock; + struct mutex reset_lock; struct { struct devfreq *devfreq; diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index c06af78ab833..94b0819ad50b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -3,8 +3,6 @@ /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ /* Copyright 2019 Collabora ltd. */ -#include <linux/bitfield.h> -#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/pagemap.h> @@ -172,13 +170,27 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, { struct panfrost_device *pfdev = dev->dev_private; struct drm_panfrost_submit *args = data; - struct drm_syncobj *sync_out; + struct drm_syncobj *sync_out = NULL; struct panfrost_job *job; int ret = 0; + if (!args->jc) + return -EINVAL; + + if (args->requirements && args->requirements != PANFROST_JD_REQ_FS) + return -EINVAL; + + if (args->out_sync > 0) { + sync_out = drm_syncobj_find(file, args->out_sync); + if (!sync_out) + return -ENODEV; + } + job = kzalloc(sizeof(*job), GFP_KERNEL); - if (!job) - return -ENOMEM; + if (!job) { + ret = -ENOMEM; + goto fail_out_sync; + } kref_init(&job->refcount); @@ -190,25 +202,24 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, ret = panfrost_copy_in_sync(dev, file, args, job); if (ret) - goto fail; + goto fail_job; ret = panfrost_lookup_bos(dev, file, args, job); if (ret) - goto fail; + goto fail_job; ret = panfrost_job_push(job); if (ret) - goto fail; + goto fail_job; /* Update the return sync object for the job */ - sync_out = drm_syncobj_find(file, args->out_sync); - if (sync_out) { + if (sync_out) drm_syncobj_replace_fence(sync_out, job->render_done_fence); - drm_syncobj_put(sync_out); - } -fail: +fail_job: panfrost_job_put(job); +fail_out_sync: + drm_syncobj_put(sync_out); return ret; } @@ -384,16 +395,15 @@ static int panfrost_probe(struct platform_device *pdev) err = panfrost_device_init(pfdev); if (err) { - dev_err(&pdev->dev, "Fatal error during GPU init\n"); + if (err != -EPROBE_DEFER) + dev_err(&pdev->dev, "Fatal error during GPU init\n"); goto err_out0; } - dma_set_mask_and_coherent(pfdev->dev, - DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features))); - err = panfrost_devfreq_init(pfdev); if (err) { - dev_err(&pdev->dev, "Fatal error during devfreq init\n"); + if (err != -EPROBE_DEFER) + dev_err(&pdev->dev, "Fatal error during devfreq init\n"); goto err_out1; } @@ -410,6 +420,7 @@ static int panfrost_probe(struct platform_device *pdev) err_out1: panfrost_device_fini(pfdev); err_out0: + pm_runtime_disable(pfdev->dev); drm_dev_put(ddev); return err; } diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c index 8a0376283a21..a5528a360ef4 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c @@ -14,7 +14,7 @@ /* Called DRM core on the last userspace/kernel unreference of the * BO. */ -void panfrost_gem_free_object(struct drm_gem_object *obj) +static void panfrost_gem_free_object(struct drm_gem_object *obj) { struct panfrost_gem_object *bo = to_panfrost_bo(obj); struct panfrost_device *pfdev = obj->dev->dev_private; diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index aceaf6e44a09..58ef25573cda 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -2,8 +2,10 @@ /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ /* Copyright 2019 Collabora ltd. */ +#include <linux/bitfield.h> #include <linux/bitmap.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> @@ -276,13 +278,13 @@ static void panfrost_gpu_init_features(struct panfrost_device *pfdev) pfdev->features.hw_issues); dev_info(pfdev->dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x", - gpu_read(pfdev, GPU_L2_FEATURES), - gpu_read(pfdev, GPU_CORE_FEATURES), - gpu_read(pfdev, GPU_TILER_FEATURES), - gpu_read(pfdev, GPU_MEM_FEATURES), - gpu_read(pfdev, GPU_MMU_FEATURES), - gpu_read(pfdev, GPU_AS_PRESENT), - gpu_read(pfdev, GPU_JS_PRESENT)); + pfdev->features.l2_features, + pfdev->features.core_features, + pfdev->features.tiler_features, + pfdev->features.mem_features, + pfdev->features.mmu_features, + pfdev->features.as_present, + pfdev->features.js_present); dev_info(pfdev->dev, "shader_present=0x%0llx l2_present=0x%0llx", pfdev->features.shader_present, pfdev->features.l2_present); @@ -332,6 +334,9 @@ int panfrost_gpu_init(struct panfrost_device *pfdev) panfrost_gpu_init_features(pfdev); + dma_set_mask_and_coherent(pfdev->dev, + DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features))); + irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu"); if (irq <= 0) return -ENODEV; diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 0a7ed04f7d52..a5716c8fe8b3 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -384,6 +384,8 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job) job_read(pfdev, JS_TAIL_LO(js)), sched_job); + mutex_lock(&pfdev->reset_lock); + for (i = 0; i < NUM_JOB_SLOTS; i++) drm_sched_stop(&pfdev->js->queue[i].sched); @@ -406,6 +408,8 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job) /* restart scheduler after GPU is usable again */ for (i = 0; i < NUM_JOB_SLOTS; i++) drm_sched_start(&pfdev->js->queue[i].sched, true); + + mutex_unlock(&pfdev->reset_lock); } static const struct drm_sched_backend_ops panfrost_sched_ops = { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 8d7a634c12c2..cb938d3cd3c2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -448,6 +448,14 @@ static int rockchip_drm_platform_remove(struct platform_device *pdev) return 0; } +static void rockchip_drm_platform_shutdown(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + if (drm) + drm_atomic_helper_shutdown(drm); +} + static const struct of_device_id rockchip_drm_dt_ids[] = { { .compatible = "rockchip,display-subsystem", }, { /* sentinel */ }, @@ -457,6 +465,7 @@ MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); static struct platform_driver rockchip_drm_platform_driver = { .probe = rockchip_drm_platform_probe, .remove = rockchip_drm_platform_remove, + .shutdown = rockchip_drm_platform_shutdown, .driver = { .name = "rockchip-drm", .of_match_table = rockchip_drm_dt_ids, |