diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index cdf39aa3943cc1de551070c2e2eac0d2a006ac44..66ad8e6fb11ed8beb54d08e062f4bccc06618936 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1813,9 +1813,6 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) /* Disable all interrupts */ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); - hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->encoder->funcs->destroy(hdmi->encoder); - clk_disable_unprepare(hdmi->iahb_clk); clk_disable_unprepare(hdmi->isfr_clk); i2c_put_adapter(hdmi->ddc); diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 56dfc4cd50c6b09432293f4b1b5ef89280a59fee..98df09c2b3885b0ad0e5d99434063005a11f6712 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -64,25 +64,6 @@ static void imx_drm_driver_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(imxdrm->fbhelper); } -static int imx_drm_driver_unload(struct drm_device *drm) -{ - struct imx_drm_device *imxdrm = drm->dev_private; - - drm_kms_helper_poll_fini(drm); - - if (imxdrm->fbhelper) - drm_fbdev_cma_fini(imxdrm->fbhelper); - - component_unbind_all(drm->dev, drm); - - drm_vblank_cleanup(drm); - drm_mode_config_cleanup(drm); - - platform_set_drvdata(drm->platformdev, NULL); - - return 0; -} - static int imx_drm_enable_vblank(struct drm_device *drm, unsigned int pipe) { struct imx_drm_device *imxdrm = drm->dev_private; @@ -146,55 +127,73 @@ static void imx_drm_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); } +static int imx_drm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + + /* + * Check modeset again in case crtc_state->mode_changed is + * updated in plane's ->atomic_check callback. + */ + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + return ret; +} + +static int imx_drm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock) +{ + struct drm_plane_state *plane_state; + struct drm_plane *plane; + struct dma_buf *dma_buf; + int i; + + /* + * If the plane fb has an dma-buf attached, fish out the exclusive + * fence for the atomic helper to wait on. + */ + for_each_plane_in_state(state, plane, plane_state, i) { + if ((plane->state->fb != plane_state->fb) && plane_state->fb) { + dma_buf = drm_fb_cma_get_gem_obj(plane_state->fb, + 0)->base.dma_buf; + if (!dma_buf) + continue; + plane_state->fence = + reservation_object_get_excl_rcu(dma_buf->resv); + } + } + + return drm_atomic_helper_commit(dev, state, nonblock); +} + static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = imx_drm_output_poll_changed, - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_check = imx_drm_atomic_check, + .atomic_commit = imx_drm_atomic_commit, }; static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - struct drm_plane_state *plane_state; - struct drm_gem_cma_object *cma_obj; - struct fence *excl; - unsigned shared_count; - struct fence **shared; - unsigned int i, j; - int ret; - - /* Wait for fences. */ - for_each_crtc_in_state(state, crtc, crtc_state, i) { - plane_state = crtc->primary->state; - if (plane_state->fb) { - cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0); - if (cma_obj->base.dma_buf) { - ret = reservation_object_get_fences_rcu( - cma_obj->base.dma_buf->resv, &excl, - &shared_count, &shared); - if (unlikely(ret)) - DRM_ERROR("failed to get fences " - "for buffer\n"); - - if (excl) { - fence_wait(excl, false); - fence_put(excl); - } - for (j = 0; j < shared_count; i++) { - fence_wait(shared[j], false); - fence_put(shared[j]); - } - } - } - } drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_planes(dev, state, - DRM_PLANE_COMMIT_ACTIVE_ONLY); + DRM_PLANE_COMMIT_ACTIVE_ONLY | + DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET); drm_atomic_helper_commit_modeset_enables(dev, state); @@ -209,111 +208,6 @@ static struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = { .atomic_commit_tail = imx_drm_atomic_commit_tail, }; -/* - * Main DRM initialisation. This binds, initialises and registers - * with DRM the subcomponents of the driver. - */ -static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) -{ - struct imx_drm_device *imxdrm; - struct drm_connector *connector; - int ret; - - imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL); - if (!imxdrm) - return -ENOMEM; - - imxdrm->drm = drm; - - drm->dev_private = imxdrm; - - /* - * enable drm irq mode. - * - with irq_enabled = true, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler and - * drivers can well take care of their interrupts - */ - drm->irq_enabled = true; - - /* - * set max width and height as default value(4096x4096). - * this value would be used to check framebuffer size limitation - * at drm_mode_addfb(). - */ - drm->mode_config.min_width = 64; - drm->mode_config.min_height = 64; - drm->mode_config.max_width = 4096; - drm->mode_config.max_height = 4096; - drm->mode_config.funcs = &imx_drm_mode_config_funcs; - drm->mode_config.helper_private = &imx_drm_mode_config_helpers; - - drm_mode_config_init(drm); - - ret = drm_vblank_init(drm, MAX_CRTC); - if (ret) - goto err_kms; - - platform_set_drvdata(drm->platformdev, drm); - - /* Now try and bind all our sub-components */ - ret = component_bind_all(drm->dev, drm); - if (ret) - goto err_vblank; - - /* - * All components are now added, we can publish the connector sysfs - * entries to userspace. This will generate hotplug events and so - * userspace will expect to be able to access DRM at this point. - */ - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - ret = drm_connector_register(connector); - if (ret) { - dev_err(drm->dev, - "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", - connector->base.id, - connector->name, ret); - goto err_unbind; - } - } - - drm_mode_config_reset(drm); - - /* - * All components are now initialised, so setup the fb helper. - * The fb helper takes copies of key hardware information, so the - * crtcs/connectors/encoders must not change after this point. - */ -#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) - if (legacyfb_depth != 16 && legacyfb_depth != 32) { - dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); - legacyfb_depth = 16; - } - imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, - drm->mode_config.num_crtc, MAX_CRTC); - if (IS_ERR(imxdrm->fbhelper)) { - ret = PTR_ERR(imxdrm->fbhelper); - imxdrm->fbhelper = NULL; - goto err_unbind; - } -#endif - - drm_kms_helper_poll_init(drm); - - return 0; - -err_unbind: - component_unbind_all(drm->dev, drm); -err_vblank: - drm_vblank_cleanup(drm); -err_kms: - drm_mode_config_cleanup(drm); - - return ret; -} - /* * imx_drm_add_crtc - add a new crtc */ @@ -406,8 +300,6 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = { static struct drm_driver imx_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .load = imx_drm_driver_load, - .unload = imx_drm_driver_unload, .lastclose = imx_drm_driver_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, @@ -460,12 +352,122 @@ static int compare_of(struct device *dev, void *data) static int imx_drm_bind(struct device *dev) { - return drm_platform_init(&imx_drm_driver, to_platform_device(dev)); + struct drm_device *drm; + struct imx_drm_device *imxdrm; + int ret; + + drm = drm_dev_alloc(&imx_drm_driver, dev); + if (!drm) + return -ENOMEM; + + imxdrm = devm_kzalloc(dev, sizeof(*imxdrm), GFP_KERNEL); + if (!imxdrm) { + ret = -ENOMEM; + goto err_unref; + } + + imxdrm->drm = drm; + drm->dev_private = imxdrm; + + /* + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler and + * drivers can well take care of their interrupts + */ + drm->irq_enabled = true; + + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + drm->mode_config.min_width = 64; + drm->mode_config.min_height = 64; + drm->mode_config.max_width = 4096; + drm->mode_config.max_height = 4096; + drm->mode_config.funcs = &imx_drm_mode_config_funcs; + drm->mode_config.helper_private = &imx_drm_mode_config_helpers; + + drm_mode_config_init(drm); + + ret = drm_vblank_init(drm, MAX_CRTC); + if (ret) + goto err_kms; + + dev_set_drvdata(dev, drm); + + /* Now try and bind all our sub-components */ + ret = component_bind_all(dev, drm); + if (ret) + goto err_vblank; + + drm_mode_config_reset(drm); + + /* + * All components are now initialised, so setup the fb helper. + * The fb helper takes copies of key hardware information, so the + * crtcs/connectors/encoders must not change after this point. + */ +#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) + if (legacyfb_depth != 16 && legacyfb_depth != 32) { + dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); + legacyfb_depth = 16; + } + imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, + drm->mode_config.num_crtc, MAX_CRTC); + if (IS_ERR(imxdrm->fbhelper)) { + ret = PTR_ERR(imxdrm->fbhelper); + imxdrm->fbhelper = NULL; + goto err_unbind; + } +#endif + + drm_kms_helper_poll_init(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + goto err_fbhelper; + + return 0; + +err_fbhelper: + drm_kms_helper_poll_fini(drm); + if (imxdrm->fbhelper) + drm_fbdev_cma_fini(imxdrm->fbhelper); +err_unbind: + component_unbind_all(drm->dev, drm); +err_vblank: + drm_vblank_cleanup(drm); +err_kms: + drm_mode_config_cleanup(drm); +err_unref: + drm_dev_unref(drm); + + return ret; } static void imx_drm_unbind(struct device *dev) { - drm_put_dev(dev_get_drvdata(dev)); + struct drm_device *drm = dev_get_drvdata(dev); + struct imx_drm_device *imxdrm = drm->dev_private; + + drm_dev_unregister(drm); + + drm_kms_helper_poll_fini(drm); + + if (imxdrm->fbhelper) + drm_fbdev_cma_fini(imxdrm->fbhelper); + + drm_mode_config_cleanup(drm); + + component_unbind_all(drm->dev, drm); + dev_set_drvdata(dev, NULL); + + drm_dev_unref(drm); } static const struct component_master_ops imx_drm_ops = { diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 4eed3a6addadf6f028055f98f47c1684f07c7cb4..3ce391c239b02ace097ab4fbfb89deaaebb24618 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -757,11 +757,10 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, for (i = 0; i < 2; i++) { struct imx_ldb_channel *channel = &imx_ldb->channel[i]; - if (!channel->connector.funcs) - continue; - - channel->connector.funcs->destroy(&channel->connector); - channel->encoder.funcs->destroy(&channel->encoder); + if (channel->bridge) + drm_bridge_detach(channel->bridge); + if (channel->panel) + drm_panel_detach(channel->panel); kfree(channel->edid); i2c_put_adapter(channel->ddc); diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index 5e875944ffa214f48f06d7ed8b687aabd1705ffb..8fc088843e553c2def5353e74bdd47171824478e 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -685,9 +685,6 @@ static void imx_tve_unbind(struct device *dev, struct device *master, { struct imx_tve *tve = dev_get_drvdata(dev); - tve->connector.funcs->destroy(&tve->connector); - tve->encoder.funcs->destroy(&tve->encoder); - if (!IS_ERR(tve->dac_reg)) regulator_disable(tve->dac_reg); } diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 6e1dc902522cbe4c373cee1a9ee4416975758bdf..9df29f1cb16af029a7bcbdd93a56562be464a2ef 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -60,7 +60,8 @@ static void ipu_crtc_enable(struct drm_crtc *crtc) ipu_di_enable(ipu_crtc->di); } -static void ipu_crtc_disable(struct drm_crtc *crtc) +static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); @@ -75,6 +76,9 @@ static void ipu_crtc_disable(struct drm_crtc *crtc) crtc->state->event = NULL; } spin_unlock_irq(&crtc->dev->event_lock); + + /* always disable planes on the CRTC */ + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true); } static void imx_drm_crtc_reset(struct drm_crtc *crtc) @@ -120,9 +124,14 @@ static void imx_drm_crtc_destroy_state(struct drm_crtc *crtc, kfree(to_imx_crtc_state(state)); } +static void imx_drm_crtc_destroy(struct drm_crtc *crtc) +{ + imx_drm_remove_crtc(to_ipu_crtc(crtc)->imx_crtc); +} + static const struct drm_crtc_funcs ipu_crtc_funcs = { .set_config = drm_atomic_helper_set_config, - .destroy = drm_crtc_cleanup, + .destroy = imx_drm_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, .reset = imx_drm_crtc_reset, .atomic_duplicate_state = imx_drm_crtc_duplicate_state, @@ -241,7 +250,7 @@ static const struct drm_crtc_helper_funcs ipu_helper_funcs = { .mode_set_nofb = ipu_crtc_mode_set_nofb, .atomic_check = ipu_crtc_atomic_check, .atomic_begin = ipu_crtc_atomic_begin, - .disable = ipu_crtc_disable, + .atomic_disable = ipu_crtc_atomic_disable, .enable = ipu_crtc_enable, }; @@ -409,8 +418,6 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, { struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); - imx_drm_remove_crtc(ipu_crtc->imx_crtc); - ipu_put_resources(ipu_crtc); if (ipu_crtc->plane[1]) ipu_plane_put_resources(ipu_crtc->plane[1]); diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 4ad67d015ec7fdcb32dc84b17ebb99eb185590d2..ce22d0a0ddc8116e8026571f7bd700f461f5ee08 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -213,8 +213,12 @@ static void ipu_plane_enable(struct ipu_plane *ipu_plane) ipu_dp_enable_channel(ipu_plane->dp); } -static void ipu_plane_disable(struct ipu_plane *ipu_plane) +static int ipu_disable_plane(struct drm_plane *plane) { + struct ipu_plane *ipu_plane = to_ipu_plane(plane); + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); if (ipu_plane->dp) @@ -223,15 +227,6 @@ static void ipu_plane_disable(struct ipu_plane *ipu_plane) ipu_dmfc_disable_channel(ipu_plane->dmfc); if (ipu_plane->dp) ipu_dp_disable(ipu_plane->ipu); -} - -static int ipu_disable_plane(struct drm_plane *plane) -{ - struct ipu_plane *ipu_plane = to_ipu_plane(plane); - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - ipu_plane_disable(ipu_plane); return 0; } @@ -242,7 +237,6 @@ static void ipu_plane_destroy(struct drm_plane *plane) DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - ipu_disable_plane(plane); drm_plane_cleanup(plane); kfree(ipu_plane); } @@ -319,13 +313,16 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; /* - * since we cannot touch active IDMAC channels, we do not support - * resizing the enabled plane or changing its format + * We support resizing active plane or changing its format by + * forcing CRTC mode change in plane's ->atomic_check callback + * and disabling all affected active planes in CRTC's ->atomic_disable + * callback. The planes will be reenabled in plane's ->atomic_update + * callback. */ if (old_fb && (state->src_w != old_state->src_w || state->src_h != old_state->src_h || fb->pixel_format != old_fb->pixel_format)) - return -EINVAL; + crtc_state->mode_changed = true; eba = drm_plane_state_to_eba(state); @@ -336,7 +333,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; if (old_fb && fb->pitches[0] != old_fb->pitches[0]) - return -EINVAL; + crtc_state->mode_changed = true; switch (fb->pixel_format) { case DRM_FORMAT_YUV420: @@ -372,7 +369,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; if (old_fb && old_fb->pitches[1] != fb->pitches[1]) - return -EINVAL; + crtc_state->mode_changed = true; } return 0; @@ -392,8 +389,12 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, enum ipu_color_space ics; if (old_state->fb) { - ipu_plane_atomic_set_base(ipu_plane, old_state); - return; + struct drm_crtc_state *crtc_state = state->crtc->state; + + if (!drm_atomic_crtc_needs_modeset(crtc_state)) { + ipu_plane_atomic_set_base(ipu_plane, old_state); + return; + } } switch (ipu_plane->dp_flow) { diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 74b0ac06fdab57fcd69218e9c45be3f7d88276e0..d796ada2a47aba793ace3968454688ce134fcb3f 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -293,8 +293,10 @@ static void imx_pd_unbind(struct device *dev, struct device *master, { struct imx_parallel_display *imxpd = dev_get_drvdata(dev); - imxpd->encoder.funcs->destroy(&imxpd->encoder); - imxpd->connector.funcs->destroy(&imxpd->connector); + if (imxpd->bridge) + drm_bridge_detach(imxpd->bridge); + if (imxpd->panel) + drm_panel_detach(imxpd->panel); kfree(imxpd->edid); } diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 107ec236a4a6fb633d5c6974c037c02360dc7eab..5f961416c4eeb44213140cb04b3c3f552f5170b8 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ - ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o + ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ + ipu-smfc.o ipu-vdi.o diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index d230988ddb8fc485a7c11a84294849098ce0104e..b9539f7c5e9adc43f4acc91db35226371699e919 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -730,6 +730,137 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) } EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); + +/* Frame Synchronization Unit Channel Linking */ + +struct fsu_link_reg_info { + int chno; + u32 reg; + u32 mask; + u32 val; +}; + +struct fsu_link_info { + struct fsu_link_reg_info src; + struct fsu_link_reg_info sink; +}; + +static const struct fsu_link_info fsu_link_info[] = { + { + .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, + FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, + .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, + FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, + }, { + .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, + FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, + .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, + FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, + }, { + .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, + FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, + .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, + FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, + }, { + .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, + .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, + FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, + }, +}; + +static const struct fsu_link_info *find_fsu_link_info(int src, int sink) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { + if (src == fsu_link_info[i].src.chno && + sink == fsu_link_info[i].sink.chno) + return &fsu_link_info[i]; + } + + return NULL; +} + +/* + * Links a source channel to a sink channel in the FSU. + */ +int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) +{ + const struct fsu_link_info *link; + u32 src_reg, sink_reg; + unsigned long flags; + + link = find_fsu_link_info(src_ch, sink_ch); + if (!link) + return -EINVAL; + + spin_lock_irqsave(&ipu->lock, flags); + + if (link->src.mask) { + src_reg = ipu_cm_read(ipu, link->src.reg); + src_reg &= ~link->src.mask; + src_reg |= link->src.val; + ipu_cm_write(ipu, src_reg, link->src.reg); + } + + if (link->sink.mask) { + sink_reg = ipu_cm_read(ipu, link->sink.reg); + sink_reg &= ~link->sink.mask; + sink_reg |= link->sink.val; + ipu_cm_write(ipu, sink_reg, link->sink.reg); + } + + spin_unlock_irqrestore(&ipu->lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fsu_link); + +/* + * Unlinks source and sink channels in the FSU. + */ +int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) +{ + const struct fsu_link_info *link; + u32 src_reg, sink_reg; + unsigned long flags; + + link = find_fsu_link_info(src_ch, sink_ch); + if (!link) + return -EINVAL; + + spin_lock_irqsave(&ipu->lock, flags); + + if (link->src.mask) { + src_reg = ipu_cm_read(ipu, link->src.reg); + src_reg &= ~link->src.mask; + ipu_cm_write(ipu, src_reg, link->src.reg); + } + + if (link->sink.mask) { + sink_reg = ipu_cm_read(ipu, link->sink.reg); + sink_reg &= ~link->sink.mask; + ipu_cm_write(ipu, sink_reg, link->sink.reg); + } + + spin_unlock_irqrestore(&ipu->lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fsu_unlink); + +/* Link IDMAC channels in the FSU */ +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) +{ + return ipu_fsu_link(src->ipu, src->num, sink->num); +} +EXPORT_SYMBOL_GPL(ipu_idmac_link); + +/* Unlink IDMAC channels in the FSU */ +int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) +{ + return ipu_fsu_unlink(src->ipu, src->num, sink->num); +} +EXPORT_SYMBOL_GPL(ipu_idmac_unlink); + struct ipu_devtype { const char *name; unsigned long cm_ofs; @@ -839,6 +970,20 @@ static int ipu_submodules_init(struct ipu_soc *ipu, goto err_ic; } + ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, + IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | + IPU_CONF_IC_INPUT); + if (ret) { + unit = "vdi"; + goto err_vdi; + } + + ret = ipu_image_convert_init(ipu, dev); + if (ret) { + unit = "image_convert"; + goto err_image_convert; + } + ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, IPU_CONF_DI0_EN, ipu_clk); if (ret) { @@ -893,6 +1038,10 @@ static int ipu_submodules_init(struct ipu_soc *ipu, err_di_1: ipu_di_exit(ipu, 0); err_di_0: + ipu_image_convert_exit(ipu); +err_image_convert: + ipu_vdi_exit(ipu); +err_vdi: ipu_ic_exit(ipu); err_ic: ipu_csi_exit(ipu, 1); @@ -977,6 +1126,8 @@ static void ipu_submodules_exit(struct ipu_soc *ipu) ipu_dc_exit(ipu); ipu_di_exit(ipu, 1); ipu_di_exit(ipu, 0); + ipu_image_convert_exit(ipu); + ipu_vdi_exit(ipu); ipu_ic_exit(ipu); ipu_csi_exit(ipu, 1); ipu_csi_exit(ipu, 0); @@ -1213,8 +1364,6 @@ EXPORT_SYMBOL_GPL(ipu_dump); static int ipu_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(imx_ipu_dt_ids, &pdev->dev); struct device_node *np = pdev->dev.of_node; struct ipu_soc *ipu; struct resource *res; @@ -1222,7 +1371,9 @@ static int ipu_probe(struct platform_device *pdev) int i, ret, irq_sync, irq_err; const struct ipu_devtype *devtype; - devtype = of_id->data; + devtype = of_device_get_match_data(&pdev->dev); + if (!devtype) + return -EINVAL; irq_sync = platform_get_irq(pdev, 0); irq_err = platform_get_irq(pdev, 1); diff --git a/drivers/gpu/ipu-v3/ipu-dmfc.c b/drivers/gpu/ipu-v3/ipu-dmfc.c index 42705bb5aaa36b6e42d495032df6758ef9a0ddcd..a40f211f382faf86678e3419ecd0bf4014b95ac6 100644 --- a/drivers/gpu/ipu-v3/ipu-dmfc.c +++ b/drivers/gpu/ipu-v3/ipu-dmfc.c @@ -123,20 +123,6 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) } EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { - if (time_after(jiffies, timeout)) { - dev_warn(priv->dev, - "Timeout waiting for DMFC FIFOs to clear\n"); - break; - } - cpu_relax(); - } -} - void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) { struct ipu_dmfc_priv *priv = dmfc->priv; @@ -145,10 +131,8 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) priv->use_count--; - if (!priv->use_count) { - ipu_dmfc_wait_fifos(priv); + if (!priv->use_count) ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); - } if (priv->use_count < 0) priv->use_count = 0; diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 1a37afcd85bd2a696eeb38f58a554b37fefb74d5..321eb983c2f59e4a3841e4a979eb1f15c2285235 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -619,7 +619,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); - if (rot >= IPU_ROTATE_90_RIGHT) + if (ipu_rot_mode_is_irt(rot)) ic->rotation = true; unlock: diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c new file mode 100644 index 0000000000000000000000000000000000000000..2ba7d437a2afc7a0758402690526de5366abb718 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -0,0 +1,1709 @@ +/* + * Copyright (C) 2012-2016 Mentor Graphics Inc. + * + * Queued image conversion support, with tiling and rotation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include