diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c')
| -rw-r--r-- | drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 243 |
1 files changed, 113 insertions, 130 deletions
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 6200f12a37e6..c9e6505cbd88 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -61,17 +61,15 @@ struct ge_b850v3_lvds { struct drm_bridge bridge; struct i2c_client *stdp4028_i2c; struct i2c_client *stdp2690_i2c; - struct edid *edid; }; static struct ge_b850v3_lvds *ge_b850v3_lvds_ptr; -static u8 *stdp2690_get_edid(struct i2c_client *client) +static int stdp2690_read_block(void *context, u8 *buf, unsigned int block, size_t len) { + struct i2c_client *client = context; struct i2c_adapter *adapter = client->adapter; - unsigned char start = 0x00; - unsigned int total_size; - u8 *block = kmalloc(EDID_LENGTH, GFP_KERNEL); + unsigned char start = block * EDID_LENGTH; struct i2c_msg msgs[] = { { @@ -82,89 +80,48 @@ static u8 *stdp2690_get_edid(struct i2c_client *client) }, { .addr = client->addr, .flags = I2C_M_RD, - .len = EDID_LENGTH, - .buf = block, + .len = len, + .buf = buf, } }; - if (!block) - return NULL; + if (i2c_transfer(adapter, msgs, 2) != 2) + return -1; - if (i2c_transfer(adapter, msgs, 2) != 2) { - DRM_ERROR("Unable to read EDID.\n"); - goto err; - } - - if (!drm_edid_block_valid(block, 0, false, NULL)) { - DRM_ERROR("Invalid EDID data\n"); - goto err; - } + return 0; +} - total_size = (block[EDID_EXT_BLOCK_CNT] + 1) * EDID_LENGTH; - if (total_size > EDID_LENGTH) { - kfree(block); - block = kmalloc(total_size, GFP_KERNEL); - if (!block) - return NULL; - - /* Yes, read the entire buffer, and do not skip the first - * EDID_LENGTH bytes. - */ - start = 0x00; - msgs[1].len = total_size; - msgs[1].buf = block; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - DRM_ERROR("Unable to read EDID extension blocks.\n"); - goto err; - } - if (!drm_edid_block_valid(block, 1, false, NULL)) { - DRM_ERROR("Invalid EDID data\n"); - goto err; - } - } +static const struct drm_edid *ge_b850v3_lvds_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct i2c_client *client; - return block; + client = ge_b850v3_lvds_ptr->stdp2690_i2c; -err: - kfree(block); - return NULL; + return drm_edid_read_custom(connector, stdp2690_read_block, client); } static int ge_b850v3_lvds_get_modes(struct drm_connector *connector) { - struct i2c_client *client; - int num_modes = 0; - - client = ge_b850v3_lvds_ptr->stdp2690_i2c; + const struct drm_edid *drm_edid; + int num_modes; - kfree(ge_b850v3_lvds_ptr->edid); - ge_b850v3_lvds_ptr->edid = (struct edid *)stdp2690_get_edid(client); + drm_edid = ge_b850v3_lvds_edid_read(&ge_b850v3_lvds_ptr->bridge, connector); - if (ge_b850v3_lvds_ptr->edid) { - drm_connector_update_edid_property(connector, - ge_b850v3_lvds_ptr->edid); - num_modes = drm_add_edid_modes(connector, - ge_b850v3_lvds_ptr->edid); - } + drm_edid_connector_update(connector, drm_edid); + num_modes = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); return num_modes; } -static enum drm_mode_status ge_b850v3_lvds_mode_valid( - struct drm_connector *connector, struct drm_display_mode *mode) -{ - return MODE_OK; -} - static const struct drm_connector_helper_funcs ge_b850v3_lvds_connector_helper_funcs = { .get_modes = ge_b850v3_lvds_get_modes, - .mode_valid = ge_b850v3_lvds_mode_valid, }; -static enum drm_connector_status ge_b850v3_lvds_detect( - struct drm_connector *connector, bool force) +static enum drm_connector_status +ge_b850v3_lvds_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) { struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; @@ -182,6 +139,12 @@ static enum drm_connector_status ge_b850v3_lvds_detect( return connector_status_unknown; } +static enum drm_connector_status ge_b850v3_lvds_detect(struct drm_connector *connector, + bool force) +{ + return ge_b850v3_lvds_bridge_detect(&ge_b850v3_lvds_ptr->bridge, connector); +} + static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .detect = ge_b850v3_lvds_detect, @@ -191,6 +154,27 @@ static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static int ge_b850v3_lvds_create_connector(struct drm_bridge *bridge) +{ + struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector; + int ret; + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, + &ge_b850v3_lvds_connector_helper_funcs); + + ret = drm_connector_init(bridge->dev, connector, + &ge_b850v3_lvds_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + + return drm_connector_attach_encoder(connector, bridge->encoder); +} + static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) { struct i2c_client *stdp4028_i2c @@ -200,46 +184,18 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR); - if (ge_b850v3_lvds_ptr->connector.dev) - drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->connector.dev); + if (ge_b850v3_lvds_ptr->bridge.dev) + drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->bridge.dev); return IRQ_HANDLED; } static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) { - struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector; struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; - int ret; - - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } - - if (!bridge->encoder) { - DRM_ERROR("Parent encoder object not found"); - return -ENODEV; - } - - connector->polled = DRM_CONNECTOR_POLL_HPD; - - drm_connector_helper_add(connector, - &ge_b850v3_lvds_connector_helper_funcs); - - ret = drm_connector_init(bridge->dev, connector, - &ge_b850v3_lvds_connector_funcs, - DRM_MODE_CONNECTOR_DisplayPort); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } - - ret = drm_connector_attach_encoder(connector, bridge->encoder); - if (ret) - return ret; /* Configures the bridge to re-enable interrupts after each ack. */ i2c_smbus_write_word_data(stdp4028_i2c, @@ -251,11 +207,16 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, STDP4028_DPTX_IRQ_EN_REG, STDP4028_DPTX_IRQ_CONFIG); - return 0; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + return ge_b850v3_lvds_create_connector(bridge); } static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = { .attach = ge_b850v3_lvds_attach, + .detect = ge_b850v3_lvds_bridge_detect, + .edid_read = ge_b850v3_lvds_edid_read, }; static int ge_b850v3_lvds_init(struct device *dev) @@ -265,13 +226,11 @@ static int ge_b850v3_lvds_init(struct device *dev) if (ge_b850v3_lvds_ptr) goto success; - ge_b850v3_lvds_ptr = devm_kzalloc(dev, - sizeof(*ge_b850v3_lvds_ptr), - GFP_KERNEL); - - if (!ge_b850v3_lvds_ptr) { + ge_b850v3_lvds_ptr = devm_drm_bridge_alloc(dev, struct ge_b850v3_lvds, bridge, + &ge_b850v3_lvds_funcs); + if (IS_ERR(ge_b850v3_lvds_ptr)) { mutex_unlock(&ge_b850v3_lvds_dev_mutex); - return -ENOMEM; + return PTR_ERR(ge_b850v3_lvds_ptr); } success: @@ -286,30 +245,27 @@ static void ge_b850v3_lvds_remove(void) * This check is to avoid both the drivers * removing the bridge in their remove() function */ - if (!ge_b850v3_lvds_ptr) + if (!ge_b850v3_lvds_ptr || + !ge_b850v3_lvds_ptr->stdp2690_i2c || + !ge_b850v3_lvds_ptr->stdp4028_i2c) goto out; drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge); - kfree(ge_b850v3_lvds_ptr->edid); - ge_b850v3_lvds_ptr = NULL; out: mutex_unlock(&ge_b850v3_lvds_dev_mutex); } -static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, - const struct i2c_device_id *id) +static int ge_b850v3_register(void) { + struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; struct device *dev = &stdp4028_i2c->dev; - ge_b850v3_lvds_init(dev); - - ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c; - i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr); - /* drm bridge initialization */ - ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; + ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT | + DRM_BRIDGE_OP_EDID; + ge_b850v3_lvds_ptr->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node; drm_bridge_add(&ge_b850v3_lvds_ptr->bridge); @@ -328,16 +284,34 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); } -static int stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c) +static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c) { - ge_b850v3_lvds_remove(); + struct device *dev = &stdp4028_i2c->dev; + int ret; - return 0; + ret = ge_b850v3_lvds_init(dev); + + if (ret) + return ret; + + ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c; + i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr); + + /* Only register after both bridges are probed */ + if (!ge_b850v3_lvds_ptr->stdp2690_i2c) + return 0; + + return ge_b850v3_register(); +} + +static void stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c) +{ + ge_b850v3_lvds_remove(); } static const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = { - {"stdp4028_ge_fw", 0}, - {}, + { "stdp4028_ge_fw" }, + {} }; MODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table); @@ -357,29 +331,34 @@ static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { }, }; -static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c, - const struct i2c_device_id *id) +static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c) { struct device *dev = &stdp2690_i2c->dev; + int ret; + + ret = ge_b850v3_lvds_init(dev); - ge_b850v3_lvds_init(dev); + if (ret) + return ret; ge_b850v3_lvds_ptr->stdp2690_i2c = stdp2690_i2c; i2c_set_clientdata(stdp2690_i2c, ge_b850v3_lvds_ptr); - return 0; + /* Only register after both bridges are probed */ + if (!ge_b850v3_lvds_ptr->stdp4028_i2c) + return 0; + + return ge_b850v3_register(); } -static int stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c) +static void stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c) { ge_b850v3_lvds_remove(); - - return 0; } static const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = { - {"stdp2690_ge_fw", 0}, - {}, + { "stdp2690_ge_fw" }, + {} }; MODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table); @@ -407,7 +386,11 @@ static int __init stdpxxxx_ge_b850v3_init(void) if (ret) return ret; - return i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); + ret = i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); + if (ret) + i2c_del_driver(&stdp4028_ge_b850v3_fw_driver); + + return ret; } module_init(stdpxxxx_ge_b850v3_init); |
