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

Commit d3c35337 authored by Dave Airlie's avatar Dave Airlie
Browse files

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

imx-drm updates

- atomic mode setting conversion
- replace DMFC FIFO allocation mechanism with a fixed allocation
  that is good enough for all cases
- support for external bridges connected to parallel-display
- improved error handling in imx-ldb, imx-tve, and parallel-display
- some code cleanup in imx-tve

* tag 'imx-drm-next-2016-07-14' of git://git.pengutronix.de/git/pza/linux:
  drm/imx: parallel-display: add bridge support
  drm/imx: parallel-display: check return code from of_get_drm_display_mode()
  gpu: ipu-v3: ipu-dc: don't bug out on invalid bus_format
  drm/imx: imx-tve: fix the error message
  drm/imx: imx-tve: remove unneeded 'or' operation
  drm/imx: imx-tve: check the value returned by regulator_set_voltage()
  drm/imx: imx-ldb: check return code on panel attach
  drm/imx: turn remaining container_of macros into inline functions
  drm/imx: store internal bus configuration in crtc state
  drm/imx: remove empty mode_set encoder callbacks
  drm/imx: atomic phase 3 step 3: Advertise DRIVER_ATOMIC
  drm/imx: atomic phase 3 step 2: Legacy callback fixups
  drm/bridge: dw-hdmi: Remove the legacy drm_connector_funcs structure
  drm/imx: atomic phase 3 step 1: Use atomic configuration
  drm/imx: Remove encoders' ->prepare callbacks
  drm/imx: atomic phase 2 step 2: Track plane_state->fb correctly in ->page_flip
  drm/imx: atomic phase 2 step 1: Wire up state ->reset, ->duplicate and ->destroy
  drm/imx: atomic phase 1: Use transitional atomic CRTC and plane helpers
  gpu: ipu-v3: ipu-dmfc: Use static DMFC FIFO allocation mechanism
  drm/imx: ipuv3 plane: Check different types of plane separately
parents f82c1372 f140b0cc
Loading
Loading
Loading
Loading
+3 −16
Original line number Diff line number Diff line
@@ -1495,14 +1495,6 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
}

static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
	.dpms = drm_helper_connector_dpms,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.detect = dw_hdmi_connector_detect,
	.destroy = dw_hdmi_connector_destroy,
	.force = dw_hdmi_connector_force,
};

static const struct drm_connector_funcs dw_hdmi_atomic_connector_funcs = {
	.dpms = drm_atomic_helper_connector_dpms,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.detect = dw_hdmi_connector_detect,
@@ -1634,11 +1626,6 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
	drm_connector_helper_add(&hdmi->connector,
				 &dw_hdmi_connector_helper_funcs);

	if (drm_core_check_feature(drm, DRIVER_ATOMIC))
		drm_connector_init(drm, &hdmi->connector,
				   &dw_hdmi_atomic_connector_funcs,
				   DRM_MODE_CONNECTOR_HDMIA);
	else
	drm_connector_init(drm, &hdmi->connector,
			   &dw_hdmi_connector_funcs,
			   DRM_MODE_CONNECTOR_HDMIA);
+19 −13
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@ struct imx_hdmi {
	struct regmap *regmap;
};

static inline struct imx_hdmi *enc_to_imx_hdmi(struct drm_encoder *e)
{
	return container_of(e, struct imx_hdmi, encoder);
}

static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
	{
		45250000, {
@@ -109,15 +114,9 @@ static void dw_hdmi_imx_encoder_disable(struct drm_encoder *encoder)
{
}

static void dw_hdmi_imx_encoder_mode_set(struct drm_encoder *encoder,
					 struct drm_display_mode *mode,
					 struct drm_display_mode *adj_mode)
static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)
{
}

static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder)
{
	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
	struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);
	int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);

	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
@@ -125,16 +124,23 @@ static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder)
			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
}

static void dw_hdmi_imx_encoder_prepare(struct drm_encoder *encoder)
static int dw_hdmi_imx_atomic_check(struct drm_encoder *encoder,
				    struct drm_crtc_state *crtc_state,
				    struct drm_connector_state *conn_state)
{
	imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_RGB888_1X24);
	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);

	imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
	imx_crtc_state->di_hsync_pin = 2;
	imx_crtc_state->di_vsync_pin = 3;

	return 0;
}

static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
	.mode_set   = dw_hdmi_imx_encoder_mode_set,
	.prepare    = dw_hdmi_imx_encoder_prepare,
	.commit     = dw_hdmi_imx_encoder_commit,
	.enable     = dw_hdmi_imx_encoder_enable,
	.disable    = dw_hdmi_imx_encoder_disable,
	.atomic_check = dw_hdmi_imx_atomic_check,
};

static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
+78 −42
Original line number Diff line number Diff line
@@ -15,10 +15,14 @@
 */
#include <linux/component.h>
#include <linux/device.h>
#include <linux/dma-buf.h>
#include <linux/fb.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>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h>
@@ -41,6 +45,7 @@ struct imx_drm_device {
	struct imx_drm_crtc			*crtc[MAX_CRTC];
	unsigned int				pipes;
	struct drm_fbdev_cma			*fbhelper;
	struct drm_atomic_state			*state;
};

struct imx_drm_crtc {
@@ -85,45 +90,6 @@ static int imx_drm_driver_unload(struct drm_device *drm)
	return 0;
}

static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc)
{
	struct imx_drm_device *imxdrm = crtc->dev->dev_private;
	unsigned i;

	for (i = 0; i < MAX_CRTC; i++)
		if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc)
			return imxdrm->crtc[i];

	return NULL;
}

int imx_drm_set_bus_config(struct drm_encoder *encoder, u32 bus_format,
		int hsync_pin, int vsync_pin, u32 bus_flags)
{
	struct imx_drm_crtc_helper_funcs *helper;
	struct imx_drm_crtc *imx_crtc;

	imx_crtc = imx_drm_find_crtc(encoder->crtc);
	if (!imx_crtc)
		return -EINVAL;

	helper = &imx_crtc->imx_drm_helper_funcs;
	if (helper->set_interface_pix_fmt)
		return helper->set_interface_pix_fmt(encoder->crtc,
					bus_format, hsync_pin, vsync_pin,
					bus_flags);
	return 0;
}
EXPORT_SYMBOL_GPL(imx_drm_set_bus_config);

int imx_drm_set_bus_format(struct drm_encoder *encoder, u32 bus_format)
{
	return imx_drm_set_bus_config(encoder, bus_format, 2, 3,
				      DRM_BUS_FLAG_DE_HIGH |
				      DRM_BUS_FLAG_PIXDATA_NEGEDGE);
}
EXPORT_SYMBOL_GPL(imx_drm_set_bus_format);

int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc)
{
	return drm_crtc_vblank_get(imx_drm_crtc->crtc);
@@ -208,6 +174,63 @@ static void imx_drm_output_poll_changed(struct drm_device *drm)
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,
};

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, true);

	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,
};

/*
@@ -249,6 +272,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
	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);

@@ -279,6 +303,8 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
		}
	}

	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
@@ -289,7 +315,6 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
		dev_warn(drm->dev, "Invalid legacyfb_depth.  Defaulting to 16bpp\n");
		legacyfb_depth = 16;
	}
	drm_helper_disable_unused_functions(drm);
	imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
				drm->mode_config.num_crtc, MAX_CRTC);
	if (IS_ERR(imxdrm->fbhelper)) {
@@ -403,7 +428,8 @@ 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_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
				  DRIVER_ATOMIC,
	.load			= imx_drm_driver_load,
	.unload			= imx_drm_driver_unload,
	.lastclose		= imx_drm_driver_lastclose,
@@ -491,6 +517,7 @@ static int imx_drm_platform_remove(struct platform_device *pdev)
static int imx_drm_suspend(struct device *dev)
{
	struct drm_device *drm_dev = dev_get_drvdata(dev);
	struct imx_drm_device *imxdrm;

	/* The drm_dev is NULL before .load hook is called */
	if (drm_dev == NULL)
@@ -498,17 +525,26 @@ static int imx_drm_suspend(struct device *dev)

	drm_kms_helper_poll_disable(drm_dev);

	imxdrm = drm_dev->dev_private;
	imxdrm->state = drm_atomic_helper_suspend(drm_dev);
	if (IS_ERR(imxdrm->state)) {
		drm_kms_helper_poll_enable(drm_dev);
		return PTR_ERR(imxdrm->state);
	}

	return 0;
}

static int imx_drm_resume(struct device *dev)
{
	struct drm_device *drm_dev = dev_get_drvdata(dev);
	struct imx_drm_device *imx_drm;

	if (drm_dev == NULL)
		return 0;

	drm_helper_resume_force_mode(drm_dev);
	imx_drm = drm_dev->dev_private;
	drm_atomic_helper_resume(drm_dev, imx_drm->state);
	drm_kms_helper_poll_enable(drm_dev);

	return 0;
+13 −8
Original line number Diff line number Diff line
@@ -15,12 +15,22 @@ struct platform_device;

unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc);

struct imx_crtc_state {
	struct drm_crtc_state			base;
	u32					bus_format;
	u32					bus_flags;
	int					di_hsync_pin;
	int					di_vsync_pin;
};

static inline struct imx_crtc_state *to_imx_crtc_state(struct drm_crtc_state *s)
{
	return container_of(s, struct imx_crtc_state, base);
}

struct imx_drm_crtc_helper_funcs {
	int (*enable_vblank)(struct drm_crtc *crtc);
	void (*disable_vblank)(struct drm_crtc *crtc);
	int (*set_interface_pix_fmt)(struct drm_crtc *crtc,
			u32 bus_format, int hsync_pin, int vsync_pin,
			u32 bus_flags);
	const struct drm_crtc_helper_funcs *crtc_helper_funcs;
	const struct drm_crtc_funcs *crtc_funcs;
};
@@ -42,11 +52,6 @@ void imx_drm_mode_config_init(struct drm_device *drm);

struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);

int imx_drm_set_bus_config(struct drm_encoder *encoder, u32 bus_format,
		int hsync_pin, int vsync_pin, u32 bus_flags);
int imx_drm_set_bus_format(struct drm_encoder *encoder,
		u32 bus_format);

int imx_drm_encoder_parse_of(struct drm_device *drm,
	struct drm_encoder *encoder, struct device_node *np);

+122 −70
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#include <linux/clk.h>
#include <linux/component.h>
#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_of.h>
@@ -49,9 +51,6 @@
#define LDB_DI1_VS_POL_ACT_LOW		(1 << 10)
#define LDB_BGREF_RMODE_INT		(1 << 15)

#define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector)
#define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder)

struct imx_ldb;

struct imx_ldb_channel {
@@ -66,9 +65,19 @@ struct imx_ldb_channel {
	int edid_len;
	struct drm_display_mode mode;
	int mode_valid;
	int bus_format;
	u32 bus_format;
};

static inline struct imx_ldb_channel *con_to_imx_ldb_ch(struct drm_connector *c)
{
	return container_of(c, struct imx_ldb_channel, connector);
}

static inline struct imx_ldb_channel *enc_to_imx_ldb_ch(struct drm_encoder *e)
{
	return container_of(e, struct imx_ldb_channel, encoder);
}

struct bus_mux {
	int reg;
	int shift;
@@ -93,6 +102,32 @@ static enum drm_connector_status imx_ldb_connector_detect(
	return connector_status_connected;
}

static void imx_ldb_ch_set_bus_format(struct imx_ldb_channel *imx_ldb_ch,
				      u32 bus_format)
{
	struct imx_ldb *ldb = imx_ldb_ch->ldb;
	int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;

	switch (bus_format) {
	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
		break;
	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
		if (imx_ldb_ch->chno == 0 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
		if (imx_ldb_ch->chno == 1 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
		break;
	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
		if (imx_ldb_ch->chno == 0 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
					 LDB_BIT_MAP_CH0_JEIDA;
		if (imx_ldb_ch->chno == 1 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
					 LDB_BIT_MAP_CH1_JEIDA;
		break;
	}
}

static int imx_ldb_connector_get_modes(struct drm_connector *connector)
{
	struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
@@ -100,11 +135,7 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector)

	if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
	    imx_ldb_ch->panel->funcs->get_modes) {
		struct drm_display_info *di = &connector->display_info;

		num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
		if (!imx_ldb_ch->bus_format && di->num_bus_formats)
			imx_ldb_ch->bus_format = di->bus_formats[0];
		if (num_modes > 0)
			return num_modes;
	}
@@ -141,10 +172,6 @@ static struct drm_encoder *imx_ldb_connector_best_encoder(
	return &imx_ldb_ch->encoder;
}

static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode)
{
}

static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
		unsigned long serial_clk, unsigned long di_clk)
{
@@ -173,43 +200,7 @@ static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
			chno);
}

static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
{
	struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
	struct imx_ldb *ldb = imx_ldb_ch->ldb;
	int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
	u32 bus_format;

	switch (imx_ldb_ch->bus_format) {
	default:
		dev_warn(ldb->dev,
			 "could not determine data mapping, default to 18-bit \"spwg\"\n");
		/* fallthrough */
	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
		bus_format = MEDIA_BUS_FMT_RGB666_1X18;
		break;
	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
		bus_format = MEDIA_BUS_FMT_RGB888_1X24;
		if (imx_ldb_ch->chno == 0 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
		if (imx_ldb_ch->chno == 1 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
		break;
	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
		bus_format = MEDIA_BUS_FMT_RGB888_1X24;
		if (imx_ldb_ch->chno == 0 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
					 LDB_BIT_MAP_CH0_JEIDA;
		if (imx_ldb_ch->chno == 1 || dual)
			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
					 LDB_BIT_MAP_CH1_JEIDA;
		break;
	}

	imx_drm_set_bus_format(encoder, bus_format);
}

static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
{
	struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
	struct imx_ldb *ldb = imx_ldb_ch->ldb;
@@ -219,8 +210,13 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
	drm_panel_prepare(imx_ldb_ch->panel);

	if (dual) {
		clk_set_parent(ldb->clk_sel[mux], ldb->clk[0]);
		clk_set_parent(ldb->clk_sel[mux], ldb->clk[1]);

		clk_prepare_enable(ldb->clk[0]);
		clk_prepare_enable(ldb->clk[1]);
	} else {
		clk_set_parent(ldb->clk_sel[mux], ldb->clk[imx_ldb_ch->chno]);
	}

	if (imx_ldb_ch == &ldb->channel[0] || dual) {
@@ -265,6 +261,7 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
	unsigned long serial_clk;
	unsigned long di_clk = mode->clock * 1000;
	int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder);
	u32 bus_format = imx_ldb_ch->bus_format;

	if (mode->clock > 170000) {
		dev_warn(ldb->dev,
@@ -286,18 +283,36 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
	}

	/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
	if (imx_ldb_ch == &ldb->channel[0]) {
	if (imx_ldb_ch == &ldb->channel[0] || dual) {
		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
			ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
		else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
			ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
	}
	if (imx_ldb_ch == &ldb->channel[1]) {
	if (imx_ldb_ch == &ldb->channel[1] || dual) {
		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
			ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
		else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
			ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
	}

	if (!bus_format) {
		struct drm_connector_state *conn_state;
		struct drm_connector *connector;
		int i;

		for_each_connector_in_state(encoder->crtc->state->state,
					    connector, conn_state, i) {
			struct drm_display_info *di = &connector->display_info;

			if (conn_state->crtc == encoder->crtc &&
			    di->num_bus_formats) {
				bus_format = di->bus_formats[0];
				break;
			}
		}
	}
	imx_ldb_ch_set_bus_format(imx_ldb_ch, bus_format);
}

static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
@@ -357,11 +372,45 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
	drm_panel_unprepare(imx_ldb_ch->panel);
}

static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
					struct drm_crtc_state *crtc_state,
					struct drm_connector_state *conn_state)
{
	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
	struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
	struct drm_display_info *di = &conn_state->connector->display_info;
	u32 bus_format = imx_ldb_ch->bus_format;

	/* Bus format description in DT overrides connector display info. */
	if (!bus_format && di->num_bus_formats)
		bus_format = di->bus_formats[0];
	switch (bus_format) {
	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
		imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
		break;
	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
		imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
		break;
	default:
		return -EINVAL;
	}

	imx_crtc_state->di_hsync_pin = 2;
	imx_crtc_state->di_vsync_pin = 3;

	return 0;
}


static const struct drm_connector_funcs imx_ldb_connector_funcs = {
	.dpms = drm_helper_connector_dpms,
	.dpms = drm_atomic_helper_connector_dpms,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.detect = imx_ldb_connector_detect,
	.destroy = imx_drm_connector_destroy,
	.reset = drm_atomic_helper_connector_reset,
	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

static const struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
@@ -374,11 +423,10 @@ static const struct drm_encoder_funcs imx_ldb_encoder_funcs = {
};

static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
	.dpms = imx_ldb_encoder_dpms,
	.prepare = imx_ldb_encoder_prepare,
	.commit = imx_ldb_encoder_commit,
	.mode_set = imx_ldb_encoder_mode_set,
	.enable = imx_ldb_encoder_enable,
	.disable = imx_ldb_encoder_disable,
	.atomic_check = imx_ldb_encoder_atomic_check,
};

static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
@@ -400,10 +448,10 @@ static int imx_ldb_register(struct drm_device *drm,
	struct imx_ldb_channel *imx_ldb_ch)
{
	struct imx_ldb *ldb = imx_ldb_ch->ldb;
	struct drm_encoder *encoder = &imx_ldb_ch->encoder;
	int ret;

	ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->encoder,
				       imx_ldb_ch->child);
	ret = imx_drm_encoder_parse_of(drm, encoder, imx_ldb_ch->child);
	if (ret)
		return ret;

@@ -417,9 +465,8 @@ static int imx_ldb_register(struct drm_device *drm,
			return ret;
	}

	drm_encoder_helper_add(&imx_ldb_ch->encoder,
			&imx_ldb_encoder_helper_funcs);
	drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs,
	drm_encoder_helper_add(encoder, &imx_ldb_encoder_helper_funcs);
	drm_encoder_init(drm, encoder, &imx_ldb_encoder_funcs,
			 DRM_MODE_ENCODER_LVDS, NULL);

	drm_connector_helper_add(&imx_ldb_ch->connector,
@@ -427,11 +474,14 @@ static int imx_ldb_register(struct drm_device *drm,
	drm_connector_init(drm, &imx_ldb_ch->connector,
			   &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);

	if (imx_ldb_ch->panel)
		drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
	if (imx_ldb_ch->panel) {
		ret = drm_panel_attach(imx_ldb_ch->panel,
				       &imx_ldb_ch->connector);
		if (ret)
			return ret;
	}

	drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
			&imx_ldb_ch->encoder);
	drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, encoder);

	return 0;
}
@@ -560,6 +610,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
		struct imx_ldb_channel *channel;
		struct device_node *ddc_node;
		struct device_node *ep;
		int bus_format;

		ret = of_property_read_u32(child, "reg", &i);
		if (ret || i < 0 || i > 1)
@@ -632,21 +683,22 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
			}
		}

		channel->bus_format = of_get_bus_format(dev, child);
		if (channel->bus_format == -EINVAL) {
		bus_format = of_get_bus_format(dev, child);
		if (bus_format == -EINVAL) {
			/*
			 * If no bus format was specified in the device tree,
			 * we can still get it from the connected panel later.
			 */
			if (channel->panel && channel->panel->funcs &&
			    channel->panel->funcs->get_modes)
				channel->bus_format = 0;
				bus_format = 0;
		}
		if (channel->bus_format < 0) {
		if (bus_format < 0) {
			dev_err(dev, "could not determine data mapping: %d\n",
				channel->bus_format);
			return channel->bus_format;
				bus_format);
			return bus_format;
		}
		channel->bus_format = bus_format;

		ret = imx_ldb_register(drm, channel);
		if (ret)
Loading