diff options
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511.h | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7533.c | 91 |
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; +} |