From: Jagan Teki <jagan@amarulasolutions.com>
To: Andrzej Hajda <a.hajda@samsung.com>,
Laurent Pinchart <Laurent.pinchart@ideasonboard.com>,
Chen-Yu Tsai <wens@csie.org>,
Maxime Ripard <maxime.ripard@bootlin.com>,
David Airlie <airlied@linux.ie>, Daniel Vetter <daniel@ffwll.ch>,
Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>
Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
dri-devel@lists.freedesktop.org, linux-sunxi@googlegroups.com,
Jagan Teki <jagan@amarulasolutions.com>,
Michael Trimarchi <michael@amarulasolutions.com>,
linux-amarula@amarulasolutions.com,
linux-arm-kernel@lists.infradead.org
Subject: [PATCH v2 5/6] drm/bridge: Add Chipone ICN6211 MIPI-DSI/RGB converter bridge
Date: Fri, 24 May 2019 16:13:16 +0530 [thread overview]
Message-ID: <20190524104317.20287-3-jagan@amarulasolutions.com> (raw)
In-Reply-To: <20190524104317.20287-1-jagan@amarulasolutions.com>
ICN6211 is MIPI-DSI/RGB converter bridge from chipone.
It has a flexible configuration of MIPI DSI signal input
and produce RGB565, RGB666, RGB888 output format.
Add bridge driver for it.
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Note:
- drm_panel_bridge_add seems not working or incompatible
as per driver setup. any inputs on this would be great.
MAINTAINERS | 6 +
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/chipone-icn6211.c | 344 +++++++++++++++++++++++
4 files changed, 361 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 4cc30c360fda..97ffb265bedc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4991,6 +4991,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/bochs/
+DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE
+M: Jagan Teki <jagan@amarulasolutions.com>
+S: Maintained
+F: drivers/gpu/drm/bridge/chipone-icn6211.c
+F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt
+
DRM DRIVER FOR FARADAY TVE200 TV ENCODER
M: Linus Walleij <linus.walleij@linaro.org>
T: git git://anongit.freedesktop.org/drm/drm-misc
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3dff9997f5e3..2e06be1aaca3 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -36,6 +36,16 @@ config DRM_CDNS_DSI
Support Cadence DPI to DSI bridge. This is an internal
bridge and is meant to be directly embedded in a SoC.
+config DRM_CHIPONE_ICN6211
+ tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge"
+ depends on DRM && DRM_PANEL
+ depends on OF
+ select DRM_MIPI_DSI
+ help
+ ICN6211 is MIPI-DSI/RGB converter bridge from chipone.
+ It has a flexible configuration of MIPI DSI signal input
+ and produce RGB565, RGB666, RGB888 output format.
+
config DRM_DUMB_VGA_DAC
tristate "Dumb VGA DAC Bridge support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4934fcf5a6f8..541fdccad10b 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
+obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
new file mode 100644
index 000000000000..76edda52dc57
--- /dev/null
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Amarula Solutions
+ * Author: Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_mipi_dsi.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <video/mipi_display.h>
+
+struct chipone_bridge_desc {
+ unsigned int lanes;
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ void (*chipone_bridge_init)(struct drm_bridge *bridge);
+};
+
+struct chipone {
+ struct device *dev;
+ struct drm_bridge bridge;
+ struct drm_connector connector;
+ struct drm_panel *panel;
+ const struct drm_display_mode *mode;
+ const struct chipone_bridge_desc *desc;
+
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct chipone, bridge);
+}
+
+static inline
+struct chipone *connector_to_chipone(struct drm_connector *connector)
+{
+ return container_of(connector, struct chipone, connector);
+}
+
+static int chipone_get_modes(struct drm_connector *connector)
+{
+ struct chipone *icn = connector_to_chipone(connector);
+
+ return drm_panel_get_modes(icn->panel);
+}
+
+static const
+struct drm_connector_helper_funcs chipone_connector_helper_funcs = {
+ .get_modes = chipone_get_modes,
+};
+
+static const struct drm_connector_funcs chipone_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .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 void chipone_disable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ ret = drm_panel_disable(bridge_to_chipone(bridge)->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret);
+}
+
+static void chipone_post_disable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ ret = drm_panel_unprepare(icn->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret);
+
+ msleep(50);
+
+ gpiod_set_value(icn->reset_gpio, 0);
+}
+
+static inline int chipone_dsi_write(struct chipone *icn, const void *seq,
+ size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev);
+
+ return mipi_dsi_generic_write(dsi, seq, len);
+}
+
+#define CHIPONE_DSI(icn, seq...) \
+ { \
+ const u8 d[] = { seq }; \
+ chipone_dsi_write(icn, d, ARRAY_SIZE(d)); \
+ }
+
+static void icn6211_bridge_init(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ const struct drm_display_mode *mode = icn->mode;
+
+ CHIPONE_DSI(icn, 0x7A, 0xC1);
+
+ /* lower 8 bits of hdisplay */
+ CHIPONE_DSI(icn, 0x20, mode->hdisplay & 0xff);
+
+ /* lower 8 bits of vdisplay */
+ CHIPONE_DSI(icn, 0x21, mode->vdisplay & 0xff);
+
+ /**
+ * lsb nibble: 2nd nibble of hdisplay
+ * msb nibble: 2nd nibble of vdisplay
+ */
+ CHIPONE_DSI(icn, 0x22, (((mode->hdisplay >> 8) & 0xf) |
+ (((mode->vdisplay >> 8) & 0xf) << 4)));
+
+ /* HFP */
+ CHIPONE_DSI(icn, 0x23, mode->hsync_start - mode->hdisplay);
+
+ /* HSYNC */
+ CHIPONE_DSI(icn, 0x24, mode->hsync_end - mode->hsync_start);
+
+ /* HBP */
+ CHIPONE_DSI(icn, 0x25, mode->htotal - mode->hsync_end);
+
+ CHIPONE_DSI(icn, 0x26, 0x00);
+
+ /* VFP */
+ CHIPONE_DSI(icn, 0x27, mode->vsync_start - mode->vdisplay);
+
+ /* VSYNC */
+ CHIPONE_DSI(icn, 0x28, mode->vsync_end - mode->vsync_start);
+
+ /* VBP */
+ CHIPONE_DSI(icn, 0x29, mode->vtotal - mode->vsync_end);
+
+ /* dsi specific sequence */
+ CHIPONE_DSI(icn, MIPI_DCS_SET_TEAR_OFF, 0x80);
+ CHIPONE_DSI(icn, MIPI_DCS_SET_ADDRESS_MODE, 0x28);
+ CHIPONE_DSI(icn, 0xB5, 0xA0);
+ CHIPONE_DSI(icn, 0x5C, 0xFF);
+ CHIPONE_DSI(icn, MIPI_DCS_SET_COLUMN_ADDRESS, 0x01);
+ CHIPONE_DSI(icn, MIPI_DCS_GET_POWER_SAVE, 0x92);
+ CHIPONE_DSI(icn, 0x6B, 0x71);
+ CHIPONE_DSI(icn, 0x69, 0x2B);
+ CHIPONE_DSI(icn, MIPI_DCS_ENTER_SLEEP_MODE, 0x40);
+ CHIPONE_DSI(icn, MIPI_DCS_EXIT_SLEEP_MODE, 0x98);
+
+ /* icn6211 specific sequence */
+ CHIPONE_DSI(icn, 0xB6, 0x20);
+ CHIPONE_DSI(icn, 0x51, 0x20);
+ CHIPONE_DSI(icn, 0x09, 0x10);
+}
+
+static void chipone_pre_enable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ gpiod_set_value(icn->reset_gpio, 0);
+ msleep(20);
+
+ gpiod_set_value(icn->reset_gpio, 1);
+ msleep(50);
+
+ icn->desc->chipone_bridge_init(bridge);
+
+ ret = drm_panel_prepare(icn->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret);
+}
+
+static void chipone_enable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ ret = drm_panel_enable(icn->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret);
+}
+
+static int chipone_attach(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ struct drm_device *drm = bridge->dev;
+ int ret;
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ icn->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ ret = drm_connector_init(drm, &icn->connector,
+ &chipone_connector_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ if (ret) {
+ DRM_DEV_ERROR(icn->dev,
+ "Couldn't initialise the rgb connector\n");
+ goto err_out;
+ }
+
+ drm_connector_helper_add(&icn->connector,
+ &chipone_connector_helper_funcs);
+ drm_connector_register(&icn->connector);
+ drm_connector_attach_encoder(&icn->connector, bridge->encoder);
+
+ ret = drm_panel_attach(icn->panel, &icn->connector);
+ if (ret) {
+ DRM_DEV_ERROR(icn->dev, "Couldn't attach our panel\n");
+ goto err_cleanup_connector;
+ }
+
+ return 0;
+
+err_cleanup_connector:
+ drm_encoder_cleanup(bridge->encoder);
+err_out:
+ return ret;
+}
+
+static void chipone_detach(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+
+ drm_connector_unregister(&icn->connector);
+ drm_panel_detach(icn->panel);
+ icn->panel = NULL;
+ drm_connector_put(&icn->connector);
+}
+
+static enum drm_mode_status chipone_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+
+ /* mode timings are useful during bridge init */
+ icn->mode = mode;
+
+ return MODE_OK;
+}
+
+static const struct drm_bridge_funcs chipone_bridge_funcs = {
+ .disable = chipone_disable,
+ .post_disable = chipone_post_disable,
+ .enable = chipone_enable,
+ .pre_enable = chipone_pre_enable,
+ .attach = chipone_attach,
+ .detach = chipone_detach,
+ .mode_valid = chipone_mode_valid,
+};
+
+static const struct chipone_bridge_desc icn6211_desc = {
+ .lanes = 4,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+ .format = MIPI_DSI_FMT_RGB888,
+ .chipone_bridge_init = icn6211_bridge_init,
+};
+
+static int chipone_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct chipone_bridge_desc *desc;
+ struct chipone *icn;
+ int ret;
+
+ icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL);
+ if (!icn)
+ return -ENOMEM;
+
+ desc = of_device_get_match_data(dev);
+
+ icn->dev = dev;
+ dsi->mode_flags = desc->mode_flags;
+ dsi->format = desc->format;
+ dsi->lanes = desc->lanes;
+
+ icn->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(icn->reset_gpio)) {
+ DRM_DEV_ERROR(dev, "Couldn't get our reset GPIO\n");
+ return PTR_ERR(icn->reset_gpio);
+ }
+
+ ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0,
+ &icn->panel, NULL);
+ if (ret && ret != -EPROBE_DEFER) {
+ DRM_DEV_ERROR(dev, "Couldn't find the panel (ret = %d)\n", ret);
+ return ret;
+ }
+
+ icn->bridge.funcs = &chipone_bridge_funcs;
+ icn->bridge.of_node = dev->of_node;
+
+ drm_bridge_add(&icn->bridge);
+ mipi_dsi_set_drvdata(dsi, icn);
+ icn->desc = desc;
+
+ return mipi_dsi_attach(dsi);
+}
+
+static int chipone_remove(struct mipi_dsi_device *dsi)
+{
+ struct chipone *icn = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_bridge_remove(&icn->bridge);
+
+ return 0;
+}
+
+static const struct of_device_id chipone_of_match[] = {
+ { .compatible = "chipone,icn6211", .data = &icn6211_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, chipone_of_match);
+
+static struct mipi_dsi_driver chipone_driver = {
+ .probe = chipone_probe,
+ .remove = chipone_remove,
+ .driver = {
+ .name = "chipone-icn6211",
+ .owner = THIS_MODULE,
+ .of_match_table = chipone_of_match,
+ },
+};
+module_mipi_dsi_driver(chipone_driver);
+
+MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
+MODULE_DESCRIPTION("Chipone ICN6211 MIPI-DSI to RGB Convertor Bridge");
+MODULE_LICENSE("GPL v2");
--
2.18.0.321.gffc6fa0e3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
WARNING: multiple messages have this Message-ID (diff)
From: Jagan Teki <jagan@amarulasolutions.com>
To: Andrzej Hajda <a.hajda@samsung.com>,
Laurent Pinchart <Laurent.pinchart@ideasonboard.com>,
Chen-Yu Tsai <wens@csie.org>,
Maxime Ripard <maxime.ripard@bootlin.com>,
David Airlie <airlied@linux.ie>, Daniel Vetter <daniel@ffwll.ch>,
Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-sunxi@googlegroups.com, linux-amarula@amarulasolutions.com,
Jagan Teki <jagan@amarulasolutions.com>
Subject: [PATCH v2 5/6] drm/bridge: Add Chipone ICN6211 MIPI-DSI/RGB converter bridge
Date: Fri, 24 May 2019 16:13:16 +0530 [thread overview]
Message-ID: <20190524104317.20287-3-jagan@amarulasolutions.com> (raw)
In-Reply-To: <20190524104317.20287-1-jagan@amarulasolutions.com>
ICN6211 is MIPI-DSI/RGB converter bridge from chipone.
It has a flexible configuration of MIPI DSI signal input
and produce RGB565, RGB666, RGB888 output format.
Add bridge driver for it.
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Note:
- drm_panel_bridge_add seems not working or incompatible
as per driver setup. any inputs on this would be great.
MAINTAINERS | 6 +
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/chipone-icn6211.c | 344 +++++++++++++++++++++++
4 files changed, 361 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 4cc30c360fda..97ffb265bedc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4991,6 +4991,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/bochs/
+DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE
+M: Jagan Teki <jagan@amarulasolutions.com>
+S: Maintained
+F: drivers/gpu/drm/bridge/chipone-icn6211.c
+F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt
+
DRM DRIVER FOR FARADAY TVE200 TV ENCODER
M: Linus Walleij <linus.walleij@linaro.org>
T: git git://anongit.freedesktop.org/drm/drm-misc
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3dff9997f5e3..2e06be1aaca3 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -36,6 +36,16 @@ config DRM_CDNS_DSI
Support Cadence DPI to DSI bridge. This is an internal
bridge and is meant to be directly embedded in a SoC.
+config DRM_CHIPONE_ICN6211
+ tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge"
+ depends on DRM && DRM_PANEL
+ depends on OF
+ select DRM_MIPI_DSI
+ help
+ ICN6211 is MIPI-DSI/RGB converter bridge from chipone.
+ It has a flexible configuration of MIPI DSI signal input
+ and produce RGB565, RGB666, RGB888 output format.
+
config DRM_DUMB_VGA_DAC
tristate "Dumb VGA DAC Bridge support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4934fcf5a6f8..541fdccad10b 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
+obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
new file mode 100644
index 000000000000..76edda52dc57
--- /dev/null
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Amarula Solutions
+ * Author: Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_mipi_dsi.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <video/mipi_display.h>
+
+struct chipone_bridge_desc {
+ unsigned int lanes;
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ void (*chipone_bridge_init)(struct drm_bridge *bridge);
+};
+
+struct chipone {
+ struct device *dev;
+ struct drm_bridge bridge;
+ struct drm_connector connector;
+ struct drm_panel *panel;
+ const struct drm_display_mode *mode;
+ const struct chipone_bridge_desc *desc;
+
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct chipone, bridge);
+}
+
+static inline
+struct chipone *connector_to_chipone(struct drm_connector *connector)
+{
+ return container_of(connector, struct chipone, connector);
+}
+
+static int chipone_get_modes(struct drm_connector *connector)
+{
+ struct chipone *icn = connector_to_chipone(connector);
+
+ return drm_panel_get_modes(icn->panel);
+}
+
+static const
+struct drm_connector_helper_funcs chipone_connector_helper_funcs = {
+ .get_modes = chipone_get_modes,
+};
+
+static const struct drm_connector_funcs chipone_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .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 void chipone_disable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ ret = drm_panel_disable(bridge_to_chipone(bridge)->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret);
+}
+
+static void chipone_post_disable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ ret = drm_panel_unprepare(icn->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret);
+
+ msleep(50);
+
+ gpiod_set_value(icn->reset_gpio, 0);
+}
+
+static inline int chipone_dsi_write(struct chipone *icn, const void *seq,
+ size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev);
+
+ return mipi_dsi_generic_write(dsi, seq, len);
+}
+
+#define CHIPONE_DSI(icn, seq...) \
+ { \
+ const u8 d[] = { seq }; \
+ chipone_dsi_write(icn, d, ARRAY_SIZE(d)); \
+ }
+
+static void icn6211_bridge_init(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ const struct drm_display_mode *mode = icn->mode;
+
+ CHIPONE_DSI(icn, 0x7A, 0xC1);
+
+ /* lower 8 bits of hdisplay */
+ CHIPONE_DSI(icn, 0x20, mode->hdisplay & 0xff);
+
+ /* lower 8 bits of vdisplay */
+ CHIPONE_DSI(icn, 0x21, mode->vdisplay & 0xff);
+
+ /**
+ * lsb nibble: 2nd nibble of hdisplay
+ * msb nibble: 2nd nibble of vdisplay
+ */
+ CHIPONE_DSI(icn, 0x22, (((mode->hdisplay >> 8) & 0xf) |
+ (((mode->vdisplay >> 8) & 0xf) << 4)));
+
+ /* HFP */
+ CHIPONE_DSI(icn, 0x23, mode->hsync_start - mode->hdisplay);
+
+ /* HSYNC */
+ CHIPONE_DSI(icn, 0x24, mode->hsync_end - mode->hsync_start);
+
+ /* HBP */
+ CHIPONE_DSI(icn, 0x25, mode->htotal - mode->hsync_end);
+
+ CHIPONE_DSI(icn, 0x26, 0x00);
+
+ /* VFP */
+ CHIPONE_DSI(icn, 0x27, mode->vsync_start - mode->vdisplay);
+
+ /* VSYNC */
+ CHIPONE_DSI(icn, 0x28, mode->vsync_end - mode->vsync_start);
+
+ /* VBP */
+ CHIPONE_DSI(icn, 0x29, mode->vtotal - mode->vsync_end);
+
+ /* dsi specific sequence */
+ CHIPONE_DSI(icn, MIPI_DCS_SET_TEAR_OFF, 0x80);
+ CHIPONE_DSI(icn, MIPI_DCS_SET_ADDRESS_MODE, 0x28);
+ CHIPONE_DSI(icn, 0xB5, 0xA0);
+ CHIPONE_DSI(icn, 0x5C, 0xFF);
+ CHIPONE_DSI(icn, MIPI_DCS_SET_COLUMN_ADDRESS, 0x01);
+ CHIPONE_DSI(icn, MIPI_DCS_GET_POWER_SAVE, 0x92);
+ CHIPONE_DSI(icn, 0x6B, 0x71);
+ CHIPONE_DSI(icn, 0x69, 0x2B);
+ CHIPONE_DSI(icn, MIPI_DCS_ENTER_SLEEP_MODE, 0x40);
+ CHIPONE_DSI(icn, MIPI_DCS_EXIT_SLEEP_MODE, 0x98);
+
+ /* icn6211 specific sequence */
+ CHIPONE_DSI(icn, 0xB6, 0x20);
+ CHIPONE_DSI(icn, 0x51, 0x20);
+ CHIPONE_DSI(icn, 0x09, 0x10);
+}
+
+static void chipone_pre_enable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ gpiod_set_value(icn->reset_gpio, 0);
+ msleep(20);
+
+ gpiod_set_value(icn->reset_gpio, 1);
+ msleep(50);
+
+ icn->desc->chipone_bridge_init(bridge);
+
+ ret = drm_panel_prepare(icn->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret);
+}
+
+static void chipone_enable(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ int ret;
+
+ ret = drm_panel_enable(icn->panel);
+ if (ret < 0)
+ DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret);
+}
+
+static int chipone_attach(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+ struct drm_device *drm = bridge->dev;
+ int ret;
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ icn->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ ret = drm_connector_init(drm, &icn->connector,
+ &chipone_connector_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ if (ret) {
+ DRM_DEV_ERROR(icn->dev,
+ "Couldn't initialise the rgb connector\n");
+ goto err_out;
+ }
+
+ drm_connector_helper_add(&icn->connector,
+ &chipone_connector_helper_funcs);
+ drm_connector_register(&icn->connector);
+ drm_connector_attach_encoder(&icn->connector, bridge->encoder);
+
+ ret = drm_panel_attach(icn->panel, &icn->connector);
+ if (ret) {
+ DRM_DEV_ERROR(icn->dev, "Couldn't attach our panel\n");
+ goto err_cleanup_connector;
+ }
+
+ return 0;
+
+err_cleanup_connector:
+ drm_encoder_cleanup(bridge->encoder);
+err_out:
+ return ret;
+}
+
+static void chipone_detach(struct drm_bridge *bridge)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+
+ drm_connector_unregister(&icn->connector);
+ drm_panel_detach(icn->panel);
+ icn->panel = NULL;
+ drm_connector_put(&icn->connector);
+}
+
+static enum drm_mode_status chipone_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
+{
+ struct chipone *icn = bridge_to_chipone(bridge);
+
+ /* mode timings are useful during bridge init */
+ icn->mode = mode;
+
+ return MODE_OK;
+}
+
+static const struct drm_bridge_funcs chipone_bridge_funcs = {
+ .disable = chipone_disable,
+ .post_disable = chipone_post_disable,
+ .enable = chipone_enable,
+ .pre_enable = chipone_pre_enable,
+ .attach = chipone_attach,
+ .detach = chipone_detach,
+ .mode_valid = chipone_mode_valid,
+};
+
+static const struct chipone_bridge_desc icn6211_desc = {
+ .lanes = 4,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+ .format = MIPI_DSI_FMT_RGB888,
+ .chipone_bridge_init = icn6211_bridge_init,
+};
+
+static int chipone_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct chipone_bridge_desc *desc;
+ struct chipone *icn;
+ int ret;
+
+ icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL);
+ if (!icn)
+ return -ENOMEM;
+
+ desc = of_device_get_match_data(dev);
+
+ icn->dev = dev;
+ dsi->mode_flags = desc->mode_flags;
+ dsi->format = desc->format;
+ dsi->lanes = desc->lanes;
+
+ icn->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(icn->reset_gpio)) {
+ DRM_DEV_ERROR(dev, "Couldn't get our reset GPIO\n");
+ return PTR_ERR(icn->reset_gpio);
+ }
+
+ ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0,
+ &icn->panel, NULL);
+ if (ret && ret != -EPROBE_DEFER) {
+ DRM_DEV_ERROR(dev, "Couldn't find the panel (ret = %d)\n", ret);
+ return ret;
+ }
+
+ icn->bridge.funcs = &chipone_bridge_funcs;
+ icn->bridge.of_node = dev->of_node;
+
+ drm_bridge_add(&icn->bridge);
+ mipi_dsi_set_drvdata(dsi, icn);
+ icn->desc = desc;
+
+ return mipi_dsi_attach(dsi);
+}
+
+static int chipone_remove(struct mipi_dsi_device *dsi)
+{
+ struct chipone *icn = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_bridge_remove(&icn->bridge);
+
+ return 0;
+}
+
+static const struct of_device_id chipone_of_match[] = {
+ { .compatible = "chipone,icn6211", .data = &icn6211_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, chipone_of_match);
+
+static struct mipi_dsi_driver chipone_driver = {
+ .probe = chipone_probe,
+ .remove = chipone_remove,
+ .driver = {
+ .name = "chipone-icn6211",
+ .owner = THIS_MODULE,
+ .of_match_table = chipone_of_match,
+ },
+};
+module_mipi_dsi_driver(chipone_driver);
+
+MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
+MODULE_DESCRIPTION("Chipone ICN6211 MIPI-DSI to RGB Convertor Bridge");
+MODULE_LICENSE("GPL v2");
--
2.18.0.321.gffc6fa0e3
next prev parent reply other threads:[~2019-05-24 10:44 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-24 10:43 [PATCH v2 3/6] drm/sun4i: dsi: Add bridge support Jagan Teki
2019-05-24 10:43 ` Jagan Teki
2019-05-24 10:43 ` [PATCH v2 4/6] dt-bindings: display: bridge: Add ICN6211 MIPI-DSI to RGB converter bridge Jagan Teki
2019-05-24 10:43 ` Jagan Teki
2019-06-14 17:40 ` Rob Herring
2019-06-14 17:40 ` Rob Herring
2019-06-14 17:40 ` Rob Herring
2019-05-24 10:43 ` Jagan Teki [this message]
2019-05-24 10:43 ` [PATCH v2 5/6] drm/bridge: Add Chipone ICN6211 MIPI-DSI/RGB " Jagan Teki
2019-05-24 10:43 ` [DO NOT MERGE] [PATCH v2 6/6] ARM: dts: sun8i: bananapi-m2m: Enable Bananapi S070WV20-CT16 DSI panel Jagan Teki
2019-05-24 10:43 ` Jagan Teki
2019-05-24 11:19 ` [PATCH v2 3/6] drm/sun4i: dsi: Add bridge support Maxime Ripard
2019-05-24 11:19 ` Maxime Ripard
2019-05-24 11:19 ` Maxime Ripard
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190524104317.20287-3-jagan@amarulasolutions.com \
--to=jagan@amarulasolutions.com \
--cc=Laurent.pinchart@ideasonboard.com \
--cc=a.hajda@samsung.com \
--cc=airlied@linux.ie \
--cc=daniel@ffwll.ch \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=linux-amarula@amarulasolutions.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sunxi@googlegroups.com \
--cc=mark.rutland@arm.com \
--cc=maxime.ripard@bootlin.com \
--cc=michael@amarulasolutions.com \
--cc=robh+dt@kernel.org \
--cc=wens@csie.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.