summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/imx/imx-drm-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/imx/imx-drm-core.c')
-rw-r--r--drivers/gpu/drm/imx/imx-drm-core.c260
1 files changed, 157 insertions, 103 deletions
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 9672b579f950..b41c0e4551ae 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -15,10 +15,8 @@
*/
#include <linux/component.h>
#include <linux/device.h>
-#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/reservation.h>
#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -29,24 +27,17 @@
#include <drm/drm_plane_helper.h>
#include <drm/drm_of.h>
#include <video/imx-ipu-v3.h>
+#include <video/dpu.h>
+#include <video/imx-dcss.h>
+#include <video/imx-lcdif.h>
#include "imx-drm.h"
-#define MAX_CRTC 4
-
struct imx_drm_component {
struct device_node *of_node;
struct list_head list;
};
-struct imx_drm_device {
- struct drm_device *drm;
- struct imx_drm_crtc *crtc[MAX_CRTC];
- unsigned int pipes;
- struct drm_fbdev_cma *fbhelper;
- struct drm_atomic_state *state;
-};
-
struct imx_drm_crtc {
struct drm_crtc *crtc;
struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs;
@@ -101,6 +92,9 @@ static const struct file_operations imx_drm_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.mmap = drm_gem_cma_mmap,
.poll = drm_poll,
.read = drm_read,
@@ -120,94 +114,6 @@ void imx_drm_encoder_destroy(struct drm_encoder *encoder)
}
EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy);
-static void imx_drm_output_poll_changed(struct drm_device *drm)
-{
- struct imx_drm_device *imxdrm = drm->dev_private;
-
- 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 = 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;
-
- drm_atomic_helper_commit_modeset_disables(dev, state);
-
- drm_atomic_helper_commit_planes(dev, state,
- DRM_PLANE_COMMIT_ACTIVE_ONLY |
- DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET);
-
- drm_atomic_helper_commit_modeset_enables(dev, state);
-
- drm_atomic_helper_commit_hw_done(state);
-
- drm_atomic_helper_wait_for_vblanks(dev, state);
-
- drm_atomic_helper_cleanup_planes(dev, state);
-}
-
-static struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = {
- .atomic_commit_tail = imx_drm_atomic_commit_tail,
-};
-
/*
* imx_drm_add_crtc - add a new crtc
*/
@@ -339,6 +245,33 @@ static int compare_of(struct device *dev, void *data)
struct ipu_client_platformdata *pdata = dev->platform_data;
return pdata->of_node == np;
+ } else if (strcmp(dev->driver->name, "imx-dpu-crtc") == 0) {
+ struct dpu_client_platformdata *pdata = dev->platform_data;
+
+ return pdata->of_node == np;
+ } else if (strcmp(dev->driver->name, "imx-dcss-crtc") == 0) {
+ struct dcss_client_platformdata *pdata = dev->platform_data;
+
+ return pdata->of_node == np;
+ } else if (strcmp(dev->driver->name, "imx-lcdif-crtc") == 0) {
+ struct lcdif_client_platformdata *pdata = dev->platform_data;
+#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
+ /* set legacyfb_depth to be 32 for lcdif, since
+ * default format of the connectors attached to
+ * lcdif is usually RGB888
+ */
+ if (pdata->of_node == np)
+ legacyfb_depth = 32;
+#endif
+
+ return pdata->of_node == np;
+ }
+
+ /* This is a special case for dpu bliteng. */
+ if (strcmp(dev->driver->name, "imx-drm-dpu-bliteng") == 0) {
+ struct dpu_client_platformdata *pdata = dev->platform_data;
+
+ return pdata->of_node == np;
}
/* Special case for LDB, one device for two channels */
@@ -350,12 +283,115 @@ static int compare_of(struct device *dev, void *data)
return dev->of_node == np;
}
+static const char *const imx_drm_dpu_comp_parents[] = {
+ "fsl,imx8qm-dpu",
+ "fsl,imx8qxp-dpu",
+};
+
+static const char *const imx_drm_dcss_comp_parents[] = {
+ "nxp,imx8mq-dcss",
+};
+
+static bool imx_drm_parent_is_compatible(struct device *dev,
+ const char *const comp_parents[],
+ int comp_parents_size)
+{
+ struct device_node *port, *parent;
+ bool ret = false;
+ int i;
+
+ port = of_parse_phandle(dev->of_node, "ports", 0);
+ if (!port)
+ return ret;
+
+ parent = of_get_parent(port);
+
+ for (i = 0; i < comp_parents_size; i++) {
+ if (of_device_is_compatible(parent, comp_parents[i])) {
+ ret = true;
+ break;
+ }
+ }
+
+ of_node_put(parent);
+
+ of_node_put(port);
+
+ return ret;
+}
+
+static inline bool has_dpu(struct device *dev)
+{
+ return imx_drm_parent_is_compatible(dev, imx_drm_dpu_comp_parents,
+ ARRAY_SIZE(imx_drm_dpu_comp_parents));
+}
+
+static inline bool has_dcss(struct device *dev)
+{
+ return imx_drm_parent_is_compatible(dev, imx_drm_dcss_comp_parents,
+ ARRAY_SIZE(imx_drm_dcss_comp_parents));
+}
+
+static void add_dpu_bliteng_components(struct device *dev,
+ struct component_match **matchptr)
+{
+ /*
+ * As there may be two dpu bliteng device,
+ * so need add something in compare data to distinguish.
+ * Use its parent dpu's of_node as the data here.
+ */
+ struct device_node *port, *parent;
+ /* assume max dpu number is 8 */
+ struct device_node *dpu[8];
+ int num_dpu = 0;
+ int i, j;
+ bool found = false;
+
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(dev->of_node, "ports", i);
+ if (!port)
+ break;
+
+ parent = of_get_parent(port);
+
+ for (j = 0; j < num_dpu; j++) {
+ if (dpu[j] == parent) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ found = false;
+ } else {
+ if (num_dpu >= ARRAY_SIZE(dpu)) {
+ dev_err(dev, "The number of found dpu is greater than max [%ld].\n",
+ ARRAY_SIZE(dpu));
+ of_node_put(parent);
+ of_node_put(port);
+ break;
+ }
+
+ dpu[num_dpu] = parent;
+ num_dpu++;
+
+ component_match_add(dev, matchptr, compare_of, parent);
+ }
+
+ of_node_put(parent);
+ of_node_put(port);
+ }
+}
+
static int imx_drm_bind(struct device *dev)
{
struct drm_device *drm;
struct imx_drm_device *imxdrm;
int ret;
+ if (has_dpu(dev))
+ imx_drm_driver.driver_features |= DRIVER_RENDER;
+
drm = drm_dev_alloc(&imx_drm_driver, dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
@@ -389,8 +425,11 @@ static int imx_drm_bind(struct device *dev)
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;
+
+ if (has_dpu(dev) || has_dcss(dev)) {
+ drm->mode_config.allow_fb_modifiers = true;
+ dev_dbg(dev, "allow fb modifiers\n");
+ }
drm_mode_config_init(drm);
@@ -417,6 +456,10 @@ static int imx_drm_bind(struct device *dev)
dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n");
legacyfb_depth = 16;
}
+
+ if (legacyfb_depth == 16 && has_dcss(dev))
+ legacyfb_depth = 32;
+
imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
drm->mode_config.num_crtc, MAX_CRTC);
if (IS_ERR(imxdrm->fbhelper)) {
@@ -443,6 +486,7 @@ err_unbind:
#endif
component_unbind_all(drm->dev, drm);
err_vblank:
+ dev_set_drvdata(dev, NULL);
drm_vblank_cleanup(drm);
err_kms:
drm_mode_config_cleanup(drm);
@@ -457,6 +501,9 @@ static void imx_drm_unbind(struct device *dev)
struct drm_device *drm = dev_get_drvdata(dev);
struct imx_drm_device *imxdrm = drm->dev_private;
+ if (has_dpu(dev))
+ imx_drm_driver.driver_features &= ~DRIVER_RENDER;
+
drm_dev_unregister(drm);
drm_kms_helper_poll_fini(drm);
@@ -479,7 +526,14 @@ static const struct component_master_ops imx_drm_ops = {
static int imx_drm_platform_probe(struct platform_device *pdev)
{
- int ret = drm_of_component_probe(&pdev->dev, compare_of, &imx_drm_ops);
+ struct component_match *match = NULL;
+ int ret;
+
+ if (has_dpu(&pdev->dev))
+ add_dpu_bliteng_components(&pdev->dev, &match);
+
+ ret = drm_of_component_probe_with_match(&pdev->dev, match, compare_of,
+ &imx_drm_ops);
if (!ret)
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));