diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/lontium-lt9611uxc.c')
| -rw-r--r-- | drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 238 |
1 files changed, 76 insertions, 162 deletions
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 3cac16db970f..38fb8776c0f4 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -6,6 +6,7 @@ #include <linux/firmware.h> #include <linux/gpio/consumer.h> +#include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/mutex.h> @@ -20,17 +21,21 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> +#include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #define EDID_BLOCK_SIZE 128 #define EDID_NUM_BLOCKS 2 +#define FW_FILE "lt9611uxc_fw.bin" + struct lt9611uxc { struct device *dev; struct drm_bridge bridge; - struct drm_connector connector; + struct drm_bridge *next_bridge; struct regmap *regmap; /* Protects all accesses to registers by stopping the on-chip MCU */ @@ -116,11 +121,6 @@ static struct lt9611uxc *bridge_to_lt9611uxc(struct drm_bridge *bridge) return container_of(bridge, struct lt9611uxc, bridge); } -static struct lt9611uxc *connector_to_lt9611uxc(struct drm_connector *connector) -{ - return container_of(connector, struct lt9611uxc, connector); -} - static void lt9611uxc_lock(struct lt9611uxc *lt9611uxc) { mutex_lock(<9611uxc->ocm_lock); @@ -167,19 +167,14 @@ static void lt9611uxc_hpd_work(struct work_struct *work) struct lt9611uxc *lt9611uxc = container_of(work, struct lt9611uxc, work); bool connected; - if (lt9611uxc->connector.dev) - drm_kms_helper_hotplug_event(lt9611uxc->connector.dev); - else { - - mutex_lock(<9611uxc->ocm_lock); - connected = lt9611uxc->hdmi_connected; - mutex_unlock(<9611uxc->ocm_lock); + mutex_lock(<9611uxc->ocm_lock); + connected = lt9611uxc->hdmi_connected; + mutex_unlock(<9611uxc->ocm_lock); - drm_bridge_hpd_notify(<9611uxc->bridge, - connected ? - connector_status_connected : - connector_status_disconnected); - } + drm_bridge_hpd_notify(<9611uxc->bridge, + connected ? + connector_status_connected : + connector_status_disconnected); } static void lt9611uxc_reset(struct lt9611uxc *lt9611uxc) @@ -257,17 +252,16 @@ static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc, const struct mipi_dsi_device_info info = { "lt9611uxc", 0, NULL }; struct mipi_dsi_device *dsi; struct mipi_dsi_host *host; + struct device *dev = lt9611uxc->dev; int ret; host = of_find_mipi_dsi_host_by_node(dsi_node); - if (!host) { - dev_err(lt9611uxc->dev, "failed to find dsi host\n"); - return ERR_PTR(-EPROBE_DEFER); - } + if (!host) + return ERR_PTR(dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n")); - dsi = mipi_dsi_device_register_full(host, &info); + dsi = devm_mipi_dsi_device_register_full(dev, host, &info); if (IS_ERR(dsi)) { - dev_err(lt9611uxc->dev, "failed to create dsi device\n"); + dev_err(dev, "failed to create dsi device\n"); return dsi; } @@ -276,128 +270,23 @@ static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc, dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_VIDEO_HSE; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { - dev_err(lt9611uxc->dev, "failed to attach dsi to host\n"); - mipi_dsi_device_unregister(dsi); + dev_err(dev, "failed to attach dsi to host\n"); return ERR_PTR(ret); } return dsi; } -static int lt9611uxc_connector_get_modes(struct drm_connector *connector) -{ - struct lt9611uxc *lt9611uxc = connector_to_lt9611uxc(connector); - unsigned int count; - struct edid *edid; - - edid = lt9611uxc->bridge.funcs->get_edid(<9611uxc->bridge, connector); - drm_connector_update_edid_property(connector, edid); - count = drm_add_edid_modes(connector, edid); - kfree(edid); - - return count; -} - -static enum drm_connector_status lt9611uxc_connector_detect(struct drm_connector *connector, - bool force) -{ - struct lt9611uxc *lt9611uxc = connector_to_lt9611uxc(connector); - - return lt9611uxc->bridge.funcs->detect(<9611uxc->bridge); -} - -static enum drm_mode_status lt9611uxc_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct lt9611uxc_mode *lt9611uxc_mode = lt9611uxc_find_mode(mode); - - return lt9611uxc_mode ? MODE_OK : MODE_BAD; -} - -static const struct drm_connector_helper_funcs lt9611uxc_bridge_connector_helper_funcs = { - .get_modes = lt9611uxc_connector_get_modes, - .mode_valid = lt9611uxc_connector_mode_valid, -}; - -static const struct drm_connector_funcs lt9611uxc_bridge_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = lt9611uxc_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, -}; - -static int lt9611uxc_connector_init(struct drm_bridge *bridge, struct lt9611uxc *lt9611uxc) -{ - int ret; - - if (!bridge->encoder) { - DRM_ERROR("Parent encoder object not found"); - return -ENODEV; - } - - drm_connector_helper_add(<9611uxc->connector, - <9611uxc_bridge_connector_helper_funcs); - ret = drm_connector_init(bridge->dev, <9611uxc->connector, - <9611uxc_bridge_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } - - return drm_connector_attach_encoder(<9611uxc->connector, bridge->encoder); -} - -static void lt9611uxc_bridge_detach(struct drm_bridge *bridge) -{ - struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); - - if (lt9611uxc->dsi1) { - mipi_dsi_detach(lt9611uxc->dsi1); - mipi_dsi_device_unregister(lt9611uxc->dsi1); - } - - mipi_dsi_detach(lt9611uxc->dsi0); - mipi_dsi_device_unregister(lt9611uxc->dsi0); -} - static int lt9611uxc_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) { struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); - int ret; - - if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { - ret = lt9611uxc_connector_init(bridge, lt9611uxc); - if (ret < 0) - return ret; - } - - /* Attach primary DSI */ - lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node); - if (IS_ERR(lt9611uxc->dsi0)) - return PTR_ERR(lt9611uxc->dsi0); - - /* Attach secondary DSI, if specified */ - if (lt9611uxc->dsi1_node) { - lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node); - if (IS_ERR(lt9611uxc->dsi1)) { - ret = PTR_ERR(lt9611uxc->dsi1); - goto err_unregister_dsi0; - } - } - - return 0; -err_unregister_dsi0: - mipi_dsi_detach(lt9611uxc->dsi0); - mipi_dsi_device_unregister(lt9611uxc->dsi0); - - return ret; + return drm_bridge_attach(encoder, lt9611uxc->next_bridge, + bridge, flags); } static enum drm_mode_status @@ -464,7 +353,8 @@ static void lt9611uxc_bridge_mode_set(struct drm_bridge *bridge, lt9611uxc_unlock(lt9611uxc); } -static enum drm_connector_status lt9611uxc_bridge_detect(struct drm_bridge *bridge) +static enum drm_connector_status +lt9611uxc_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) { struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); unsigned int reg_val = 0; @@ -521,8 +411,8 @@ static int lt9611uxc_get_edid_block(void *data, u8 *buf, unsigned int block, siz return 0; }; -static struct edid *lt9611uxc_bridge_get_edid(struct drm_bridge *bridge, - struct drm_connector *connector) +static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) { struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); int ret; @@ -536,16 +426,15 @@ static struct edid *lt9611uxc_bridge_get_edid(struct drm_bridge *bridge, return NULL; } - return drm_do_get_edid(connector, lt9611uxc_get_edid_block, lt9611uxc); + return drm_edid_read_custom(connector, lt9611uxc_get_edid_block, lt9611uxc); } static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = { .attach = lt9611uxc_bridge_attach, - .detach = lt9611uxc_bridge_detach, .mode_valid = lt9611uxc_bridge_mode_valid, .mode_set = lt9611uxc_bridge_mode_set, .detect = lt9611uxc_bridge_detect, - .get_edid = lt9611uxc_bridge_get_edid, + .edid_read = lt9611uxc_bridge_edid_read, }; static int lt9611uxc_parse_dt(struct device *dev, @@ -559,7 +448,7 @@ static int lt9611uxc_parse_dt(struct device *dev, lt9611uxc->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1); - return 0; + return drm_of_find_panel_or_bridge(dev->of_node, 2, -1, NULL, <9611uxc->next_bridge); } static int lt9611uxc_gpio_init(struct lt9611uxc *lt9611uxc) @@ -635,7 +524,8 @@ static void lt9611uxc_audio_shutdown(struct device *dev, void *data) } static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component, - struct device_node *endpoint) + struct device_node *endpoint, + void *data) { struct of_endpoint of_ep; int ret; @@ -784,7 +674,7 @@ static int lt9611uxc_firmware_update(struct lt9611uxc *lt9611uxc) REG_SEQ0(0x805a, 0x00), }; - ret = request_firmware(&fw, "lt9611uxc_fw.bin", lt9611uxc->dev); + ret = request_firmware(&fw, FW_FILE, lt9611uxc->dev); if (ret < 0) return ret; @@ -874,8 +764,7 @@ static const struct attribute_group *lt9611uxc_attr_groups[] = { NULL, }; -static int lt9611uxc_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9611uxc_probe(struct i2c_client *client) { struct lt9611uxc *lt9611uxc; struct device *dev = &client->dev; @@ -887,11 +776,11 @@ static int lt9611uxc_probe(struct i2c_client *client, return -ENODEV; } - lt9611uxc = devm_kzalloc(dev, sizeof(*lt9611uxc), GFP_KERNEL); - if (!lt9611uxc) - return -ENOMEM; + lt9611uxc = devm_drm_bridge_alloc(dev, struct lt9611uxc, bridge, <9611uxc_bridge_funcs); + if (IS_ERR(lt9611uxc)) + return PTR_ERR(lt9611uxc); - lt9611uxc->dev = &client->dev; + lt9611uxc->dev = dev; lt9611uxc->client = client; mutex_init(<9611uxc->ocm_lock); @@ -901,7 +790,7 @@ static int lt9611uxc_probe(struct i2c_client *client, return PTR_ERR(lt9611uxc->regmap); } - ret = lt9611uxc_parse_dt(&client->dev, lt9611uxc); + ret = lt9611uxc_parse_dt(dev, lt9611uxc); if (ret) { dev_err(dev, "failed to parse device tree\n"); return ret; @@ -958,9 +847,9 @@ retry: init_waitqueue_head(<9611uxc->wq); INIT_WORK(<9611uxc->work, lt9611uxc_hpd_work); - ret = devm_request_threaded_irq(dev, client->irq, NULL, - lt9611uxc_irq_thread_handler, - IRQF_ONESHOT, "lt9611uxc", lt9611uxc); + ret = request_threaded_irq(client->irq, NULL, + lt9611uxc_irq_thread_handler, + IRQF_ONESHOT, "lt9611uxc", lt9611uxc); if (ret) { dev_err(dev, "failed to request irq\n"); goto err_disable_regulators; @@ -968,7 +857,6 @@ retry: i2c_set_clientdata(client, lt9611uxc); - lt9611uxc->bridge.funcs = <9611uxc_bridge_funcs; lt9611uxc->bridge.of_node = client->dev.of_node; lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; if (lt9611uxc->hpd_supported) @@ -977,7 +865,32 @@ retry: drm_bridge_add(<9611uxc->bridge); - return lt9611uxc_audio_init(dev, lt9611uxc); + /* Attach primary DSI */ + lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node); + if (IS_ERR(lt9611uxc->dsi0)) { + ret = PTR_ERR(lt9611uxc->dsi0); + goto err_remove_bridge; + } + + /* Attach secondary DSI, if specified */ + if (lt9611uxc->dsi1_node) { + lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node); + if (IS_ERR(lt9611uxc->dsi1)) { + ret = PTR_ERR(lt9611uxc->dsi1); + goto err_remove_bridge; + } + } + + ret = lt9611uxc_audio_init(dev, lt9611uxc); + if (ret) + goto err_remove_bridge; + + return 0; + +err_remove_bridge: + free_irq(client->irq, lt9611uxc); + cancel_work_sync(<9611uxc->work); + drm_bridge_remove(<9611uxc->bridge); err_disable_regulators: regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies); @@ -989,12 +902,12 @@ err_of_put: return ret; } -static int lt9611uxc_remove(struct i2c_client *client) +static void lt9611uxc_remove(struct i2c_client *client) { struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client); - disable_irq(client->irq); - flush_scheduled_work(); + free_irq(client->irq, lt9611uxc); + cancel_work_sync(<9611uxc->work); lt9611uxc_audio_exit(lt9611uxc); drm_bridge_remove(<9611uxc->bridge); @@ -1004,12 +917,10 @@ static int lt9611uxc_remove(struct i2c_client *client) of_node_put(lt9611uxc->dsi1_node); of_node_put(lt9611uxc->dsi0_node); - - return 0; } -static struct i2c_device_id lt9611uxc_id[] = { - { "lontium,lt9611uxc", 0 }, +static const struct i2c_device_id lt9611uxc_id[] = { + { "lontium,lt9611uxc" }, { /* sentinel */ } }; @@ -1032,4 +943,7 @@ static struct i2c_driver lt9611uxc_driver = { module_i2c_driver(lt9611uxc_driver); MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>"); +MODULE_DESCRIPTION("Lontium LT9611UXC DSI/HDMI bridge driver"); MODULE_LICENSE("GPL v2"); + +MODULE_FIRMWARE(FW_FILE); |
