#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/of_graph.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder_slave.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_mipi_dsi.h>
#include "adv7511.h"
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;
};
return;
if (adv7511->powered) {
- /* set number of dsi lanes (hardcoded to 4 for now) */
- regmap_write(adv7511->regmap_cec, 0x1c, 0x4 << 4);
+ struct mipi_dsi_device *dsi = adv7511->dsi;
+
+ /* set number of dsi lanes */
+ regmap_write(adv7511->regmap_cec, 0x1c, dsi->lanes << 4);
/* disable internal timing generator */
regmap_write(adv7511->regmap_cec, 0x27, 0x0b);
/* enable hdmi */
adv7511_mode_set(adv, mode, adj_mode);
}
+static int adv7533_attach_dsi(struct adv7511 *adv7511)
+{
+ struct device *dev = &adv7511->i2c_main->dev;
+ struct mipi_dsi_device *dsi;
+ struct mipi_dsi_host *host;
+ int ret;
+
+ host = of_find_mipi_dsi_host_by_node(adv7511->host_node);
+ if (!host) {
+ dev_err(dev, "failed to find dsi host\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* can adv7533 virtual channel be non-zero? */
+ dsi = mipi_dsi_new_dummy(host, 0);
+ if (IS_ERR(dsi)) {
+ dev_err(dev, "failed to create dummy dsi device\n");
+ ret = PTR_ERR(dsi);
+ goto err_dsi_device;
+ }
+
+ adv7511->dsi = dsi;
+
+ dsi->lanes = adv7511->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_unregister_device(dsi);
+err_dsi_device:
+ return ret;
+}
+
static int adv7533_bridge_attach(struct drm_bridge *bridge)
{
struct adv7511 *adv = bridge_to_adv7511(bridge);
drm_helper_hpd_irq_event(adv->connector.dev);
+ adv7533_attach_dsi(adv);
+
return ret;
}
return 0;
}
+static int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv7511)
+{
+ 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;
+
+ adv7511->num_dsi_lanes = num_lanes;
+
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint) {
+ DRM_ERROR("adv dsi input endpoint not found\n");
+ return -ENODEV;
+ }
+
+ adv7511->host_node = of_graph_get_remote_port_parent(endpoint);
+ if (!adv7511->host_node) {
+ DRM_ERROR("dsi host node not found\n");
+ of_node_put(endpoint);
+ return -ENODEV;
+ }
+
+ of_node_put(endpoint);
+ of_node_put(adv7511->host_node);
+
+ /* TODO: Check if these need to be parsed by DT or not */
+ adv7511->rgb = true;
+ adv7511->embedded_sync = false;
+
+ return 0;
+}
+
static const int edid_i2c_addr = 0x7e;
static const int packet_i2c_addr = 0x70;
static const int cec_i2c_addr = 0x78;
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
kfree(adv7511->edid);
- if (adv7511->type == ADV7533)
+ if (adv7511->type == ADV7533) {
+ mipi_dsi_detach(adv7511->dsi);
+ mipi_dsi_unregister_device(adv7511->dsi);
drm_bridge_remove(&adv7511->bridge);
+ }
return 0;
}