#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");