Skip to content

Commit

Permalink
drm/i2c: adv7511: Convert to drm_bridge
Browse files Browse the repository at this point in the history
We don't want to use the old i2c slave encoder interface anymore.

Remove that and make the i2c driver create a drm_bridge entity instead.
Converting to bridges helps because the kms drivers don't need to
exract encoder slave ops from this driver and use it within their
own encoder/connector ops.

The driver now creates its own connector when a kms driver attaches
itself to the bridge. Therefore, kms drivers don't need to create
their own connectors anymore.

The old encoder slave ops are now used by the new bridge and connector
entities.

The of_node member in drm_bridge is accessible only when CONFIG_OF is
enabled. The driver anyway only works only when OF is available. Make
the driver depend on OF in its Kconfig.

Signed-off-by: Archit Taneja <[email protected]>
  • Loading branch information
boddob committed Jul 13, 2016
1 parent 2ae9958 commit e12c2f6
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 74 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
213 changes: 139 additions & 74 deletions drivers/gpu/drm/i2c/adv7511.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
#include <linux/slab.h>

#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder_slave.h>

#include "adv7511.h"

Expand All @@ -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;
Expand All @@ -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 },
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -658,20 +642,19 @@ 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;

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;
Expand Down Expand Up @@ -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,
};

/* -----------------------------------------------------------------------------
Expand Down Expand Up @@ -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:
Expand All @@ -956,27 +1047,15 @@ 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);

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 },
Expand All @@ -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 <[email protected]>");
MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver");
Expand Down

0 comments on commit e12c2f6

Please sign in to comment.