diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/adv7511/adv7511_drv.c')
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 131 |
1 files changed, 114 insertions, 17 deletions
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 1b2fae915ecc..ff3f73fd4867 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -9,7 +9,9 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_graph.h> #include <linux/slab.h> #include <drm/drmP.h> @@ -365,7 +367,7 @@ static void adv7511_power_on(struct adv7511 *adv7511) */ regcache_sync(adv7511->regmap); - if (adv7511->type == ADV7533) + if (adv7511->type == ADV7533 || adv7511->type == ADV7535) adv7533_dsi_power_on(adv7511); adv7511->powered = true; } @@ -382,7 +384,7 @@ static void __adv7511_power_off(struct adv7511 *adv7511) static void adv7511_power_off(struct adv7511 *adv7511) { __adv7511_power_off(adv7511); - if (adv7511->type == ADV7533) + if (adv7511->type == ADV7533 || adv7511->type == ADV7535) adv7533_dsi_power_off(adv7511); adv7511->powered = false; } @@ -583,6 +585,8 @@ static int adv7511_get_modes(struct adv7511 *adv7511, { struct edid *edid; unsigned int count; + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; + int ret; /* Reading the EDID only works if the device is powered */ if (!adv7511->powered) { @@ -611,6 +615,14 @@ static int adv7511_get_modes(struct adv7511 *adv7511, adv7511_set_config_csc(adv7511, connector, adv7511->rgb); + connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW | + DRM_BUS_FLAG_PIXDATA_NEGEDGE; + + ret = drm_display_info_set_bus_formats(&connector->display_info, + &bus_format, 1); + if (ret) + return ret; + return count; } @@ -744,12 +756,17 @@ static void adv7511_mode_set(struct adv7511 *adv7511, else low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE; - regmap_update_bits(adv7511->regmap, 0xfb, - 0x6, low_refresh_rate << 1); + if (adv7511->type == ADV7535) + regmap_update_bits(adv7511->regmap, 0x4a, + 0xc, low_refresh_rate << 2); + else + regmap_update_bits(adv7511->regmap, 0xfb, + 0x6, low_refresh_rate << 1); + regmap_update_bits(adv7511->regmap, 0x17, 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); - if (adv7511->type == ADV7533) + if (adv7511->type == ADV7533 || adv7511->type == ADV7535) adv7533_mode_set(adv7511, adj_mode); drm_mode_copy(&adv7511->curr_mode, adj_mode); @@ -836,6 +853,18 @@ static void adv7511_bridge_mode_set(struct drm_bridge *bridge, adv7511_mode_set(adv, mode, adj_mode); } +static bool adv7511_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + if (adv->type == ADV7533 || adv->type == ADV7535) + return adv7533_mode_fixup(adv, adjusted_mode); + + return true; +} + static int adv7511_bridge_attach(struct drm_bridge *bridge) { struct adv7511 *adv = bridge_to_adv7511(bridge); @@ -859,7 +888,7 @@ 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) + if (adv->type == ADV7533 || adv->type == ADV7535) ret = adv7533_attach_dsi(adv); return ret; @@ -869,6 +898,7 @@ static struct drm_bridge_funcs adv7511_bridge_funcs = { .enable = adv7511_bridge_enable, .disable = adv7511_bridge_disable, .mode_set = adv7511_bridge_mode_set, + .mode_fixup = adv7511_bridge_mode_fixup, .attach = adv7511_bridge_attach, }; @@ -964,8 +994,15 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) struct adv7511_link_config link_config; struct adv7511 *adv7511; struct device *dev = &i2c->dev; +#if IS_ENABLED(CONFIG_OF_DYNAMIC) + struct device_node *remote_node = NULL, *endpoint = NULL; + struct of_changeset ocs; + struct property *prop; +#endif unsigned int main_i2c_addr = i2c->addr << 1; unsigned int edid_i2c_addr = main_i2c_addr + 4; + unsigned int cec_i2c_addr = main_i2c_addr - 2; + unsigned int pkt_i2c_addr = main_i2c_addr - 0xa; unsigned int val; int ret; @@ -993,6 +1030,21 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (ret) return ret; + if (adv7511->addr_cec != 0) + cec_i2c_addr = adv7511->addr_cec << 1; + else + adv7511->addr_cec = cec_i2c_addr >> 1; + + if (adv7511->addr_edid != 0) + edid_i2c_addr = adv7511->addr_edid << 1; + else + adv7511->addr_edid = edid_i2c_addr >> 1; + + if (adv7511->addr_pkt != 0) + pkt_i2c_addr = adv7511->addr_pkt << 1; + else + adv7511->addr_pkt = pkt_i2c_addr >> 1; + /* * The power down GPIO is optional. If present, toggle it from active to * inactive to wake up the encoder. @@ -1007,12 +1059,14 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config); - if (IS_ERR(adv7511->regmap)) - return PTR_ERR(adv7511->regmap); + if (IS_ERR(adv7511->regmap)) { + ret = PTR_ERR(adv7511->regmap); + goto of_reconfig; + } ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val); if (ret) - return ret; + goto of_reconfig; dev_dbg(dev, "Rev. %d\n", val); if (adv7511->type == ADV7511) @@ -1024,11 +1078,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (ret) return ret; - regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); + regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, + edid_i2c_addr); regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, - main_i2c_addr - 0xa); + pkt_i2c_addr); regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, - main_i2c_addr - 2); + cec_i2c_addr); adv7511_packet_disable(adv7511, 0xffff); @@ -1037,7 +1092,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (!adv7511->i2c_edid) return -ENOMEM; - if (adv7511->type == ADV7533) { + if (adv7511->type == ADV7533 || adv7511->type == ADV7535) { ret = adv7533_init_cec(adv7511); if (ret) goto err_i2c_unregister_edid; @@ -1082,6 +1137,45 @@ err_unregister_cec: adv7533_uninit_cec(adv7511); err_i2c_unregister_edid: i2c_unregister_device(adv7511->i2c_edid); +of_reconfig: +#if IS_ENABLED(CONFIG_OF_DYNAMIC) + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (endpoint) + remote_node = of_graph_get_remote_port_parent(endpoint); + + if (remote_node) { + int num_endpoints = 0; + + /* + * Remote node should have two endpoints (input and output: us) + * If remote node has more than two endpoints, probably that it + * has more outputs, so there is no need to disable it. + */ + endpoint = NULL; + while ((endpoint = of_graph_get_next_endpoint(remote_node, + endpoint))) + num_endpoints++; + + if (num_endpoints > 2) { + of_node_put(remote_node); + return ret; + } + + prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL); + prop->name = devm_kstrdup(dev, "status", GFP_KERNEL); + prop->value = devm_kstrdup(dev, "disabled", GFP_KERNEL); + prop->length = 9; + of_changeset_init(&ocs); + of_changeset_update_property(&ocs, remote_node, prop); + ret = of_changeset_apply(&ocs); + if (!ret) + dev_warn(dev, + "Probe failed. Remote port '%s' disabled\n", + remote_node->full_name); + + of_node_put(remote_node); + }; +#endif return ret; } @@ -1090,16 +1184,17 @@ static int adv7511_remove(struct i2c_client *i2c) { struct adv7511 *adv7511 = i2c_get_clientdata(i2c); - if (adv7511->type == ADV7533) { + if (adv7511->type == ADV7533 || adv7511->type == ADV7535) { adv7533_detach_dsi(adv7511); adv7533_uninit_cec(adv7511); } drm_bridge_remove(&adv7511->bridge); - i2c_unregister_device(adv7511->i2c_edid); - - kfree(adv7511->edid); + if (adv7511->i2c_edid) { + i2c_unregister_device(adv7511->i2c_edid); + kfree(adv7511->edid); + } return 0; } @@ -1110,6 +1205,7 @@ static const struct i2c_device_id adv7511_i2c_ids[] = { { "adv7513", ADV7511 }, #ifdef CONFIG_DRM_I2C_ADV7533 { "adv7533", ADV7533 }, + { "adv7535", ADV7535 }, #endif { } }; @@ -1121,6 +1217,7 @@ static const struct of_device_id adv7511_of_ids[] = { { .compatible = "adi,adv7513", .data = (void *)ADV7511 }, #ifdef CONFIG_DRM_I2C_ADV7533 { .compatible = "adi,adv7533", .data = (void *)ADV7533 }, + { .compatible = "adi,adv7535", .data = (void *)ADV7535 }, #endif { } }; |