Эх сурвалжийг харах

Merge branch 'audio_display-ti-linux-4.19.y' of git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree into ti-linux-4.19.y

TI-Feature: audio-display
TI-Tree: git@git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree.git
TI-Branch: audio_display-ti-linux-4.19.y

* 'audio_display-ti-linux-4.19.y' of git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree:
  drm/tidss: dispc7: Add subrev to struct dispc7_features
  drm/tidss: dispc7: Workaround errata i2000: Disable YUV formats for AM6 DSS
  drm/bridge: sii902x: Disable mclk only if it was successfully enabled
  drm/bridge: sii902x: Fix i2s-fifo-routing property parsing
  drm/bridge: sii902x: Fix locking error in sii902x_bridge_mode_set()
  drm/bridge/sii902x: Fix EDID readback
  drm/bridge: sii902x: Select I2C_MUX
  Revert "drm/bridge: sii902x: HACK Add more relaxed custom DDC transfer implementation"

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger 6 жил өмнө
parent
commit
c416a87331

+ 0 - 5
Documentation/devicetree/bindings/display/bridge/sii902x.txt

@@ -23,11 +23,6 @@ Optional properties:
 	  "i2s-fifo-routing"-property is present. This property follows
 	  Documentation/devicetree/bindings/clock/clock-bindings.txt
 	  consumer binding.
-	- use-custom-ddc-probe: Use custom DDC read function for
-	  getting EDID. SiI902X DDC pass trough transfers appears to
-	  be somewhat unreliable on some systems. When this property
-	  is present there is an extra sleep before each DDC transfer
-	  operation. This helps to make the ddc probe more reliable.
 
 Optional subnodes:
 	- video input: this subnode can contain a video input port node

+ 0 - 1
arch/arm/boot/dts/am437x-gp-evm-hdmi.dts

@@ -84,7 +84,6 @@
 			0
 		>;
 		clocks = <&sii9022_mclk>;
-		use-custom-ddc-probe;
 
 		ports {
 			#address-cells = <1>;

+ 0 - 1
arch/arm/boot/dts/keystone-k2g-evm.dts

@@ -513,7 +513,6 @@
 			0
 		>;
 		clocks = <&sii9022_mclk>;
-		use-custom-ddc-probe;
 
 		ports {
 			#address-cells = <1>;

+ 1 - 0
drivers/gpu/drm/bridge/Kconfig

@@ -95,6 +95,7 @@ config DRM_SII902X
 	depends on OF
 	select DRM_KMS_HELPER
 	select REGMAP_I2C
+	select I2C_MUX
 	---help---
 	  Silicon Image sii902x bridge chip driver.
 

+ 196 - 165
drivers/gpu/drm/bridge/sii902x.c

@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2018 Renesas Electronics
+ *
  * Copyright (C) 2016 Atmel
  *		      Bo Shen <voice.shen@atmel.com>
  *
@@ -21,6 +23,7 @@
  */
 
 #include <linux/gpio/consumer.h>
+#include <linux/i2c-mux.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
@@ -174,16 +177,57 @@ struct sii902x {
 	struct drm_bridge bridge;
 	struct drm_connector connector;
 	struct gpio_desc *reset_gpio;
-	bool use_custom_ddc_probe;
+	struct i2c_mux_core *i2cmux;
 	struct mutex mutex;
 	struct sii902x_audio {
 		struct platform_device *pdev;
 		u8 channels;
 		struct clk *mclk;
+		bool mclk_enabled;
 		u32 i2s_fifo_routing[4];
 	} audio;
 };
 
+static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val)
+{
+	union i2c_smbus_data data;
+	int ret;
+
+	ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
+			       I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data);
+
+	if (ret < 0)
+		return ret;
+
+	*val = data.byte;
+	return 0;
+}
+
+static int sii902x_write_unlocked(struct i2c_client *i2c, u8 reg, u8 val)
+{
+	union i2c_smbus_data data;
+
+	data.byte = val;
+
+	return __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
+				I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA,
+				&data);
+}
+
+static int sii902x_update_bits_unlocked(struct i2c_client *i2c, u8 reg, u8 mask,
+					u8 val)
+{
+	int ret;
+	u8 status;
+
+	ret = sii902x_read_unlocked(i2c, reg, &status);
+	if (ret)
+		return ret;
+	status &= ~mask;
+	status |= val & mask;
+	return sii902x_write_unlocked(i2c, reg, status);
+}
+
 static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge)
 {
 	return container_of(bridge, struct sii902x, bridge);
@@ -232,129 +276,17 @@ static const struct drm_connector_funcs sii902x_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-/*
- * This is a copy-paste from drm_do_probe_ddc_edid() with the extra
- * sleep to make SiI902x pass trough DDC transfer more reliable.
- */
-#define DDC_SEGMENT_ADDR 0x30
-static int
-sii902x_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
-{
-	struct i2c_adapter *adapter = data;
-	unsigned char start = block * EDID_LENGTH;
-	unsigned char segment = block >> 1;
-	unsigned char xfers = segment ? 3 : 2;
-	int ret, retries = 5;
-
-	/*
-	 * The core I2C driver will automatically retry the transfer if the
-	 * adapter reports EAGAIN. However, we find that bit-banging transfers
-	 * are susceptible to errors under a heavily loaded machine and
-	 * generate spurious NAKs and timeouts. Retrying the transfer
-	 * of the individual block a few times seems to overcome this.
-	 */
-	do {
-		struct i2c_msg msgs[] = {
-			{
-				.addr	= DDC_SEGMENT_ADDR,
-				.flags	= 0,
-				.len	= 1,
-				.buf	= &segment,
-			}, {
-				.addr	= DDC_ADDR,
-				.flags	= 0,
-				.len	= 1,
-				.buf	= &start,
-			}, {
-				.addr	= DDC_ADDR,
-				.flags	= I2C_M_RD,
-				.len	= len,
-				.buf	= buf,
-			}
-		};
-
-		/* This sleep helps k2g-evm DDC read to become reliable */
-		msleep(10);
-
-		/*
-		 * Avoid sending the segment addr to not upset non-compliant
-		 * DDC monitors.
-		 */
-		ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
-
-		if (ret == -ENXIO) {
-			DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
-					adapter->name);
-			break;
-		}
-	} while (ret != xfers && --retries);
-
-	return ret == xfers ? 0 : -1;
-}
-
-/*
- * This is a simplified version of drm_get_edid(). It does not implement
- * all the features - just the necessary parts - and it uses the above
- * custom DDC read function. Tile group feature is not supported.
- */
-static struct edid *sii902x_get_edid(struct drm_connector *connector,
-				     struct i2c_adapter *adapter)
-{
-	struct edid *edid;
-
-	if (connector->force == DRM_FORCE_OFF)
-		return NULL;
-
-	edid = drm_do_get_edid(connector, sii902x_do_probe_ddc_edid, adapter);
-
-	return edid;
-}
-
 static int sii902x_get_modes(struct drm_connector *connector)
 {
 	struct sii902x *sii902x = connector_to_sii902x(connector);
-	struct regmap *regmap = sii902x->regmap;
 	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
-	struct device *dev = &sii902x->i2c->dev;
-	unsigned long timeout;
-	unsigned int retries;
-	unsigned int status;
 	struct edid *edid;
 	u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
-	int num = 0;
-	int ret;
+	int num = 0, ret;
 
 	mutex_lock(&sii902x->mutex);
 
-	ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
-				 SII902X_SYS_CTRL_DDC_BUS_REQ,
-				 SII902X_SYS_CTRL_DDC_BUS_REQ);
-	if (ret)
-		goto error_out;
-
-	timeout = jiffies +
-		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
-	do {
-		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
-		if (ret)
-			goto error_out;
-	} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
-		 time_before(jiffies, timeout));
-
-	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-		dev_err(dev, "failed to acquire the i2c bus\n");
-		ret = -ETIMEDOUT;
-		goto error_out;
-	}
-
-	ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status);
-	if (ret)
-		goto error_out;
-
-	if (sii902x->use_custom_ddc_probe)
-		edid = sii902x_get_edid(connector, sii902x->i2c->adapter);
-	else
-		edid = drm_get_edid(connector, sii902x->i2c->adapter);
+	edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]);
 	drm_connector_update_edid_property(connector, edid);
 	if (edid) {
 	        if (drm_detect_hdmi_monitor(edid))
@@ -369,44 +301,11 @@ static int sii902x_get_modes(struct drm_connector *connector)
 	if (ret)
 		goto error_out;
 
-	/*
-	 * Sometimes the I2C bus can stall after failure to use the
-	 * EDID channel. Retry a few times to see if things clear
-	 * up, else continue anyway.
-	 */
-	retries = 5;
-	do {
-		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA,
-				  &status);
-		retries--;
-	} while (ret && retries);
-	if (ret)
-		dev_err(dev, "failed to read status (%d)\n", ret);
-
-	ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
-				 SII902X_SYS_CTRL_DDC_BUS_REQ |
-				 SII902X_SYS_CTRL_DDC_BUS_GRTD |
+	ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
 				 SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);
 	if (ret)
 		goto error_out;
 
-	timeout = jiffies +
-		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
-	do {
-		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
-		if (ret)
-			goto error_out;
-	} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
-			   SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
-		 time_before(jiffies, timeout));
-
-	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
-		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-		dev_err(dev, "failed to release the i2c bus\n");
-		ret = -ETIMEDOUT;
-		goto error_out;
-	}
-
 	ret = num;
 
 error_out:
@@ -484,25 +383,25 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
 
 	ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);
 	if (ret)
-		return;
+		goto out;
 
 	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, adj, false);
 	if (ret < 0) {
 		DRM_ERROR("couldn't fill AVI infoframe\n");
-		return;
+		goto out;
 	}
 
 	ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
 	if (ret < 0) {
 		DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
-		return;
+		goto out;
 	}
 
 	/* Do not send the infoframe header, but keep the CRC field. */
 	regmap_bulk_write(regmap, SII902X_TPI_AVI_INFOFRAME,
 			  buf + HDMI_INFOFRAME_HEADER_SIZE - 1,
 			  HDMI_AVI_INFOFRAME_SIZE + 1);
-
+out:
 	mutex_unlock(&sii902x->mutex);
 }
 
@@ -660,6 +559,13 @@ static int sii902x_audio_hw_params(struct device *dev, void *data,
 		return -EINVAL;
 	};
 
+	ret = clk_prepare_enable(sii902x->audio.mclk);
+	if (ret) {
+		dev_err(dev, "Enabling mclk failed: %d\n", ret);
+		return ret;
+	}
+	sii902x->audio.mclk_enabled = true;
+
 	mclk_rate = clk_get_rate(sii902x->audio.mclk);
 
 	ret = sii902x_select_mclk_div(&i2s_config_reg, params->sample_rate,
@@ -670,12 +576,6 @@ static int sii902x_audio_hw_params(struct device *dev, void *data,
 
 	sii902x->audio.channels = params->channels;
 
-	ret = clk_prepare_enable(sii902x->audio.mclk);
-	if (ret) {
-		dev_err(dev, "Enabling mclk failed: %d\n", ret);
-		return ret;
-	}
-
 	mutex_lock(&sii902x->mutex);
 
 	ret = sii902x_mute(sii902x, true);
@@ -756,7 +656,10 @@ static void sii902x_audio_shutdown(struct device *dev, void *data)
 
 	mutex_unlock(&sii902x->mutex);
 
-	clk_disable_unprepare(sii902x->audio.mclk);
+	if (sii902x->audio.mclk_enabled) {
+		clk_disable_unprepare(sii902x->audio.mclk);
+		sii902x->audio.mclk_enabled = false;
+	}
 }
 
 int sii902x_audio_digital_mute(struct device *dev, void *data, bool enable)
@@ -834,7 +737,7 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x,
 	}
 
 	for (i = 0; i < ARRAY_SIZE(sii902x->audio.i2s_fifo_routing); i++) {
-		if (sii902x->audio.i2s_fifo_routing[i] | ENABLE_BIT)
+		if (sii902x->audio.i2s_fifo_routing[i] & ENABLE_BIT)
 			codec_data.max_i2s_channels += 2;
 		sii902x->audio.i2s_fifo_routing[i] |= i2s_fifo_defaults[i];
 	}
@@ -877,6 +780,121 @@ static irqreturn_t sii902x_interrupt(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+/*
+ * The purpose of sii902x_i2c_bypass_select is to enable the pass through
+ * mode of the HDMI transmitter. Do not use regmap from within this function,
+ * only use sii902x_*_unlocked functions to read/modify/write registers.
+ * We are holding the parent adapter lock here, keep this in mind before
+ * adding more i2c transactions.
+ *
+ * Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere
+ * in this driver, we need to make sure that we only touch 0x1A[2:1] from
+ * within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that
+ * we leave the remaining bits as we have found them.
+ */
+static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
+{
+	struct sii902x *sii902x = i2c_mux_priv(mux);
+	struct device *dev = &sii902x->i2c->dev;
+	unsigned long timeout;
+	u8 status;
+	int ret;
+
+	ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					   SII902X_SYS_CTRL_DDC_BUS_REQ,
+					   SII902X_SYS_CTRL_DDC_BUS_REQ);
+	if (ret)
+		return ret;
+
+	timeout = jiffies +
+		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
+	do {
+		ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					    &status);
+		if (ret)
+			return ret;
+	} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
+		 time_before(jiffies, timeout));
+
+	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
+		dev_err(dev, "Failed to acquire the i2c bus\n");
+		return -ETIMEDOUT;
+	}
+
+	return sii902x_write_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+				      status);
+}
+
+/*
+ * The purpose of sii902x_i2c_bypass_deselect is to disable the pass through
+ * mode of the HDMI transmitter. Do not use regmap from within this function,
+ * only use sii902x_*_unlocked functions to read/modify/write registers.
+ * We are holding the parent adapter lock here, keep this in mind before
+ * adding more i2c transactions.
+ *
+ * Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere
+ * in this driver, we need to make sure that we only touch 0x1A[2:1] from
+ * within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that
+ * we leave the remaining bits as we have found them.
+ */
+static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
+{
+	struct sii902x *sii902x = i2c_mux_priv(mux);
+	struct device *dev = &sii902x->i2c->dev;
+	unsigned long timeout;
+	unsigned int retries;
+	u8 status;
+	int ret;
+
+	/*
+	 * When the HDMI transmitter is in pass through mode, we need an
+	 * (undocumented) additional delay between STOP and START conditions
+	 * to guarantee the bus won't get stuck.
+	 */
+	udelay(30);
+
+	/*
+	 * Sometimes the I2C bus can stall after failure to use the
+	 * EDID channel. Retry a few times to see if things clear
+	 * up, else continue anyway.
+	 */
+	retries = 5;
+	do {
+		ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					    &status);
+		retries--;
+	} while (ret && retries);
+	if (ret) {
+		dev_err(dev, "failed to read status (%d)\n", ret);
+		return ret;
+	}
+
+	ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					   SII902X_SYS_CTRL_DDC_BUS_REQ |
+					   SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
+	if (ret)
+		return ret;
+
+	timeout = jiffies +
+		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
+	do {
+		ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					    &status);
+		if (ret)
+			return ret;
+	} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
+			   SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
+		 time_before(jiffies, timeout));
+
+	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
+		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
+		dev_err(dev, "failed to release the i2c bus\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
 static const struct drm_bridge_timings default_sii902x_timings = {
 	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
 		 | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
@@ -892,6 +910,13 @@ static int sii902x_probe(struct i2c_client *client,
 	u8 chipid[4];
 	int ret;
 
+	ret = i2c_check_functionality(client->adapter,
+				      I2C_FUNC_SMBUS_BYTE_DATA);
+	if (!ret) {
+		dev_err(dev, "I2C adapter not suitable\n");
+		return -EIO;
+	}
+
 	sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL);
 	if (!sii902x)
 		return -ENOMEM;
@@ -947,9 +972,6 @@ static int sii902x_probe(struct i2c_client *client,
 			return ret;
 	}
 
-	sii902x->use_custom_ddc_probe =
-		of_property_read_bool(dev->of_node, "use-custom-ddc-probe");
-
 	sii902x->bridge.funcs = &sii902x_bridge_funcs;
 	sii902x->bridge.of_node = dev->of_node;
 	sii902x->bridge.timings = &default_sii902x_timings;
@@ -959,7 +981,15 @@ static int sii902x_probe(struct i2c_client *client,
 
 	i2c_set_clientdata(client, sii902x);
 
-	return 0;
+	sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev,
+					1, 0, I2C_MUX_GATE,
+					sii902x_i2c_bypass_select,
+					sii902x_i2c_bypass_deselect);
+	if (!sii902x->i2cmux)
+		return -ENOMEM;
+
+	sii902x->i2cmux->priv = sii902x;
+	return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0);
 }
 
 static int sii902x_remove(struct i2c_client *client)
@@ -967,6 +997,7 @@ static int sii902x_remove(struct i2c_client *client)
 {
 	struct sii902x *sii902x = i2c_get_clientdata(client);
 
+	i2c_mux_del_adapters(sii902x->i2cmux);
 	drm_bridge_remove(&sii902x->bridge);
 
 	return 0;

+ 16 - 4
drivers/gpu/drm/tidss/tidss_dispc7.c

@@ -55,6 +55,8 @@ static const struct dispc7_features dispc7_am6_feats = {
 		.xinc_max = 32,
 	},
 
+	.subrev = DSS7_AM6,
+
 	.num_vps = 2,
 	.vp_name = { "vp1", "vp2" },
 	.ovr_name = { "ovr1", "ovr2" },
@@ -66,6 +68,10 @@ static const struct dispc7_features dispc7_am6_feats = {
 	.vid_name = { "vid", "vidl1" },
 	.vid_lite = { false, true, },
 	.vid_order = { 1, 0 },
+
+	.errata = {
+		.i2000 = true,
+	},
 };
 
 static const struct of_device_id dispc7_of_table[] = {
@@ -1941,7 +1947,7 @@ static int dispc7_modeset_init(struct dispc_device *dispc)
 	struct tidss_device *tidss = dispc->tidss;
 	struct device *dev = tidss->dev;
 	u32 fourccs[ARRAY_SIZE(dispc7_color_formats)];
-	unsigned int i;
+	unsigned int i, num_fourccs;
 
 	struct pipe {
 		u32 hw_videoport;
@@ -1956,8 +1962,14 @@ static int dispc7_modeset_init(struct dispc_device *dispc)
 	u32 num_pipes = 0;
 	u32 crtc_mask;
 
-	for (i = 0; i < ARRAY_SIZE(fourccs); ++i)
-		fourccs[i] = dispc7_color_formats[i].fourcc;
+	num_fourccs = 0;
+	for (i = 0; i < ARRAY_SIZE(fourccs); ++i) {
+		if (dispc->feat->errata.i2000 &&
+		    dispc7_fourcc_is_yuv(dispc7_color_formats[i].fourcc))
+			continue;
+
+		fourccs[num_fourccs++] = dispc7_color_formats[i].fourcc;
+	}
 
 	/* first find all the connected panels & bridges */
 
@@ -2024,7 +2036,7 @@ static int dispc7_modeset_init(struct dispc_device *dispc)
 
 		tplane = tidss_plane_create(tidss, hw_plane_id,
 					    DRM_PLANE_TYPE_PRIMARY, crtc_mask,
-					    fourccs, ARRAY_SIZE(fourccs));
+					    fourccs, num_fourccs);
 		if (IS_ERR(tplane)) {
 			dev_err(tidss->dev, "plane create failed\n");
 			return PTR_ERR(tplane);

+ 12 - 0
drivers/gpu/drm/tidss/tidss_dispc7.h

@@ -21,11 +21,19 @@ struct dispc7_features_scaling {
 	u32 xinc_max;
 };
 
+struct dispc7_errata {
+	bool i2000; /* DSS Does Not Support YUV Pixel Data Formats */
+};
+
 enum dispc7_vp_bus_type {
 	DISPC7_VP_DPI,
 	DISPC7_VP_OLDI,
 };
 
+enum dispc7_dss_subrevision {
+	DSS7_AM6,
+};
+
 struct dispc7_features {
 	/* XXX should these come from the .dts? Min pclk is not feature of DSS IP */
 	unsigned long min_pclk;
@@ -33,6 +41,8 @@ struct dispc7_features {
 
 	struct dispc7_features_scaling scaling;
 
+	enum dispc7_dss_subrevision subrev;
+
 	u32 num_vps;
 	const char *vp_name[DISPC7_MAX_PORTS]; /* Should match dt reg names */
 	const char *ovr_name[DISPC7_MAX_PORTS]; /* Should match dt reg names */
@@ -42,6 +52,8 @@ struct dispc7_features {
 	const char *vid_name[DISPC7_MAX_PLANES]; /* Should match dt reg names */
 	bool vid_lite[DISPC7_MAX_PLANES];
 	u32 vid_order[DISPC7_MAX_PLANES];
+
+	struct dispc7_errata errata;
 };
 
 #define DSS_REVISION			0x4