Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26e34d2d authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'imx-drm-next-2016-09-19' of git://git.pengutronix.de/git/pza/linux into drm-next

imx-drm active plane reconfiguration, cleanup, FSU/IC/IRT/VDIC support

- add active plane reconfiguration support (v4),
  use the atomic_disable callback
- stop calling disable_plane manually in the plane destroy path
- let mode cleanup destroy mode objects on driver unbind
- drop deprecated load/unload drm_driver ops
- add exclusive fence to plane state, so the atomic helper can
  wait on them, remove the open-coded fence wait from imx-drm
- add low level deinterlacer (VDIC) support
- add support for channel linking via the frame synchronisation unit (FSU)
- add queued image conversion support for memory-to-memory scaling, rotation,
  and color space conversion, using IC and IRT.

* tag 'imx-drm-next-2016-09-19' of git://git.pengutronix.de/git/pza/linux:
  gpu: ipu-v3: Add queued image conversion support
  gpu: ipu-v3: Add ipu_rot_mode_is_irt()
  gpu: ipu-v3: fix a possible NULL dereference
  drm/imx: parallel-display: detach bridge or panel on unbind
  drm/imx: imx-ldb: detach bridge on unbind
  drm/imx: imx-ldb: detach panel on unbind
  gpu: ipu-v3: Add FSU channel linking support
  gpu: ipu-v3: Add Video Deinterlacer unit
  drm/imx: add exclusive fence to plane state
  drm/imx: fold ipu_plane_disable into ipu_disable_plane
  drm/imx: don't destroy mode objects manually on driver unbind
  drm/imx: drop deprecated load/unload drm_driver ops
  drm/imx: don't call disable_plane in plane destroy path
  drm/imx: Add active plane reconfiguration support
  drm/imx: Use DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET flag
  drm/imx: ipuv3-crtc: Use the callback ->atomic_disable instead of ->disable
  gpu: ipu-v3: Do not wait for DMFC FIFO to clear when disabling DMFC channel
parents b81a6179 cd98e85a
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -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);
+167 −165
Original line number Diff line number Diff line
@@ -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 = {
+4 −5
Original line number Diff line number Diff line
@@ -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);
+0 −3
Original line number Diff line number Diff line
@@ -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);
}
+12 −5
Original line number Diff line number Diff line
@@ -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]);
Loading