summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i2c/adv7511.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i2c/adv7511.c')
-rw-r--r--drivers/gpu/drm/i2c/adv7511.c707
1 files changed, 607 insertions, 100 deletions
diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c
index 2aaa3c88999e..895d55c0c2c4 100644
--- a/drivers/gpu/drm/i2c/adv7511.c
+++ b/drivers/gpu/drm/i2c/adv7511.c
@@ -17,41 +17,13 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.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 <linux/of_irq.h>
#include "adv7511.h"
-struct adv7511 {
- struct i2c_client *i2c_main;
- struct i2c_client *i2c_edid;
-
- struct regmap *regmap;
- struct regmap *packet_memory_regmap;
- enum drm_connector_status status;
- bool powered;
-
- unsigned int f_tmds;
-
- unsigned int current_edid_segment;
- uint8_t edid_buf[256];
- bool edid_read;
-
- wait_queue_head_t wq;
- struct drm_encoder *encoder;
-
- bool embedded_sync;
- enum adv7511_sync_polarity vsync_polarity;
- enum adv7511_sync_polarity hsync_polarity;
- bool rgb;
-
- struct edid *edid;
-
- struct gpio_desc *gpio_pd;
-};
-
-static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder)
-{
- return to_encoder_slave(encoder)->slave_priv;
-}
/* ADI recommended values for proper operation. */
static const struct reg_default adv7511_fixed_registers[] = {
@@ -66,6 +38,24 @@ static const struct reg_default adv7511_fixed_registers[] = {
{ 0x55, 0x02 },
};
+/* ADI recommended values for proper operation. */
+static const struct reg_default adv7533_fixed_registers[] = {
+ { 0x16, 0x20 },
+ { 0x9a, 0xe0 },
+ { 0xba, 0x70 },
+ { 0xde, 0x82 },
+ { 0xe4, 0x40 },
+ { 0xe5, 0x80 },
+};
+
+static const struct reg_default adv7533_cec_fixed_registers[] = {
+ { 0x15, 0xd0 },
+ { 0x17, 0xd0 },
+ { 0x24, 0x20 },
+ { 0x57, 0x11 },
+ { 0x05, 0xc8 },
+};
+
/* -----------------------------------------------------------------------------
* Register access
*/
@@ -158,6 +148,15 @@ static const struct regmap_config adv7511_regmap_config = {
.volatile_reg = adv7511_register_volatile,
};
+static const struct regmap_config adv7533_cec_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
/* -----------------------------------------------------------------------------
* Hardware configuration
*/
@@ -193,7 +192,7 @@ static void adv7511_set_colormap(struct adv7511 *adv7511, bool enable,
ADV7511_CSC_UPDATE_MODE, 0);
}
-static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet)
+int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet)
{
if (packet & 0xff)
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0,
@@ -208,7 +207,7 @@ static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet)
return 0;
}
-static int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet)
+int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet)
{
if (packet & 0xff)
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0,
@@ -325,6 +324,17 @@ static void adv7511_set_link_config(struct adv7511 *adv7511,
unsigned int color_depth;
unsigned int input_id;
+ adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
+
+ /*
+ * TODO: some of the below configurations might be needed for ADV7533
+ * too, check in ADV7533 spec.
+ */
+ if (adv7511->type == ADV7533) {
+ adv7511->num_dsi_lanes = config->num_dsi_lanes;
+ return;
+ }
+
clock_delay = (config->clock_delay + 1200) / 400;
color_depth = config->input_color_depth == 8 ? 3
: (config->input_color_depth == 10 ? 1 : 2);
@@ -355,17 +365,75 @@ static void adv7511_set_link_config(struct adv7511 *adv7511,
adv7511->embedded_sync = config->embedded_sync;
adv7511->hsync_polarity = config->hsync_polarity;
adv7511->vsync_polarity = config->vsync_polarity;
- adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
+}
+
+static void adv7511_dsi_config_tgen(struct adv7511 *adv7511)
+{
+ struct drm_display_mode *mode = adv7511->curr_mode;
+ unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
+
+ hsw = mode->hsync_end - mode->hsync_start;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hbp = mode->htotal - mode->hsync_end;
+ vsw = mode->vsync_end - mode->vsync_start;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+
+ /* set pixel clock divider mode to auto */
+ regmap_write(adv7511->regmap_cec, 0x16, 0x00);
+
+ /* horizontal porch params */
+ regmap_write(adv7511->regmap_cec, 0x28, mode->htotal >> 4);
+ regmap_write(adv7511->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
+ regmap_write(adv7511->regmap_cec, 0x2a, hsw >> 4);
+ regmap_write(adv7511->regmap_cec, 0x2b, (hsw << 4) & 0xff);
+ regmap_write(adv7511->regmap_cec, 0x2c, hfp >> 4);
+ regmap_write(adv7511->regmap_cec, 0x2d, (hfp << 4) & 0xff);
+ regmap_write(adv7511->regmap_cec, 0x2e, hbp >> 4);
+ regmap_write(adv7511->regmap_cec, 0x2f, (hbp << 4) & 0xff);
+
+ /* vertical porch params */
+ regmap_write(adv7511->regmap_cec, 0x30, mode->vtotal >> 4);
+ regmap_write(adv7511->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
+ regmap_write(adv7511->regmap_cec, 0x32, vsw >> 4);
+ regmap_write(adv7511->regmap_cec, 0x33, (vsw << 4) & 0xff);
+ regmap_write(adv7511->regmap_cec, 0x34, vfp >> 4);
+ regmap_write(adv7511->regmap_cec, 0x35, (vfp << 4) & 0xff);
+ regmap_write(adv7511->regmap_cec, 0x36, vbp >> 4);
+ regmap_write(adv7511->regmap_cec, 0x37, (vbp << 4) & 0xff);
+}
+
+static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511)
+{
+ if (adv7511->type != ADV7533)
+ return;
+
+ if (adv7511->powered) {
+ adv7511_dsi_config_tgen(adv7511);
+
+ /* set number of dsi lanes */
+ regmap_write(adv7511->regmap_cec, 0x1c, adv7511->num_dsi_lanes << 4);
+
+ /* reset internal timing generator */
+ regmap_write(adv7511->regmap_cec, 0x27, 0xcb);
+ regmap_write(adv7511->regmap_cec, 0x27, 0x8b);
+ regmap_write(adv7511->regmap_cec, 0x27, 0xcb);
+
+ /* enable hdmi */
+ regmap_write(adv7511->regmap_cec, 0x03, 0x89);
+
+ /* explicitly disable test mode */
+ regmap_write(adv7511->regmap_cec, 0x55, 0x00);
+ } else {
+ regmap_write(adv7511->regmap_cec, 0x03, 0x0b);
+ regmap_write(adv7511->regmap_cec, 0x27, 0x0b);
+ }
}
static void adv7511_power_on(struct adv7511 *adv7511)
{
adv7511->current_edid_segment = -1;
- regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
- ADV7511_INT0_EDID_READY);
- regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
- ADV7511_INT1_DDC_ERROR);
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, 0);
@@ -386,7 +454,13 @@ static void adv7511_power_on(struct adv7511 *adv7511)
*/
regcache_sync(adv7511->regmap);
+ if (adv7511->type == ADV7533)
+ regmap_register_patch(adv7511->regmap_cec,
+ adv7533_cec_fixed_registers,
+ ARRAY_SIZE(adv7533_cec_fixed_registers));
adv7511->powered = true;
+
+ adv7511_dsi_receiver_dpms(adv7511);
}
static void adv7511_power_off(struct adv7511 *adv7511)
@@ -398,6 +472,8 @@ static void adv7511_power_off(struct adv7511 *adv7511)
regcache_mark_dirty(adv7511->regmap);
adv7511->powered = false;
+
+ adv7511_dsi_receiver_dpms(adv7511);
}
/* -----------------------------------------------------------------------------
@@ -438,13 +514,13 @@ static int adv7511_irq_process(struct adv7511 *adv7511)
regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
- if (irq0 & ADV7511_INT0_HDP && adv7511->encoder)
- drm_helper_hpd_irq_event(adv7511->encoder->dev);
+ //if (adv7511->encoder && (irq0 & ADV7511_INT0_HDP))
+ //drm_helper_hpd_irq_event(adv7511->encoder->dev);
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
adv7511->edid_read = true;
- if (adv7511->i2c_main->irq)
+ if (adv7511->irq)
wake_up_all(&adv7511->wq);
}
@@ -468,7 +544,7 @@ static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout)
{
int ret;
- if (adv7511->i2c_main->irq) {
+ if (adv7511->irq) {
ret = wait_event_interruptible_timeout(adv7511->wq,
adv7511->edid_read, msecs_to_jiffies(timeout));
} else {
@@ -509,11 +585,24 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
if (status != 2) {
adv7511->edid_read = false;
+
+ /* enable DDC interrupts */
+ regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
+ ADV7511_INT0_EDID_READY);
+ regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
+ ADV7511_INT1_DDC_ERROR);
+
regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
block);
ret = adv7511_wait_for_edid(adv7511, 200);
if (ret < 0)
return ret;
+
+ /* disable DDC interrupts */
+ regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
+ 0);
+ regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
+ 0);
}
/* Break this apart, hopefully more I2C controllers will
@@ -555,18 +644,19 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
}
/* -----------------------------------------------------------------------------
- * Encoder operations
+ * ADV75xx helpers
*/
-
-static int adv7511_get_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
+static int adv7511_get_modes(struct adv7511 *adv7511,
+ struct drm_connector *connector)
{
- struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
struct edid *edid;
unsigned int count;
/* Reading the EDID only works if the device is powered */
if (!adv7511->powered) {
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+ ADV7511_REG_POWER2_HDP_SRC_MASK,
+ ADV7511_REG_POWER2_HDP_SRC_NONE);
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
ADV7511_INT0_EDID_READY);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
@@ -596,21 +686,10 @@ static int adv7511_get_modes(struct drm_encoder *encoder,
return count;
}
-static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
- struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
-
- if (mode == DRM_MODE_DPMS_ON)
- adv7511_power_on(adv7511);
- else
- adv7511_power_off(adv7511);
-}
-
static enum drm_connector_status
-adv7511_encoder_detect(struct drm_encoder *encoder,
+adv7511_detect(struct adv7511 *adv7511,
struct drm_connector *connector)
{
- struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
enum drm_connector_status status;
unsigned int val;
bool hpd;
@@ -624,7 +703,7 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
status = connector_status_connected;
else
status = connector_status_disconnected;
-
+#if 0
hpd = adv7511_hpd(adv7511);
/* The chip resets itself when the cable is disconnected, so in case
@@ -634,7 +713,7 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
if (status == connector_status_connected && hpd && adv7511->powered) {
regcache_mark_dirty(adv7511->regmap);
adv7511_power_on(adv7511);
- adv7511_get_modes(encoder, connector);
+ adv7511_get_modes(adv7511, connector);
if (adv7511->status == connector_status_connected)
status = connector_status_disconnected;
} else {
@@ -643,25 +722,16 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
ADV7511_REG_POWER2_HDP_SRC_MASK,
ADV7511_REG_POWER2_HDP_SRC_BOTH);
}
-
+#endif
adv7511->status = status;
- return status;
-}
-
-static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
- struct drm_display_mode *mode)
-{
- if (mode->clock > 165000)
- return MODE_CLOCK_HIGH;
- return MODE_OK;
+ return status;
}
-static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
+static void adv7511_mode_set(struct adv7511 *adv7511,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
{
- struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
unsigned int low_refresh_rate;
unsigned int hsync_polarity = 0;
unsigned int vsync_polarity = 0;
@@ -744,6 +814,7 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
regmap_update_bits(adv7511->regmap, 0x17,
0x60, (vsync_polarity << 6) | (hsync_polarity << 5));
+ adv7511->curr_mode = adj_mode;
/*
* TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is
* supposed to give better results.
@@ -752,26 +823,216 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
adv7511->f_tmds = mode->clock;
}
+#ifdef CONFIG_DRM_I2C_ADV7511_SLAVE_ENCODER
+/* -----------------------------------------------------------------------------
+ * Encoder i2c slave functions
+ */
+
+static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder)
+{
+ return to_encoder_slave(encoder)->slave_priv;
+}
+
+static int adv7511_encoder_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+
+ return adv7511_get_modes(adv7511, connector);
+}
+
+static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ adv7511_power_on(adv7511);
+ else
+ adv7511_power_off(adv7511);
+}
+
+static enum drm_connector_status
+adv7511_encoder_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+
+ return adv7511_detect(adv7511, connector);
+}
+
+static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 165000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+
+ adv7511_mode_set(adv7511, mode, adj_mode);
+}
+
static struct drm_encoder_slave_funcs adv7511_encoder_funcs = {
.dpms = adv7511_encoder_dpms,
.mode_valid = adv7511_encoder_mode_valid,
.mode_set = adv7511_encoder_mode_set,
.detect = adv7511_encoder_detect,
- .get_modes = adv7511_get_modes,
+ .get_modes = adv7511_encoder_get_modes,
+};
+#else
+/* -----------------------------------------------------------------------------
+ * Bridge and connector functions
+ */
+
+static struct adv7511 *connector_to_adv7511(struct drm_connector *connector)
+{
+ return container_of(connector, struct adv7511, connector);
+}
+
+/* connector helper functions */
+static int adv7511_connector_get_modes(struct drm_connector *connector)
+{
+ struct adv7511 *adv7511 = connector_to_adv7511(connector);
+
+ return adv7511_get_modes(adv7511, connector);
+}
+
+static struct drm_encoder *adv7511_connector_best_encoder(struct drm_connector *connector)
+{
+ struct adv7511 *adv7511 = connector_to_adv7511(connector);
+
+ return adv7511->bridge.encoder;
+}
+
+static enum drm_mode_status
+adv7511_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 165000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = {
+ .get_modes = adv7511_connector_get_modes,
+ .best_encoder = adv7511_connector_best_encoder,
+ .mode_valid = adv7511_connector_mode_valid,
+};
+
+static enum drm_connector_status
+adv7511_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct adv7511 *adv7511 = connector_to_adv7511(connector);
+
+ return adv7511_detect(adv7511, connector);
+}
+
+static struct drm_connector_funcs adv7511_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = adv7511_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
+/* bridge funcs */
+static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct adv7511, bridge);
+}
+
+static void adv7511_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ adv7511_power_on(adv7511);
+}
+
+static void adv7511_bridge_post_disable(struct drm_bridge *bridge)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ if (!adv7511->powered)
+ return;
+
+ adv7511_power_off(adv7511);
+}
+
+static void adv7511_bridge_enable(struct drm_bridge *bridge)
+{
+}
+
+static void adv7511_bridge_disable(struct drm_bridge *bridge)
+{
+}
+
+static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ adv7511_mode_set(adv7511, mode, adj_mode);
+}
+
+static int adv7511_bridge_attach(struct drm_bridge *bridge)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+ int ret;
+
+ adv7511->encoder = bridge->encoder;
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ //adv7511->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ ret = drm_connector_init(bridge->dev, &adv7511->connector,
+ &adv7511_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+ drm_connector_helper_add(&adv7511->connector,
+ &adv7511_connector_helper_funcs);
+ drm_connector_register(&adv7511->connector);
+ drm_mode_connector_attach_encoder(&adv7511->connector, adv7511->encoder);
+
+ //drm_helper_hpd_irq_event(adv7511->connector.dev);
+
+ return ret;
+}
+
+static struct drm_bridge_funcs adv7511_bridge_funcs = {
+ .pre_enable = adv7511_bridge_pre_enable,
+ .enable = adv7511_bridge_enable,
+ .disable = adv7511_bridge_disable,
+ .post_disable = adv7511_bridge_post_disable,
+ .attach = adv7511_bridge_attach,
+ .mode_set = adv7511_bridge_mode_set,
+};
+#endif
+
/* -----------------------------------------------------------------------------
* Probe & remove
*/
-static int adv7511_parse_dt(struct device_node *np,
+static int adv7511_parse_dt(struct adv7511 *adv7511, struct device_node *np,
struct adv7511_link_config *config)
{
const char *str;
int ret;
- memset(config, 0, sizeof(*config));
-
of_property_read_u32(np, "adi,input-depth", &config->input_color_depth);
if (config->input_color_depth != 8 && config->input_color_depth != 10 &&
config->input_color_depth != 12)
@@ -790,6 +1051,16 @@ static int adv7511_parse_dt(struct device_node *np,
else
return -EINVAL;
+ if (adv7511->type == ADV7533) {
+ of_property_read_u32(np, "adi,dsi-lanes",
+ &config->num_dsi_lanes);
+
+ if (config->num_dsi_lanes < 1 || config->num_dsi_lanes > 4)
+ return -EINVAL;
+
+ return 0;
+ }
+
ret = of_property_read_string(np, "adi,input-clock", &str);
if (ret < 0)
return ret;
@@ -852,7 +1123,18 @@ static int adv7511_parse_dt(struct device_node *np,
static const int edid_i2c_addr = 0x7e;
static const int packet_i2c_addr = 0x70;
static const int cec_i2c_addr = 0x78;
+static const int main_i2c_addr = 0x72;
+
+static const struct of_device_id adv7511_of_ids[] = {
+ { .compatible = "adi,adv7511", .data = (void *)ADV7511 },
+ { .compatible = "adi,adv7511w", .data = (void *)ADV7511 },
+ { .compatible = "adi,adv7513", .data= (void *)ADV7511 },
+ { .compatible = "adi,adv7533", .data = (void *)ADV7533 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adv7511_of_ids);
+#ifdef CONFIG_DRM_I2C_ADV7511_SLAVE_ENCODER
static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
{
struct adv7511_link_config link_config;
@@ -871,7 +1153,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
adv7511->powered = false;
adv7511->status = connector_status_disconnected;
- ret = adv7511_parse_dt(dev->of_node, &link_config);
+ if (dev->of_node) {
+ const struct of_device_id *of_id;
+ of_id = of_match_node(adv7511_of_ids, dev->of_node);
+ adv7511->type = (unsigned long)of_id->data;
+ } else {
+ adv7511->type = id->driver_data;
+ }
+
+ memset(&link_config, 0, sizeof(link_config));
+
+ ret = adv7511_parse_dt(adv7511, dev->of_node, &link_config);
if (ret)
return ret;
@@ -897,10 +1189,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
return ret;
dev_dbg(dev, "Rev. %d\n", val);
- ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers,
+ if (adv7511->type == ADV7511) {
+ ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers,
ARRAY_SIZE(adv7511_fixed_registers));
- if (ret)
- return ret;
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_register_patch(adv7511->regmap, adv7533_fixed_registers,
+ ARRAY_SIZE(adv7533_fixed_registers));
+ if (ret)
+ return ret;
+ }
regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr);
regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
@@ -913,6 +1212,26 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
if (!adv7511->i2c_edid)
return -ENOMEM;
+ adv7511->i2c_cec = i2c_new_dummy(i2c->adapter, cec_i2c_addr >> 1);
+ if (!adv7511->i2c_cec) {
+ ret = -ENOMEM;
+ goto err_i2c_unregister_edid;
+ }
+
+ adv7511->regmap_cec = devm_regmap_init_i2c(adv7511->i2c_cec,
+ &adv7533_cec_regmap_config);
+ if (IS_ERR(adv7511->regmap_cec)) {
+ ret = PTR_ERR(adv7511->regmap_cec);
+ goto err_i2c_unregister_cec;
+ }
+
+ if (adv7511->type == ADV7533) {
+ ret = regmap_register_patch(adv7511->regmap_cec, adv7533_cec_fixed_registers,
+ ARRAY_SIZE(adv7533_cec_fixed_registers));
+ if (ret)
+ return ret;
+ }
+
if (i2c->irq) {
init_waitqueue_head(&adv7511->wq);
@@ -921,7 +1240,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
IRQF_ONESHOT, dev_name(dev),
adv7511);
if (ret)
- goto err_i2c_unregister_device;
+ goto err_i2c_unregister_cec;
+
+ adv7511->irq = i2c->irq;
}
/* CEC is unused for now */
@@ -932,11 +1253,15 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
i2c_set_clientdata(i2c, adv7511);
+ adv7511_audio_init(dev);
+
adv7511_set_link_config(adv7511, &link_config);
return 0;
-err_i2c_unregister_device:
+err_i2c_unregister_cec:
+ i2c_unregister_device(adv7511->i2c_cec);
+err_i2c_unregister_edid:
i2c_unregister_device(adv7511->i2c_edid);
return ret;
@@ -946,6 +1271,8 @@ static int adv7511_remove(struct i2c_client *i2c)
{
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+ adv7511_audio_exit(&i2c->dev);
+ i2c_unregister_device(adv7511->i2c_cec);
i2c_unregister_device(adv7511->i2c_edid);
kfree(adv7511->edid);
@@ -953,6 +1280,15 @@ static int adv7511_remove(struct i2c_client *i2c)
return 0;
}
+static const struct i2c_device_id adv7511_i2c_ids[] = {
+ { "adv7511", ADV7511 },
+ { "adv7511w", ADV7511 },
+ { "adv7513", ADV7511 },
+ { "adv7533", ADV7533 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
+
static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev,
struct drm_encoder_slave *encoder)
{
@@ -967,22 +1303,6 @@ static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev,
return 0;
}
-static const struct i2c_device_id adv7511_i2c_ids[] = {
- { "adv7511", 0 },
- { "adv7511w", 0 },
- { "adv7513", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
-
-static const struct of_device_id adv7511_of_ids[] = {
- { .compatible = "adi,adv7511", },
- { .compatible = "adi,adv7511w", },
- { .compatible = "adi,adv7513", },
- { }
-};
-MODULE_DEVICE_TABLE(of, adv7511_of_ids);
-
static struct drm_i2c_encoder_driver adv7511_driver = {
.i2c_driver = {
.driver = {
@@ -1008,6 +1328,193 @@ static void __exit adv7511_exit(void)
drm_i2c_encoder_unregister(&adv7511_driver);
}
module_exit(adv7511_exit);
+#else
+
+static int adv7533_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct adv7511 *adv;
+ struct adv7511_link_config link_config;
+ struct i2c_adapter *adapter;
+ struct device_node *adapter_node;
+ unsigned int val;
+ int irq;
+ int ret;
+
+ adv = devm_kzalloc(dev, sizeof(struct adv7511), GFP_KERNEL);
+ if (!adv)
+ return -ENOMEM;
+
+ mipi_dsi_set_drvdata(dsi, adv);
+
+ dsi->lanes = 4;
+ 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;
+
+ adv->type = ADV7533;
+ adv->powered = false;
+ adv->status = connector_status_disconnected;
+
+ memset(&link_config, 0, sizeof(link_config));
+
+ ret = adv7511_parse_dt(adv, dev->of_node, &link_config);
+ if (ret)
+ return ret;
+
+ adv->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH);
+ if (IS_ERR(adv->gpio_pd))
+ return PTR_ERR(adv->gpio_pd);
+
+ if (adv->gpio_pd) {
+ mdelay(5);
+ gpiod_set_value_cansleep(adv->gpio_pd, 0);
+ }
+
+ adapter_node = of_parse_phandle(dev->of_node, "i2c-bus", 0);
+ if (adapter_node) {
+ adapter = of_find_i2c_adapter_by_node(adapter_node);
+ if (adapter == NULL)
+ return -ENODEV;
+ } else {
+ return -ENODEV;
+ }
+
+ adv->i2c_main = i2c_new_dummy(adapter, main_i2c_addr >> 1);
+ if (!adv->i2c_main)
+ return -ENOMEM;
+
+ adv->regmap = devm_regmap_init_i2c(adv->i2c_main, &adv7511_regmap_config);
+ if (IS_ERR(adv->regmap))
+ return PTR_ERR(adv->regmap);
+
+ ret = regmap_read(adv->regmap, ADV7511_REG_CHIP_REVISION, &val);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "Rev. %d\n", val);
+
+ ret = regmap_register_patch(adv->regmap, adv7533_fixed_registers,
+ ARRAY_SIZE(adv7533_fixed_registers));
+ if (ret)
+ return ret;
+
+ regmap_write(adv->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr);
+ regmap_write(adv->regmap, ADV7511_REG_PACKET_I2C_ADDR,
+ packet_i2c_addr);
+ regmap_write(adv->regmap, ADV7511_REG_CEC_I2C_ADDR, cec_i2c_addr);
+ adv7511_packet_disable(adv, 0xffff);
+
+ adv->i2c_edid = i2c_new_dummy(adapter, edid_i2c_addr >> 1);
+ if (!adv->i2c_edid) {
+ ret = -ENOMEM;
+ goto err_i2c_unregister_main;
+ }
+
+ adv->i2c_cec = i2c_new_dummy(adapter, cec_i2c_addr >> 1);
+ if (!adv->i2c_cec) {
+ ret = -ENOMEM;
+ goto err_i2c_unregister_edid;
+ }
+
+ adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
+ &adv7533_cec_regmap_config);
+ if (IS_ERR(adv->regmap_cec)) {
+ ret = PTR_ERR(adv->regmap_cec);
+ goto err_i2c_unregister_cec;
+ }
+
+ ret = regmap_register_patch(adv->regmap_cec,
+ adv7533_cec_fixed_registers,
+ ARRAY_SIZE(adv7533_cec_fixed_registers));
+ if (ret) {
+ goto err_i2c_unregister_cec;
+ }
+
+ irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (irq) {
+ init_waitqueue_head(&adv->wq);
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ adv7511_irq_handler,
+ IRQF_ONESHOT, dev_name(dev),
+ adv);
+ if (ret)
+ goto err_i2c_unregister_cec;
+
+ adv->irq = irq;
+ }
+
+ /* CEC is unused for now */
+ regmap_write(adv->regmap, ADV7511_REG_CEC_CTRL,
+ ADV7511_CEC_CTRL_POWER_DOWN);
+
+ adv7511_power_off(adv);
+
+ adv7511_audio_init(dev);
+
+ adv7511_set_link_config(adv, &link_config);
+
+ adv->bridge.funcs = &adv7511_bridge_funcs;
+ adv->bridge.of_node = dev->of_node;
+
+ ret = drm_bridge_add(&adv->bridge);
+ if (ret) {
+ DRM_ERROR("Failed to add adv7533 bridge\n");
+ return ret;
+ }
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0)
+ goto err_bridge_remove;
+
+ return 0;
+
+err_bridge_remove:
+ drm_bridge_remove(&adv->bridge);
+err_i2c_unregister_cec:
+ i2c_unregister_device(adv->i2c_cec);
+err_i2c_unregister_edid:
+ i2c_unregister_device(adv->i2c_edid);
+err_i2c_unregister_main:
+ i2c_unregister_device(adv->i2c_main);
+
+ return ret;
+}
+
+static int adv7533_remove(struct mipi_dsi_device *dsi)
+{
+ struct adv7511 *adv = mipi_dsi_get_drvdata(dsi);
+
+ adv7511_audio_exit(&dsi->dev);
+
+ i2c_unregister_device(adv->i2c_main);
+ i2c_unregister_device(adv->i2c_cec);
+ i2c_unregister_device(adv->i2c_edid);
+
+ mipi_dsi_detach(dsi);
+ drm_bridge_remove(&adv->bridge);
+ kfree(adv->edid);
+
+ return 0;
+}
+
+static struct of_device_id adv7533_of_match[] = {
+ { .compatible = "adi,adv7533" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adv7533_of_match);
+
+static struct mipi_dsi_driver adv7533_driver = {
+ .probe = adv7533_probe,
+ .remove = adv7533_remove,
+ .driver = {
+ .name = "adv7533",
+ .of_match_table = adv7533_of_match,
+ },
+};
+module_mipi_dsi_driver(adv7533_driver);
+#endif
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver");