drm/bridge: adv7533: Create a MIPI DSI device
authorArchit Taneja <architt@codeaurora.org>
Wed, 15 Jun 2016 11:31:27 +0000 (17:01 +0530)
committerArchit Taneja <architt@codeaurora.org>
Wed, 13 Jul 2016 08:54:37 +0000 (14:24 +0530)
In order to pass DSI specific parameters to the DSI host, we need the
driver to create a mipi_dsi_device DSI device that attaches to the
host.

Use of_graph helpers to get the DSI host DT node. Create a MIPI DSI
device using this host. Finally, attach this device to the DSI host.

Populate DT parameters (number of data lanes for now) that are required
for DSI RX to work correctly. Hardcode few other parameters (rgb,
embedded_sync) for now.

Select DRM_MIPI_DSI config option only when ADV7533 support is enabled.

Signed-off-by: Archit Taneja <architt@codeaurora.org>
drivers/gpu/drm/bridge/adv7511/Kconfig
drivers/gpu/drm/bridge/adv7511/adv7511.h
drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
drivers/gpu/drm/bridge/adv7511/adv7533.c

index eb844199d14801f3e29044dc28024e5638e0ee3c..d2b0499ab7d75d0e7fc47ad46234015b7f93d73b 100644 (file)
@@ -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.
index 5dea769c3c719bd297b80e3e4391ccd6bd6b58e6..3e4d47a3124a20cac99834cc720f7aa2b4b6faa0 100644 (file)
@@ -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__ */
index e33702bbd5d5be29f8ffa539b13a9daf01b3a752..6586c5220842e2df21aa4718a1b82112e69cd96a 100644 (file)
@@ -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");
index cb4ca640a2b30144ae5894cfa19c30a1d5a7ae19..ecbcaa010931b8c01d435f4804c3da37bfa6ffde 100644 (file)
@@ -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;
+}
This page took 0.031688 seconds and 5 git commands to generate.