diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/dss/hdmi4.c')
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4.c | 390 |
1 files changed, 194 insertions, 196 deletions
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index aabdda394c9c..3cd612af2449 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HDMI interface DSS driver for TI's OMAP4 family of SoCs. * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "HDMI" @@ -31,7 +20,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/component.h> #include <linux/of.h> @@ -39,6 +27,10 @@ #include <sound/omap-hdmi-audio.h> #include <media/cec.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> + #include "omapdss.h" #include "hdmi4_core.h" #include "hdmi4_cec.h" @@ -52,10 +44,10 @@ static int hdmi_runtime_get(struct omap_hdmi *hdmi) DSSDBG("hdmi_runtime_get\n"); r = pm_runtime_get_sync(&hdmi->pdev->dev); - WARN_ON(r < 0); - if (r < 0) + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(&hdmi->pdev->dev); return r; - + } return 0; } @@ -248,20 +240,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi) hdmi_power_off_core(hdmi); } -static void hdmi_display_set_timings(struct omap_dss_device *dssdev, - const struct videomode *vm) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - mutex_lock(&hdmi->lock); - - hdmi->cfg.vm = *vm; - - dispc_set_tv_pclk(hdmi->dss->dispc, vm->pixelclock); - - mutex_unlock(&hdmi->lock); -} - static int hdmi_dump_regs(struct seq_file *s, void *p) { struct omap_hdmi *hdmi = s->private; @@ -283,23 +261,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p) return 0; } -static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len) -{ - int r; - - mutex_lock(&hdmi->lock); - - r = hdmi_runtime_get(hdmi); - BUG_ON(r); - - r = hdmi4_read_edid(&hdmi->core, buf, len); - - hdmi_runtime_put(hdmi); - mutex_unlock(&hdmi->lock); - - return r; -} - static void hdmi_start_audio_stream(struct omap_hdmi *hd) { hdmi_wp_audio_enable(&hd->wp, true); @@ -312,45 +273,21 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd) hdmi_wp_audio_enable(&hd->wp, false); } -static int hdmi_display_enable(struct omap_dss_device *dssdev) +int hdmi4_core_enable(struct hdmi_core_data *core) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - unsigned long flags; + struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); int r = 0; - DSSDBG("ENTER hdmi_display_enable\n"); + DSSDBG("ENTER omapdss_hdmi4_core_enable\n"); mutex_lock(&hdmi->lock); - if (!dssdev->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - r = -ENODEV; - goto err0; - } - - r = hdmi_power_on_full(hdmi); + r = hdmi_power_on_core(hdmi); if (r) { DSSERR("failed to power on device\n"); goto err0; } - if (hdmi->audio_configured) { - r = hdmi4_audio_config(&hdmi->core, &hdmi->wp, - &hdmi->audio_config, - hdmi->cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); - hdmi->audio_abort_cb(&hdmi->pdev->dev); - hdmi->audio_configured = false; - } - } - - spin_lock_irqsave(&hdmi->audio_playing_lock, flags); - if (hdmi->audio_configured && hdmi->audio_playing) - hdmi_start_audio_stream(hdmi); - hdmi->display_enabled = true; - spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - mutex_unlock(&hdmi->lock); return 0; @@ -359,86 +296,152 @@ err0: return r; } -static void hdmi_display_disable(struct omap_dss_device *dssdev) +void hdmi4_core_disable(struct hdmi_core_data *core) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - unsigned long flags; + struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); - DSSDBG("Enter hdmi_display_disable\n"); + DSSDBG("Enter omapdss_hdmi4_core_disable\n"); mutex_lock(&hdmi->lock); - spin_lock_irqsave(&hdmi->audio_playing_lock, flags); - hdmi_stop_audio_stream(hdmi); - hdmi->display_enabled = false; - spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - - hdmi_power_off_full(hdmi); + hdmi_power_off_core(hdmi); mutex_unlock(&hdmi->lock); } -int hdmi4_core_enable(struct hdmi_core_data *core) +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int hdmi4_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) { - struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); - int r = 0; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - DSSDBG("ENTER omapdss_hdmi4_core_enable\n"); + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + return drm_bridge_attach(encoder, hdmi->output.next_bridge, + bridge, flags); +} + +static void hdmi4_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); mutex_lock(&hdmi->lock); - r = hdmi_power_on_core(hdmi); - if (r) { - DSSERR("failed to power on device\n"); - goto err0; - } + drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm); - mutex_unlock(&hdmi->lock); - return 0; + dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000); -err0: mutex_unlock(&hdmi->lock); - return r; } -void hdmi4_core_disable(struct hdmi_core_data *core) +static void hdmi4_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_crtc_state *crtc_state; + unsigned long flags; + int ret; + + /* + * None of these should fail, as the bridge can't be enabled without a + * valid CRTC to connector path with fully populated new states. + */ + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; - DSSDBG("Enter omapdss_hdmi4_core_disable\n"); + hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi + ? HDMI_HDMI : HDMI_DVI; + + if (connector->display_info.is_hdmi) { + const struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi; + + mode = &crtc_state->adjusted_mode; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, + mode); + if (ret == 0) + hdmi->cfg.infoframe = avi; + } mutex_lock(&hdmi->lock); - hdmi_power_off_core(hdmi); + ret = hdmi_power_on_full(hdmi); + if (ret) { + DSSERR("failed to power on device\n"); + goto done; + } + if (hdmi->audio_configured) { + ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp, + &hdmi->audio_config, + hdmi->cfg.vm.pixelclock); + if (ret) { + DSSERR("Error restoring audio configuration: %d", ret); + hdmi->audio_abort_cb(&hdmi->pdev->dev); + hdmi->audio_configured = false; + } + } + + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + if (hdmi->audio_configured && hdmi->audio_playing) + hdmi_start_audio_stream(hdmi); + hdmi->display_enabled = true; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + +done: mutex_unlock(&hdmi->lock); } -static int hdmi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) +static void hdmi4_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - int r; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + unsigned long flags; - r = omapdss_device_connect(dst->dss, dst, dst->next); - if (r) - return r; + mutex_lock(&hdmi->lock); - dst->dispc_channel_connected = true; - return 0; + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + hdmi_stop_audio_stream(hdmi); + hdmi->display_enabled = false; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + + hdmi_power_off_full(hdmi); + + mutex_unlock(&hdmi->lock); } -static void hdmi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) +static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) { - dst->dispc_channel_connected = false; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - omapdss_device_disconnect(dst, dst->next); + if (status == connector_status_disconnected) + hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); } -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) +static const struct drm_edid *hdmi4_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + const struct drm_edid *drm_edid = NULL; + unsigned int cec_addr; bool need_enable; int r; @@ -447,63 +450,72 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, if (need_enable) { r = hdmi4_core_enable(&hdmi->core); if (r) - return r; + return NULL; } - r = read_edid(hdmi, edid, len); - if (r >= 256) - hdmi4_cec_set_phys_addr(&hdmi->core, - cec_get_edid_phys_addr(edid, r, NULL)); - else - hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); - if (need_enable) - hdmi4_core_disable(&hdmi->core); + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); - return r; -} + r = hdmi4_core_ddc_init(&hdmi->core); + if (r) + goto done; -static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + drm_edid = drm_edid_read_custom(connector, hdmi4_core_ddc_read, &hdmi->core); - hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); -} +done: + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + if (drm_edid) { + /* + * FIXME: The CEC physical address should be set using + * hdmi4_cec_set_phys_addr(&hdmi->core, + * connector->display_info.source_physical_address) from a path + * that has read the EDID and called + * drm_edid_connector_update(). + */ + const struct edid *edid = drm_edid_raw(drm_edid); + unsigned int len = (edid->extensions + 1) * EDID_LENGTH; - hdmi->cfg.infoframe = *avi; - return 0; -} + cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL); + } else { + cec_addr = CEC_PHYS_ADDR_INVALID; + } -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); - hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; -} + if (need_enable) + hdmi4_core_disable(&hdmi->core); -static const struct omap_dss_device_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, + return drm_edid; +} - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, +static const struct drm_bridge_funcs hdmi4_bridge_funcs = { + .attach = hdmi4_bridge_attach, + .mode_set = hdmi4_bridge_mode_set, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = hdmi4_bridge_enable, + .atomic_disable = hdmi4_bridge_disable, + .hpd_notify = hdmi4_bridge_hpd_notify, + .edid_read = hdmi4_bridge_edid_read, +}; - .set_timings = hdmi_display_set_timings, +static void hdmi4_bridge_init(struct omap_hdmi *hdmi) +{ + hdmi->bridge.of_node = hdmi->pdev->dev.of_node; + hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; - .read_edid = hdmi_read_edid, + drm_bridge_add(&hdmi->bridge); +} - .hdmi = { - .lost_hotplug = hdmi_lost_hotplug, - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, - }, -}; +static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi) +{ + drm_bridge_remove(&hdmi->bridge); +} /* ----------------------------------------------------------------------------- * Audio Callbacks @@ -696,27 +708,18 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) struct omap_dss_device *out = &hdmi->output; int r; + hdmi4_bridge_init(hdmi); + out->dev = &hdmi->pdev->dev; out->id = OMAP_DSS_OUTPUT_HDMI; - out->output_type = OMAP_DISPLAY_TYPE_HDMI; + out->type = OMAP_DISPLAY_TYPE_HDMI; out->name = "hdmi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops = &hdmi_ops; - out->owner = THIS_MODULE; - out->of_ports = BIT(0); - out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; - - out->next = omapdss_of_find_connected_device(out->dev->of_node, 0); - if (IS_ERR(out->next)) { - if (PTR_ERR(out->next) != -EPROBE_DEFER) - dev_err(out->dev, "failed to find video sink\n"); - return PTR_ERR(out->next); - } + out->of_port = 0; - r = omapdss_output_validate(out); - if (r) { - omapdss_device_put(out->next); - out->next = NULL; + r = omapdss_device_init_output(out, &hdmi->bridge); + if (r < 0) { + hdmi4_bridge_cleanup(hdmi); return r; } @@ -729,9 +732,10 @@ static void hdmi4_uninit_output(struct omap_hdmi *hdmi) { struct omap_dss_device *out = &hdmi->output; - if (out->next) - omapdss_device_put(out->next); omapdss_device_unregister(out); + omapdss_device_cleanup_output(out); + + hdmi4_bridge_cleanup(hdmi); } static int hdmi4_probe_of(struct omap_hdmi *hdmi) @@ -756,9 +760,9 @@ static int hdmi4_probe(struct platform_device *pdev) int irq; int r; - hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi4_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->pdev = pdev; @@ -769,25 +773,24 @@ static int hdmi4_probe(struct platform_device *pdev) r = hdmi4_probe_of(hdmi); if (r) - goto err_free; + return r; r = hdmi_wp_init(pdev, &hdmi->wp, 4); if (r) - goto err_free; + return r; r = hdmi_phy_init(pdev, &hdmi->phy, 4); if (r) - goto err_free; + return r; r = hdmi4_core_init(pdev, &hdmi->core); if (r) - goto err_free; + return r; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_free; + return -ENODEV; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -795,7 +798,7 @@ static int hdmi4_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", hdmi); if (r) { DSSERR("HDMI IRQ request failed\n"); - goto err_free; + return r; } hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); @@ -803,7 +806,7 @@ static int hdmi4_probe(struct platform_device *pdev) r = PTR_ERR(hdmi->vdda_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA regulator\n"); - goto err_free; + return r; } pm_runtime_enable(&pdev->dev); @@ -822,12 +825,10 @@ err_uninit_output: hdmi4_uninit_output(hdmi); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(hdmi); return r; } -static int hdmi4_remove(struct platform_device *pdev) +static void hdmi4_remove(struct platform_device *pdev) { struct omap_hdmi *hdmi = platform_get_drvdata(pdev); @@ -836,9 +837,6 @@ static int hdmi4_remove(struct platform_device *pdev) hdmi4_uninit_output(hdmi); pm_runtime_disable(&pdev->dev); - - kfree(hdmi); - return 0; } static const struct of_device_id hdmi_of_match[] = { |
