diff mbox

[RFC,5/7] drm: atmel-hlcdc: add DPI support

Message ID 1412609873-1894-6-git-send-email-boris.brezillon@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Boris BREZILLON Oct. 6, 2014, 3:37 p.m. UTC
Implement a DPI host in the HLCDC driver.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/gpu/drm/atmel-hlcdc/Kconfig           |   1 +
 drivers/gpu/drm/atmel-hlcdc/Makefile          |   1 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dpi.c | 212 ++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dpi.c
diff mbox

Patch

diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig
index 942407f..af660e2 100644
--- a/drivers/gpu/drm/atmel-hlcdc/Kconfig
+++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig
@@ -5,6 +5,7 @@  config DRM_ATMEL_HLCDC
 	select DRM_KMS_HELPER
 	select DRM_KMS_FB_HELPER
 	select DRM_KMS_CMA_HELPER
+	select DRM_MIPI_DPI
 	select DRM_PANEL
 	select MFD_ATMEL_HLCDC
 	depends on OF
diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile
index 10ae426..979e431 100644
--- a/drivers/gpu/drm/atmel-hlcdc/Makefile
+++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
@@ -1,5 +1,6 @@ 
 atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
 		atmel_hlcdc_dc.o \
+		atmel_hlcdc_dpi.o \
 		atmel_hlcdc_layer.o \
 		atmel_hlcdc_output.o \
 		atmel_hlcdc_plane.o
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dpi.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dpi.c
new file mode 100644
index 0000000..b563dfc
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dpi.c
@@ -0,0 +1,212 @@ 
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dpi.h>
+
+#include "atmel_hlcdc_dc.h"
+
+enum atmel_hlcdc_output_mode {
+	ATMEL_HLCDC_OUTPUT_FMT_RGB444,
+	ATMEL_HLCDC_OUTPUT_FMT_RGB565,
+	ATMEL_HLCDC_OUTPUT_FMT_RGB666,
+	ATMEL_HLCDC_OUTPUT_FMT_RGB888,
+};
+
+struct atmel_hlcdc_dpi_host {
+	struct mipi_dpi_host base;
+	struct atmel_hlcdc_dc *dc;
+};
+
+static inline struct atmel_hlcdc_dpi_host *
+to_atmel_hlcdc_dpi_host(struct mipi_dpi_host *host)
+{
+	return container_of(host, struct atmel_hlcdc_dpi_host, base);
+}
+
+static int atmel_hlcdc_dpi_attach(struct mipi_dpi_host *host,
+				  struct mipi_dpi_device *dpi)
+{
+	return 0;
+}
+
+static int atmel_hlcdc_dpi_detach(struct mipi_dpi_host *host,
+				  struct mipi_dpi_device *dpi)
+{
+	return 0;
+}
+
+static int atmel_hlcdc_dpi_best_format_exclusive(struct mipi_dpi_host *host,
+						 enum video_bus_format *format)
+{
+	struct mipi_dpi_device *dpi;
+	bool agreed = false;
+	int i;
+
+	for (i = 0; i < host->num_supported_formats; i++) {
+		enum video_bus_format hfmt = host->supported_formats[i];
+		agreed = true;
+
+		list_for_each_entry(dpi, &host->devices, node) {
+			int j;
+
+			if (!dpi->enabled)
+				continue;
+
+			for (j = 0; j < dpi->num_supported_formats; j++) {
+				if (hfmt == dpi->supported_formats[j])
+					break;
+			}
+
+			if (j == dpi->num_supported_formats) {
+				agreed = false;
+				break;
+			}
+		}
+
+		if (agreed) {
+			*format = hfmt;
+			break;
+		}
+	}
+
+	if (!agreed)
+		return -EINVAL;
+
+	list_for_each_entry(dpi, &host->devices, node) {
+		if (!dpi->enabled)
+			continue;
+
+		dpi->next_format = *format;
+	}
+
+	return 0;
+}
+
+static int
+atmel_hlcdc_dpi_best_format_non_exclusive(struct mipi_dpi_host *host,
+					  enum video_bus_format *format)
+{
+	struct mipi_dpi_device *dpi;
+	int best_format_index = 0;
+
+	list_for_each_entry(dpi, &host->devices, node) {
+		int i, j;
+
+		if (!dpi->enabled)
+			continue;
+
+		for (i = 0; i < host->num_supported_formats; i++) {
+			enum video_bus_format hfmt = host->supported_formats[i];
+			for (j = 0; j < dpi->num_supported_formats; j++) {
+				if (hfmt == dpi->supported_formats[j])
+					break;
+			}
+
+			if (j < dpi->num_supported_formats) {
+				dpi->next_format = hfmt;
+				break;
+			}
+		}
+
+		if (i > best_format_index)
+			best_format_index = i;
+	}
+
+	*format = host->supported_formats[best_format_index];
+
+	return 0;
+}
+
+static int atmel_hlcdc_dpi_set_format(struct mipi_dpi_host *h,
+				      enum video_bus_format fmt)
+{
+	struct atmel_hlcdc_dpi_host *host = to_atmel_hlcdc_dpi_host(h);
+	unsigned int cfg;
+
+	switch (fmt) {
+	case VIDEO_BUS_FMT_RGB888_1X24:
+		cfg = ATMEL_HLCDC_OUTPUT_FMT_RGB888;
+		break;
+	case VIDEO_BUS_FMT_RGB666_1X18:
+		cfg = ATMEL_HLCDC_OUTPUT_FMT_RGB666;
+		break;
+	case VIDEO_BUS_FMT_RGB565_1X16:
+		cfg = ATMEL_HLCDC_OUTPUT_FMT_RGB565;
+		break;
+	case VIDEO_BUS_FMT_RGB444_1X12:
+		cfg = ATMEL_HLCDC_OUTPUT_FMT_RGB444;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(host->dc->hlcdc->regmap, ATMEL_HLCDC_CFG(5),
+			   ATMEL_HLCDC_MODE_MASK,
+			   cfg << 8);
+
+	return 0;
+}
+
+static const struct mipi_dpi_host_ops atmel_hlcdc_dpi_host_ops = {
+	.attach = atmel_hlcdc_dpi_attach,
+	.detach = atmel_hlcdc_dpi_detach,
+	.best_format = atmel_hlcdc_dpi_best_format_exclusive,
+	.set_format = atmel_hlcdc_dpi_set_format,
+};
+
+static const enum video_bus_format atmel_hlcdc_dpi_supported_formats[] = {
+	VIDEO_BUS_FMT_RGB888_1X24,
+	VIDEO_BUS_FMT_RGB666_1X18,
+	VIDEO_BUS_FMT_RGB565_1X16,
+	VIDEO_BUS_FMT_RGB444_1X12,
+};
+
+int atmel_hlcdc_dpi_create(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_dpi_host *dpi;
+	int ret;
+
+	dpi = devm_kzalloc(dev->dev, sizeof(*dpi), GFP_KERNEL);
+	if (!dpi)
+		return -ENOMEM;
+
+	mipi_dpi_host_init(&dpi->base);
+
+	dpi->dc = dc;
+	dpi->base.ddev = dev;
+	dpi->base.dev = dev->dev;
+	dpi->base.supported_formats = atmel_hlcdc_dpi_supported_formats;
+	dpi->base.num_supported_formats =
+			ARRAY_SIZE(atmel_hlcdc_dpi_supported_formats);
+	dpi->base.ops = &atmel_hlcdc_dpi_host_ops;
+	dpi->base.of_node = of_get_child_by_name(dev->dev->of_node, "dpi");
+	dpi->base.possible_crtcs = 0x1;
+
+	ret = mipi_dpi_host_register(&dpi->base);
+	if (ret)
+		return ret;
+
+	dc->dpi = &dpi->base;
+
+	return 0;
+}