Skip to content

Commit

Permalink
drm: bridge: icn6211: Add I2C configuration support
Browse files Browse the repository at this point in the history
The ICN6211 chip starts in I2C configuration mode after cold boot.
Implement support for configuring the chip via I2C in addition to
the current DSI LP command mode configuration support. The later
seems to be available only on chips which have additional MCU on
the panel/bridge board which preconfigures the ICN6211, while the
I2C configuration mode added by this patch does not require any
such MCU.

Acked-by: Maxime Ripard <maxime@cerno.tech>
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Robert Foss <robert.foss@linaro.org>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
To: dri-devel@lists.freedesktop.org
Signed-off-by: Robert Foss <robert.foss@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220331150509.9838-10-marex@denx.de
  • Loading branch information
Marek Vasut authored and robertfoss committed Mar 31, 2022
1 parent cda3822 commit 8dde6f7
Showing 1 changed file with 156 additions and 22 deletions.
178 changes: 156 additions & 22 deletions drivers/gpu/drm/bridge/chipone-icn6211.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#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>
Expand Down Expand Up @@ -133,6 +134,7 @@

struct chipone {
struct device *dev;
struct i2c_client *client;
struct drm_bridge bridge;
struct drm_display_mode mode;
struct drm_bridge *panel_bridge;
Expand All @@ -141,19 +143,23 @@ struct chipone {
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...) \
Expand Down Expand Up @@ -258,7 +264,10 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,
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);

Expand Down Expand Up @@ -374,6 +383,67 @@ static void chipone_mode_set(struct drm_bridge *bridge,
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)
Expand Down Expand Up @@ -466,17 +536,15 @@ static int chipone_parse_dt(struct chipone *icn)
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;

icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL);
if (!icn)
return -ENOMEM;

mipi_dsi_set_drvdata(dsi, icn);
icn->dev = dev;

ret = chipone_parse_dt(icn);
Expand All @@ -486,25 +554,58 @@ static int chipone_probe(struct mipi_dsi_device *dsi)
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);

Expand All @@ -520,16 +621,49 @@ static const struct of_device_id chipone_of_match[] = {
};
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");
Expand Down

0 comments on commit 8dde6f7

Please sign in to comment.