summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/bridge/adv7511/Kconfig1
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511.h23
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c40
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7533.c91
4 files changed, 147 insertions, 8 deletions
diff --git a/drivers/gpu/drm/bridge/adv7511/Kconfig b/drivers/gpu/drm/bridge/adv7511/Kconfig
index eb844199d148..d2b0499ab7d7 100644
--- a/drivers/gpu/drm/bridge/adv7511/Kconfig
+++ b/drivers/gpu/drm/bridge/adv7511/Kconfig
@@ -9,6 +9,7 @@ config DRM_I2C_ADV7511
config DRM_I2C_ADV7533
bool "ADV7533 encoder"
depends on DRM_I2C_ADV7511
+ select DRM_MIPI_DSI
default y
help
Support for the Analog Devices ADV7533 DSI to HDMI encoder.
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index 5dea769c3c71..3e4d47a3124a 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -14,6 +14,7 @@
#include <linux/regmap.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
#define ADV7511_REG_CHIP_REVISION 0x00
#define ADV7511_REG_N0 0x01
@@ -324,6 +325,11 @@ struct adv7511 {
struct gpio_desc *gpio_pd;
+ /* ADV7533 DSI RX related params */
+ struct device_node *host_node;
+ struct mipi_dsi_device *dsi;
+ u8 num_dsi_lanes;
+
enum adv7511_type type;
};
@@ -333,6 +339,9 @@ void adv7533_dsi_power_off(struct adv7511 *adv);
int adv7533_patch_registers(struct adv7511 *adv);
void adv7533_uninit_cec(struct adv7511 *adv);
int adv7533_init_cec(struct adv7511 *adv);
+int adv7533_attach_dsi(struct adv7511 *adv);
+void adv7533_detach_dsi(struct adv7511 *adv);
+int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
#else
static inline void adv7533_dsi_power_on(struct adv7511 *adv)
{
@@ -355,6 +364,20 @@ static inline int adv7533_init_cec(struct adv7511 *adv)
{
return -ENODEV;
}
+
+static inline int adv7533_attach_dsi(struct adv7511 *adv)
+{
+ return -ENODEV;
+}
+
+static inline void adv7533_detach_dsi(struct adv7511 *adv)
+{
+}
+
+static inline int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
+{
+ return -ENODEV;
+}
#endif
#endif /* __DRM_I2C_ADV7511_H__ */
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index e33702bbd5d5..6586c5220842 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -817,6 +817,9 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge)
&adv7511_connector_helper_funcs);
drm_mode_connector_attach_encoder(&adv->connector, bridge->encoder);
+ if (adv->type == ADV7533)
+ ret = adv7533_attach_dsi(adv);
+
return ret;
}
@@ -943,11 +946,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
memset(&link_config, 0, sizeof(link_config));
- if (adv7511->type == ADV7511) {
+ if (adv7511->type == ADV7511)
ret = adv7511_parse_dt(dev->of_node, &link_config);
- if (ret)
- return ret;
- }
+ else
+ ret = adv7533_parse_dt(dev->of_node, adv7511);
+ if (ret)
+ return ret;
/*
* The power down GPIO is optional. If present, toggle it from active to
@@ -1042,9 +1046,13 @@ static int adv7511_remove(struct i2c_client *i2c)
{
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+ if (adv7511->type == ADV7533) {
+ adv7533_detach_dsi(adv7511);
+ adv7533_uninit_cec(adv7511);
+ }
+
drm_bridge_remove(&adv7511->bridge);
- adv7533_uninit_cec(adv7511);
i2c_unregister_device(adv7511->i2c_edid);
kfree(adv7511->edid);
@@ -1074,6 +1082,10 @@ static const struct of_device_id adv7511_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, adv7511_of_ids);
+static struct mipi_dsi_driver adv7533_dsi_driver = {
+ .driver.name = "adv7533",
+};
+
static struct i2c_driver adv7511_driver = {
.driver = {
.name = "adv7511",
@@ -1084,7 +1096,23 @@ static struct i2c_driver adv7511_driver = {
.remove = adv7511_remove,
};
-module_i2c_driver(adv7511_driver);
+static int __init adv7511_init(void)
+{
+ if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+ mipi_dsi_driver_register(&adv7533_dsi_driver);
+
+ return i2c_add_driver(&adv7511_driver);
+}
+module_init(adv7511_init);
+
+static void __exit adv7511_exit(void)
+{
+ i2c_del_driver(&adv7511_driver);
+
+ if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+ mipi_dsi_driver_unregister(&adv7533_dsi_driver);
+}
+module_exit(adv7511_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver");
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c
index cb4ca640a2b3..ecbcaa010931 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7533.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#include <linux/of_graph.h>
+
#include "adv7511.h"
static const struct reg_sequence adv7533_fixed_registers[] = {
@@ -39,8 +41,10 @@ static const struct regmap_config adv7533_cec_regmap_config = {
void adv7533_dsi_power_on(struct adv7511 *adv)
{
- /* set number of dsi lanes (hardcoded to 4 for now) */
- regmap_write(adv->regmap_cec, 0x1c, 4 << 4);
+ struct mipi_dsi_device *dsi = adv->dsi;
+
+ /* set number of dsi lanes */
+ regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
/* disable internal timing generator */
regmap_write(adv->regmap_cec, 0x27, 0x0b);
/* enable hdmi */
@@ -98,3 +102,86 @@ err:
adv7533_uninit_cec(adv);
return ret;
}
+
+int adv7533_attach_dsi(struct adv7511 *adv)
+{
+ struct device *dev = &adv->i2c_main->dev;
+ struct mipi_dsi_host *host;
+ struct mipi_dsi_device *dsi;
+ int ret = 0;
+ const struct mipi_dsi_device_info info = { .type = "adv7533",
+ .channel = 0,
+ .node = NULL,
+ };
+
+ host = of_find_mipi_dsi_host_by_node(adv->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)) {
+ dev_err(dev, "failed to create dsi device\n");
+ ret = PTR_ERR(dsi);
+ goto err_dsi_device;
+ }
+
+ adv->dsi = dsi;
+
+ dsi->lanes = adv->num_dsi_lanes;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "failed to attach dsi to host\n");
+ goto err_dsi_attach;
+ }
+
+ return 0;
+
+err_dsi_attach:
+ mipi_dsi_device_unregister(dsi);
+err_dsi_device:
+ return ret;
+}
+
+void adv7533_detach_dsi(struct adv7511 *adv)
+{
+ mipi_dsi_detach(adv->dsi);
+ mipi_dsi_device_unregister(adv->dsi);
+}
+
+int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
+{
+ u32 num_lanes;
+ struct device_node *endpoint;
+
+ of_property_read_u32(np, "adi,dsi-lanes", &num_lanes);
+
+ if (num_lanes < 1 || num_lanes > 4)
+ return -EINVAL;
+
+ adv->num_dsi_lanes = num_lanes;
+
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint)
+ return -ENODEV;
+
+ adv->host_node = of_graph_get_remote_port_parent(endpoint);
+ if (!adv->host_node) {
+ of_node_put(endpoint);
+ return -ENODEV;
+ }
+
+ of_node_put(endpoint);
+ of_node_put(adv->host_node);
+
+ /* TODO: Check if these need to be parsed by DT or not */
+ adv->rgb = true;
+ adv->embedded_sync = false;
+
+ return 0;
+}
OpenPOWER on IntegriCloud