@@ -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
@@ -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
new file mode 100644
@@ -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;
+}
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