diff options
Diffstat (limited to 'drivers/gpu/drm/sti/sti_hdmi.c')
| -rw-r--r-- | drivers/gpu/drm/sti/sti_hdmi.c | 191 |
1 files changed, 104 insertions, 87 deletions
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index ccf718404a1c..f8222e60b1e0 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -8,15 +8,20 @@ #include <linux/component.h> #include <linux/debugfs.h> #include <linux/hdmi.h> +#include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_gpio.h> +#include <linux/io.h> #include <linux/platform_device.h> #include <linux/reset.h> -#include <drm/drmP.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_drv.h> #include <drm/drm_edid.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> #include <sound/hdmi-codec.h> @@ -163,6 +168,17 @@ struct sti_hdmi_connector { #define to_sti_hdmi_connector(x) \ container_of(x, struct sti_hdmi_connector, drm_connector) +static struct sti_hdmi *drm_bridge_to_sti_hdmi(struct drm_bridge *bridge) +{ + return container_of(bridge, struct sti_hdmi, bridge); +} + +static const struct drm_prop_enum_list colorspace_mode_names[] = { + { HDMI_COLORSPACE_RGB, "rgb" }, + { HDMI_COLORSPACE_YUV422, "yuv422" }, + { HDMI_COLORSPACE_YUV444, "yuv444" }, +}; + u32 hdmi_read(struct sti_hdmi *hdmi, int offset) { return readl(hdmi->regs + offset); @@ -173,7 +189,7 @@ void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset) writel(val, hdmi->regs + offset); } -/** +/* * HDMI interrupt handler threaded * * @irq: irq number @@ -205,7 +221,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) return IRQ_HANDLED; } -/** +/* * HDMI interrupt handler * * @irq: irq number @@ -227,7 +243,7 @@ static irqreturn_t hdmi_irq(int irq, void *arg) return IRQ_WAKE_THREAD; } -/** +/* * Set hdmi active area depending on the drm display mode selected * * @hdmi: pointer on the hdmi internal structure @@ -248,13 +264,14 @@ static void hdmi_active_area(struct sti_hdmi *hdmi) hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX); } -/** +/* * Overall hdmi configuration * * @hdmi: pointer on the hdmi internal structure */ static void hdmi_config(struct sti_hdmi *hdmi) { + struct drm_connector *connector = hdmi->drm_connector; u32 conf; DRM_DEBUG_DRIVER("\n"); @@ -264,7 +281,7 @@ static void hdmi_config(struct sti_hdmi *hdmi) /* Select encryption type and the framing mode */ conf |= HDMI_CFG_ESS_NOT_OESS; - if (hdmi->hdmi_monitor) + if (connector->display_info.is_hdmi) conf |= HDMI_CFG_HDMI_NOT_DVI; /* Set Hsync polarity */ @@ -326,11 +343,10 @@ static void hdmi_infoframe_reset(struct sti_hdmi *hdmi, hdmi_write(hdmi, 0x0, pack_offset + i); } -/** +/* * Helper to concatenate infoframe in 32 bits word * * @ptr: pointer on the hdmi internal structure - * @data: infoframe to write * @size: size to write */ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size) @@ -344,7 +360,7 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size) return value; } -/** +/* * Helper to write info frame * * @hdmi: pointer on the hdmi internal structure @@ -414,7 +430,7 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, hdmi_write(hdmi, val, HDMI_SW_DI_CFG); } -/** +/* * Prepare and configure the AVI infoframe * * AVI infoframe are transmitted at least once per two video field and @@ -434,7 +450,8 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) DRM_DEBUG_DRIVER("\n"); - ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, mode, false); + ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, + hdmi->drm_connector, mode); if (ret < 0) { DRM_ERROR("failed to setup AVI infoframe: %d\n", ret); return ret; @@ -456,7 +473,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) return 0; } -/** +/* * Prepare and configure the AUDIO infoframe * * AUDIO infoframe are transmitted once per frame and @@ -539,13 +556,14 @@ static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi) return 0; } -/** +#define HDMI_TIMEOUT_SWRESET 100 /*milliseconds */ + +/* * Software reset of the hdmi subsystem * * @hdmi: pointer on the hdmi internal structure * */ -#define HDMI_TIMEOUT_SWRESET 100 /*milliseconds */ static void hdmi_swreset(struct sti_hdmi *hdmi) { u32 val; @@ -722,21 +740,21 @@ static struct drm_info_list hdmi_debugfs_files[] = { { "hdmi", hdmi_dbg_show, 0, NULL }, }; -static int hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor) +static void hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor) { unsigned int i; for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_files); i++) hdmi_debugfs_files[i].data = hdmi; - return drm_debugfs_create_files(hdmi_debugfs_files, - ARRAY_SIZE(hdmi_debugfs_files), - minor->debugfs_root, minor); + drm_debugfs_create_files(hdmi_debugfs_files, + ARRAY_SIZE(hdmi_debugfs_files), + minor->debugfs_root, minor); } static void sti_hdmi_disable(struct drm_bridge *bridge) { - struct sti_hdmi *hdmi = bridge->driver_private; + struct sti_hdmi *hdmi = drm_bridge_to_sti_hdmi(bridge); u32 val = hdmi_read(hdmi, HDMI_CFG); @@ -774,7 +792,7 @@ static void sti_hdmi_disable(struct drm_bridge *bridge) cec_notifier_set_phys_addr(hdmi->notifier, CEC_PHYS_ADDR_INVALID); } -/** +/* * sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent * clocks. None-coherent clocks means that audio and TMDS clocks have not the * same source (drifts between clocks). In this case assumption is that CTS is @@ -845,10 +863,13 @@ static int hdmi_audio_configure(struct sti_hdmi *hdmi) switch (info->channels) { case 8: audio_cfg |= HDMI_AUD_CFG_CH78_VALID; + fallthrough; case 6: audio_cfg |= HDMI_AUD_CFG_CH56_VALID; + fallthrough; case 4: audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH; + fallthrough; case 2: audio_cfg |= HDMI_AUD_CFG_CH12_VALID; break; @@ -865,7 +886,7 @@ static int hdmi_audio_configure(struct sti_hdmi *hdmi) static void sti_hdmi_pre_enable(struct drm_bridge *bridge) { - struct sti_hdmi *hdmi = bridge->driver_private; + struct sti_hdmi *hdmi = drm_bridge_to_sti_hdmi(bridge); DRM_DEBUG_DRIVER("\n"); @@ -878,7 +899,7 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) if (clk_prepare_enable(hdmi->clk_tmds)) DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n"); if (clk_prepare_enable(hdmi->clk_phy)) - DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n"); + DRM_ERROR("Failed to prepare/enable hdmi_rejection_pll clk\n"); hdmi->enabled = true; @@ -917,16 +938,16 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) } static void sti_hdmi_set_mode(struct drm_bridge *bridge, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { - struct sti_hdmi *hdmi = bridge->driver_private; + struct sti_hdmi *hdmi = drm_bridge_to_sti_hdmi(bridge); int ret; DRM_DEBUG_DRIVER("\n"); /* Copy the drm display mode in the connector local structure */ - memcpy(&hdmi->mode, mode, sizeof(struct drm_display_mode)); + drm_mode_copy(&hdmi->mode, mode); /* Update clock framerate according to the selected mode */ ret = clk_set_rate(hdmi->clk_pix, mode->clock * 1000); @@ -958,28 +979,32 @@ static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = { static int sti_hdmi_connector_get_modes(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; - struct edid *edid; + const struct drm_edid *drm_edid; int count; DRM_DEBUG_DRIVER("\n"); - edid = drm_get_edid(connector, hdmi->ddc_adapt); - if (!edid) + drm_edid = drm_edid_read(connector); + + drm_edid_connector_update(connector, drm_edid); + + cec_notifier_set_phys_addr(hdmi->notifier, + connector->display_info.source_physical_address); + + if (!drm_edid) goto fail; - hdmi->hdmi_monitor = drm_detect_hdmi_monitor(edid); - DRM_DEBUG_KMS("%s : %dx%d cm\n", - (hdmi->hdmi_monitor ? "hdmi monitor" : "dvi monitor"), - edid->width_cm, edid->height_cm); - cec_notifier_set_phys_addr_from_edid(hdmi->notifier, edid); + count = drm_edid_connector_add_modes(connector); - count = drm_add_edid_modes(connector, edid); - drm_connector_update_edid_property(connector, edid); + DRM_DEBUG_KMS("%s : %dx%d cm\n", + info->is_hdmi ? "hdmi monitor" : "dvi monitor", + info->width_mm / 10, info->height_mm / 10); - kfree(edid); + drm_edid_free(drm_edid); return count; fail: @@ -989,8 +1014,9 @@ fail: #define CLK_TOLERANCE_HZ 50 -static int sti_hdmi_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) +static enum drm_mode_status +sti_hdmi_connector_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode) { int target = mode->clock * 1000; int target_min = target - CLK_TOLERANCE_HZ; @@ -1105,10 +1131,7 @@ static int sti_hdmi_late_register(struct drm_connector *connector) = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; - if (hdmi_debugfs_init(hdmi, hdmi->drm_dev->primary)) { - DRM_ERROR("HDMI debugfs setup failed\n"); - return -EINVAL; - } + hdmi_debugfs_init(hdmi, hdmi->drm_dev->primary); return 0; } @@ -1164,12 +1187,12 @@ static int hdmi_audio_hw_params(struct device *dev, DRM_DEBUG_DRIVER("\n"); if ((daifmt->fmt != HDMI_I2S) || daifmt->bit_clk_inv || - daifmt->frame_clk_inv || daifmt->bit_clk_master || - daifmt->frame_clk_master) { + daifmt->frame_clk_inv || daifmt->bit_clk_provider || + daifmt->frame_clk_provider) { dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__, daifmt->bit_clk_inv, daifmt->frame_clk_inv, - daifmt->bit_clk_master, - daifmt->frame_clk_master); + daifmt->bit_clk_provider, + daifmt->frame_clk_provider); return -EINVAL; } @@ -1186,7 +1209,8 @@ static int hdmi_audio_hw_params(struct device *dev, return 0; } -static int hdmi_audio_digital_mute(struct device *dev, void *data, bool enable) +static int hdmi_audio_mute(struct device *dev, void *data, + bool enable, int direction) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); @@ -1206,7 +1230,9 @@ static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size struct drm_connector *connector = hdmi->drm_connector; DRM_DEBUG_DRIVER("\n"); + mutex_lock(&connector->eld_mutex); memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); + mutex_unlock(&connector->eld_mutex); return 0; } @@ -1214,7 +1240,7 @@ static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size static const struct hdmi_codec_ops audio_codec_ops = { .hw_params = hdmi_audio_hw_params, .audio_shutdown = hdmi_audio_shutdown, - .digital_mute = hdmi_audio_digital_mute, + .mute_stream = hdmi_audio_mute, .get_eld = hdmi_audio_get_eld, }; @@ -1225,6 +1251,7 @@ static int sti_hdmi_register_audio_driver(struct device *dev, .ops = &audio_codec_ops, .max_i2s_channels = 8, .i2s = 1, + .no_capture_mute = 1, }; DRM_DEBUG_DRIVER("\n"); @@ -1249,8 +1276,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) struct drm_device *drm_dev = data; struct drm_encoder *encoder; struct sti_hdmi_connector *connector; + struct cec_connector_info conn_info; struct drm_connector *drm_connector; - struct drm_bridge *bridge; int err; /* Set the drm device handle */ @@ -1266,13 +1293,7 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) connector->hdmi = hdmi; - bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return -EINVAL; - - bridge->driver_private = hdmi; - bridge->funcs = &sti_hdmi_bridge_funcs; - drm_bridge_attach(encoder, bridge, NULL); + drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); connector->encoder = encoder; @@ -1280,8 +1301,10 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) drm_connector->polled = DRM_CONNECTOR_POLL_HPD; - drm_connector_init(drm_dev, drm_connector, - &sti_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + drm_connector_init_with_ddc(drm_dev, drm_connector, + &sti_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->ddc_adapt); drm_connector_helper_add(drm_connector, &sti_hdmi_connector_helper_funcs); @@ -1309,6 +1332,14 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) goto err_sysfs; } + cec_fill_conn_info_from_drm(&conn_info, drm_connector); + hdmi->notifier = cec_notifier_conn_register(&hdmi->dev, NULL, + &conn_info); + if (!hdmi->notifier) { + hdmi->drm_connector = NULL; + return -ENOMEM; + } + /* Enable default interrupts */ hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); @@ -1322,6 +1353,9 @@ err_sysfs: static void sti_hdmi_unbind(struct device *dev, struct device *master, void *data) { + struct sti_hdmi *hdmi = dev_get_drvdata(dev); + + cec_notifier_conn_unregister(hdmi->notifier); } static const struct component_ops sti_hdmi_ops = { @@ -1344,15 +1378,14 @@ static int sti_hdmi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sti_hdmi *hdmi; struct device_node *np = dev->of_node; - struct resource *res; struct device_node *ddc; int ret; DRM_INFO("%s\n", __func__); - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(dev, struct sti_hdmi, bridge, &sti_hdmi_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); ddc = of_parse_phandle(pdev->dev.of_node, "ddc", 0); if (ddc) { @@ -1363,17 +1396,9 @@ static int sti_hdmi_probe(struct platform_device *pdev) } hdmi->dev = pdev->dev; - - /* Get resources */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi-reg"); - if (!res) { - DRM_ERROR("Invalid hdmi resource\n"); - ret = -ENOMEM; - goto release_adapter; - } - hdmi->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); - if (!hdmi->regs) { - ret = -ENOMEM; + hdmi->regs = devm_platform_ioremap_resource_byname(pdev, "hdmi-reg"); + if (IS_ERR(hdmi->regs)) { + ret = PTR_ERR(hdmi->regs); goto release_adapter; } @@ -1427,10 +1452,6 @@ static int sti_hdmi_probe(struct platform_device *pdev) goto release_adapter; } - hdmi->notifier = cec_notifier_get(&pdev->dev); - if (!hdmi->notifier) - goto release_adapter; - hdmi->reset = devm_reset_control_get(dev, "hdmi"); /* Take hdmi out of reset */ if (!IS_ERR(hdmi->reset)) @@ -1438,6 +1459,7 @@ static int sti_hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hdmi); + drm_bridge_add(&hdmi->bridge); return component_add(&pdev->dev, &sti_hdmi_ops); release_adapter: @@ -1446,25 +1468,20 @@ static int sti_hdmi_probe(struct platform_device *pdev) return ret; } -static int sti_hdmi_remove(struct platform_device *pdev) +static void sti_hdmi_remove(struct platform_device *pdev) { struct sti_hdmi *hdmi = dev_get_drvdata(&pdev->dev); - cec_notifier_set_phys_addr(hdmi->notifier, CEC_PHYS_ADDR_INVALID); - i2c_put_adapter(hdmi->ddc_adapt); if (hdmi->audio_pdev) platform_device_unregister(hdmi->audio_pdev); component_del(&pdev->dev, &sti_hdmi_ops); - - cec_notifier_put(hdmi->notifier); - return 0; + drm_bridge_remove(&hdmi->bridge); } struct platform_driver sti_hdmi_driver = { .driver = { .name = "sti-hdmi", - .owner = THIS_MODULE, .of_match_table = hdmi_of_match, }, .probe = sti_hdmi_probe, |
