Implement a DPI host in the HLCDC driver.

Signed-off-by: Boris Brezillon <boris.brezillon at 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 --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 at 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;
+}
-- 
1.9.1

Reply via email to