#include <linux/delay.h>
 #include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/regulator/consumer.h>
 
 struct chipone {
        struct device *dev;
+       struct i2c_client *client;
        struct drm_bridge bridge;
        struct drm_display_mode mode;
        struct drm_bridge *panel_bridge;
        struct regulator *vdd1;
        struct regulator *vdd2;
        struct regulator *vdd3;
+       bool interface_i2c;
 };
 
 static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
        return container_of(bridge, struct chipone, bridge);
 }
 
-static inline int chipone_dsi_write(struct chipone *icn,  const void *seq,
+static inline int chipone_dsi_write(struct chipone *icn, const u8 *seq,
                                    size_t len)
 {
-       struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev);
-
-       return mipi_dsi_generic_write(dsi, seq, len);
+       if (icn->interface_i2c) {
+               return i2c_smbus_write_byte_data(icn->client, seq[0], seq[1]);
+       } else {
+               return mipi_dsi_generic_write(icn->dsi,
+                                             (u8[]){seq[0], seq[1]}, 2);
+       }
 }
 
 #define ICN6211_DSI(icn, seq...)                               \
        bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
        bus_flags = bridge_state->output_bus_cfg.flags;
 
-       ICN6211_DSI(icn, MIPI_CFG_PW, MIPI_CFG_PW_CONFIG_DSI);
+       if (icn->interface_i2c)
+               ICN6211_DSI(icn, MIPI_CFG_PW, MIPI_CFG_PW_CONFIG_I2C)
+       else
+               ICN6211_DSI(icn, MIPI_CFG_PW, MIPI_CFG_PW_CONFIG_DSI)
 
        ICN6211_DSI(icn, HACTIVE_LI, mode->hdisplay & 0xff);
 
        struct chipone *icn = bridge_to_chipone(bridge);
 
        drm_mode_copy(&icn->mode, adjusted_mode);
+};
+
+static int chipone_dsi_attach(struct chipone *icn)
+{
+       struct mipi_dsi_device *dsi = icn->dsi;
+       int ret;
+
+       dsi->lanes = 4;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+                         MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0)
+               dev_err(icn->dev, "failed to attach dsi\n");
+
+       return ret;
+}
+
+static int chipone_dsi_host_attach(struct chipone *icn)
+{
+       struct device *dev = icn->dev;
+       struct device_node *host_node;
+       struct device_node *endpoint;
+       struct mipi_dsi_device *dsi;
+       struct mipi_dsi_host *host;
+       int ret = 0;
+
+       const struct mipi_dsi_device_info info = {
+               .type = "chipone",
+               .channel = 0,
+               .node = NULL,
+       };
+
+       endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
+       host_node = of_graph_get_remote_port_parent(endpoint);
+       of_node_put(endpoint);
+
+       if (!host_node)
+               return -EINVAL;
+
+       host = of_find_mipi_dsi_host_by_node(host_node);
+       of_node_put(host_node);
+       if (!host) {
+               dev_err(dev, "failed to find dsi host\n");
+               return -EPROBE_DEFER;
+       }
+
+       dsi = mipi_dsi_device_register_full(host, &info);
+       if (IS_ERR(dsi)) {
+               return dev_err_probe(dev, PTR_ERR(dsi),
+                                    "failed to create dsi device\n");
+       }
+
+       icn->dsi = dsi;
+
+       ret = chipone_dsi_attach(icn);
+       if (ret < 0)
+               mipi_dsi_device_unregister(dsi);
+
+       return ret;
 }
 
 static int chipone_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
        return 0;
 }
 
-static int chipone_probe(struct mipi_dsi_device *dsi)
+static int chipone_common_probe(struct device *dev, struct chipone **icnr)
 {
-       struct device *dev = &dsi->dev;
        struct chipone *icn;
        int ret;
 
        if (!icn)
                return -ENOMEM;
 
-       mipi_dsi_set_drvdata(dsi, icn);
        icn->dev = dev;
 
        ret = chipone_parse_dt(icn);
        icn->bridge.funcs = &chipone_bridge_funcs;
        icn->bridge.type = DRM_MODE_CONNECTOR_DPI;
        icn->bridge.of_node = dev->of_node;
+
+       *icnr = icn;
+
+       return ret;
+}
+
+static int chipone_dsi_probe(struct mipi_dsi_device *dsi)
+{
+       struct device *dev = &dsi->dev;
+       struct chipone *icn;
+       int ret;
+
+       ret = chipone_common_probe(dev, &icn);
+       if (ret)
+               return ret;
+
+       icn->interface_i2c = false;
        icn->dsi = dsi;
 
-       drm_bridge_add(&icn->bridge);
+       mipi_dsi_set_drvdata(dsi, icn);
 
-       dsi->lanes = 4;
-       dsi->format = MIPI_DSI_FMT_RGB888;
-       dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
-                         MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
+       drm_bridge_add(&icn->bridge);
 
-       ret = mipi_dsi_attach(dsi);
-       if (ret < 0) {
+       ret = chipone_dsi_attach(icn);
+       if (ret)
                drm_bridge_remove(&icn->bridge);
-               dev_err(dev, "failed to attach dsi\n");
-       }
 
        return ret;
 }
 
-static int chipone_remove(struct mipi_dsi_device *dsi)
+static int chipone_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct chipone *icn;
+       int ret;
+
+       ret = chipone_common_probe(dev, &icn);
+       if (ret)
+               return ret;
+
+       icn->interface_i2c = true;
+       icn->client = client;
+       dev_set_drvdata(dev, icn);
+       i2c_set_clientdata(client, icn);
+
+       drm_bridge_add(&icn->bridge);
+
+       return chipone_dsi_host_attach(icn);
+}
+
+static int chipone_dsi_remove(struct mipi_dsi_device *dsi)
 {
        struct chipone *icn = mipi_dsi_get_drvdata(dsi);
 
 };
 MODULE_DEVICE_TABLE(of, chipone_of_match);
 
-static struct mipi_dsi_driver chipone_driver = {
-       .probe = chipone_probe,
-       .remove = chipone_remove,
+static struct mipi_dsi_driver chipone_dsi_driver = {
+       .probe = chipone_dsi_probe,
+       .remove = chipone_dsi_remove,
        .driver = {
                .name = "chipone-icn6211",
                .owner = THIS_MODULE,
                .of_match_table = chipone_of_match,
        },
 };
-module_mipi_dsi_driver(chipone_driver);
+
+static struct i2c_device_id chipone_i2c_id[] = {
+       { "chipone,icn6211" },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, chipone_i2c_id);
+
+static struct i2c_driver chipone_i2c_driver = {
+       .probe = chipone_i2c_probe,
+       .id_table = chipone_i2c_id,
+       .driver = {
+               .name = "chipone-icn6211-i2c",
+               .owner = THIS_MODULE,
+               .of_match_table = chipone_of_match,
+       },
+};
+
+static int __init chipone_init(void)
+{
+       if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+               mipi_dsi_driver_register(&chipone_dsi_driver);
+
+       return i2c_add_driver(&chipone_i2c_driver);
+}
+module_init(chipone_init);
+
+static void __init chipone_exit(void)
+{
+       i2c_del_driver(&chipone_i2c_driver);
+
+       if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+               mipi_dsi_driver_unregister(&chipone_dsi_driver);
+}
+module_exit(chipone_exit);
 
 MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
 MODULE_DESCRIPTION("Chipone ICN6211 MIPI-DSI to RGB Converter Bridge");