diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 22c7ed63a001..8bb06977343a 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -3,6 +3,7 @@ menu "I2C encoder or helper chips" config DRM_I2C_ADV7511 tristate "AV7511 encoder" + depends on OF select REGMAP_I2C help Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index a02112ba1c3d..c2642f99865d 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -14,9 +14,10 @@ #include #include +#include +#include #include #include -#include #include "adv7511.h" @@ -36,7 +37,8 @@ struct adv7511 { bool edid_read; wait_queue_head_t wq; - struct drm_encoder *encoder; + struct drm_bridge bridge; + struct drm_connector connector; bool embedded_sync; enum adv7511_sync_polarity vsync_polarity; @@ -48,11 +50,6 @@ struct adv7511 { 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_sequence adv7511_fixed_registers[] = { { 0x98, 0x03 }, @@ -446,8 +443,8 @@ 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_HPD && adv7511->encoder) - drm_helper_hpd_irq_event(adv7511->encoder->dev); + if (irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) + drm_helper_hpd_irq_event(adv7511->connector.dev); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { adv7511->edid_read = true; @@ -563,13 +560,12 @@ 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, +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; @@ -606,21 +602,9 @@ 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, - struct drm_connector *connector) +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; @@ -644,7 +628,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 { @@ -658,8 +642,8 @@ adv7511_encoder_detect(struct drm_encoder *encoder, return status; } -static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) +static int adv7511_mode_valid(struct adv7511 *adv7511, + struct drm_display_mode *mode) { if (mode->clock > 165000) return MODE_CLOCK_HIGH; @@ -667,11 +651,10 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, return MODE_OK; } -static void adv7511_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) +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; @@ -762,12 +745,111 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, adv7511->f_tmds = mode->clock; } -static const 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, +/* Connector funcs */ +static struct adv7511 *connector_to_adv7511(struct drm_connector *connector) +{ + return container_of(connector, struct adv7511, connector); +} + +static int adv7511_connector_get_modes(struct drm_connector *connector) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_get_modes(adv, connector); +} + +static enum drm_mode_status +adv7511_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_mode_valid(adv, mode); +} + +static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = { + .get_modes = adv7511_connector_get_modes, + .mode_valid = adv7511_connector_mode_valid, +}; + +static enum drm_connector_status +adv7511_connector_detect(struct drm_connector *connector, bool force) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_detect(adv, connector); +} + +static struct drm_connector_funcs adv7511_connector_funcs = { + .dpms = drm_atomic_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_enable(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_power_on(adv); +} + +static void adv7511_bridge_disable(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_power_off(adv); +} + +static void adv7511_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_mode_set(adv, mode, adj_mode); +} + +static int adv7511_bridge_attach(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + int ret; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + adv->connector.polled = DRM_CONNECTOR_POLL_HPD; + + ret = drm_connector_init(bridge->dev, &adv->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(&adv->connector, + &adv7511_connector_helper_funcs); + drm_mode_connector_attach_encoder(&adv->connector, bridge->encoder); + + return ret; +} + +static struct drm_bridge_funcs adv7511_bridge_funcs = { + .enable = adv7511_bridge_enable, + .disable = adv7511_bridge_disable, + .mode_set = adv7511_bridge_mode_set, + .attach = adv7511_bridge_attach, }; /* ----------------------------------------------------------------------------- @@ -944,6 +1026,15 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) adv7511_set_link_config(adv7511, &link_config); + adv7511->bridge.funcs = &adv7511_bridge_funcs; + adv7511->bridge.of_node = dev->of_node; + + ret = drm_bridge_add(&adv7511->bridge); + if (ret) { + dev_err(dev, "failed to add adv7511 bridge\n"); + goto err_i2c_unregister_device; + } + return 0; err_i2c_unregister_device: @@ -956,6 +1047,8 @@ static int adv7511_remove(struct i2c_client *i2c) { struct adv7511 *adv7511 = i2c_get_clientdata(i2c); + drm_bridge_remove(&adv7511->bridge); + i2c_unregister_device(adv7511->i2c_edid); kfree(adv7511->edid); @@ -963,20 +1056,6 @@ static int adv7511_remove(struct i2c_client *i2c) return 0; } -static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, - struct drm_encoder_slave *encoder) -{ - - struct adv7511 *adv7511 = i2c_get_clientdata(i2c); - - encoder->slave_priv = adv7511; - encoder->slave_funcs = &adv7511_encoder_funcs; - - adv7511->encoder = &encoder->base; - - return 0; -} - static const struct i2c_device_id adv7511_i2c_ids[] = { { "adv7511", 0 }, { "adv7511w", 0 }, @@ -993,31 +1072,17 @@ static const struct of_device_id adv7511_of_ids[] = { }; MODULE_DEVICE_TABLE(of, adv7511_of_ids); -static struct drm_i2c_encoder_driver adv7511_driver = { - .i2c_driver = { - .driver = { - .name = "adv7511", - .of_match_table = adv7511_of_ids, - }, - .id_table = adv7511_i2c_ids, - .probe = adv7511_probe, - .remove = adv7511_remove, +static struct i2c_driver adv7511_driver = { + .driver = { + .name = "adv7511", + .of_match_table = adv7511_of_ids, }, - - .encoder_init = adv7511_encoder_init, + .id_table = adv7511_i2c_ids, + .probe = adv7511_probe, + .remove = adv7511_remove, }; -static int __init adv7511_init(void) -{ - return drm_i2c_encoder_register(THIS_MODULE, &adv7511_driver); -} -module_init(adv7511_init); - -static void __exit adv7511_exit(void) -{ - drm_i2c_encoder_unregister(&adv7511_driver); -} -module_exit(adv7511_exit); +module_i2c_driver(adv7511_driver); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver");