From d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:45:28 +0000 Subject: [PATCH] add boot partition size --- kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1614 +++++++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 1,065 insertions(+), 549 deletions(-) diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 7ba60fb..f395a69 100644 --- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1,49 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * DesignWare High-Definition Multimedia Interface (HDMI) driver * * Copyright (C) 2013-2015 Mentor Graphics Inc. * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ -#include <linux/module.h> -#include <linux/irq.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/extcon.h> #include <linux/extcon-provider.h> -#include <linux/clk.h> #include <linux/hdmi.h> +#include <linux/irq.h> +#include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> #include <linux/regmap.h> +#include <linux/dma-mapping.h> #include <linux/spinlock.h> #include <linux/pinctrl/consumer.h> -#include <drm/drm_of.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 <drm/drm_scdc_helper.h> -#include <drm/bridge/dw_hdmi.h> +#include <media/cec-notifier.h> #include <uapi/linux/media-bus-format.h> #include <uapi/linux/videodev2.h> -#include "dw-hdmi.h" +#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_scdc_helper.h> + #include "dw-hdmi-audio.h" #include "dw-hdmi-cec.h" #include "dw-hdmi-hdcp.h" - -#include <media/cec-notifier.h> +#include "dw-hdmi.h" #define DDC_CI_ADDR 0x37 #define DDC_SEGMENT_ADDR 0x30 @@ -150,56 +147,20 @@ static const u16 csc_coeff_rgb_in_eitu601[3][4] = { { 0x2591, 0x1322, 0x074b, 0x0000 }, - { 0xe535, 0x2000, 0xfacc, 0x0200 }, - { 0xeacd, 0xf534, 0x2000, 0x0200 } -}; - -static const u16 csc_coeff_rgb_in_eitu601_10bit[3][4] = { - { 0x2591, 0x1322, 0x074b, 0x0000 }, - { 0xe535, 0x2000, 0xfacc, 0x0800 }, - { 0xeacd, 0xf534, 0x2000, 0x0800 } -}; - -static const u16 csc_coeff_rgb_in_eitu601_limited[3][4] = { - { 0x2044, 0x106f, 0x0644, 0x0040 }, - { 0xe677, 0x1c1c, 0xfd46, 0x0200 }, - { 0xed60, 0xf685, 0x1c1c, 0x0200 } -}; - -static const u16 csc_coeff_rgb_in_eitu601_10bit_limited[3][4] = { - { 0x2044, 0x106f, 0x0644, 0x0100 }, - { 0xe677, 0x1c1c, 0xfd46, 0x0800 }, - { 0xed60, 0xf685, 0x1c1c, 0x0800 } + { 0x6535, 0x2000, 0x7acc, 0x0200 }, + { 0x6acd, 0x7534, 0x2000, 0x0200 } }; static const u16 csc_coeff_rgb_in_eitu709[3][4] = { { 0x2dc5, 0x0d9b, 0x049e, 0x0000 }, - { 0xe2f0, 0x2000, 0xfd11, 0x0200 }, - { 0xe756, 0xf8ab, 0x2000, 0x0200 } + { 0x62f0, 0x2000, 0x7d11, 0x0200 }, + { 0x6756, 0x78ab, 0x2000, 0x0200 } }; -static const u16 csc_coeff_rgb_in_eitu709_10bit[3][4] = { - { 0x2dc5, 0x0d9b, 0x049e, 0x0000 }, - { 0xe2f0, 0x2000, 0xfd11, 0x0800 }, - { 0xe756, 0xf8ab, 0x2000, 0x0800 } -}; - -static const u16 csc_coeff_rgb_in_eitu709_limited[3][4] = { - { 0x2750, 0x0baf, 0x03f8, 0x0040 }, - { 0xe677, 0x1c1c, 0xfd6d, 0x0200 }, - { 0xea55, 0xf98f, 0x1c1c, 0x0200 } -}; - -static const u16 csc_coeff_rgb_in_eitu709_10bit_limited[3][4] = { - { 0x2750, 0x0baf, 0x03f8, 0x0100 }, - { 0xe677, 0x1c1c, 0xfd6d, 0x0800 }, - { 0xea55, 0xf98f, 0x1c1c, 0x0800 } -}; - -static const u16 csc_coeff_full_to_limited[3][4] = { - { 0x36f7, 0x0000, 0x0000, 0x0040 }, - { 0x0000, 0x36f7, 0x0000, 0x0040 }, - { 0x0000, 0x0000, 0x36f7, 0x0040 } +static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = { + { 0x1b7c, 0x0000, 0x0000, 0x0020 }, + { 0x0000, 0x1b7c, 0x0000, 0x0020 }, + { 0x0000, 0x0000, 0x1b7c, 0x0020 } }; static const struct drm_display_mode dw_hdmi_default_modes[] = { @@ -207,36 +168,32 @@ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, 1430, 1650, 0, 720, 725, 730, 750, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 16 - 1920x1080@60Hz 16:9 */ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 31 - 1920x1080@50Hz 16:9 */ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 19 - 1280x720@50Hz 16:9 */ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, 1760, 1980, 0, 720, 725, 730, 750, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 0x10 - 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 17 - 720x576@50Hz 4:3 */ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, /* 2 - 720x480@60Hz 4:3 */ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, }; struct hdmi_vmode { @@ -258,7 +215,7 @@ unsigned int quant_range; unsigned int pix_repet_factor; struct hdmi_vmode video_mode; - bool update; + bool rgb_limited_range; }; struct dw_hdmi_i2c { @@ -304,6 +261,7 @@ struct hdmi_data_info hdmi_data; const struct dw_hdmi_plat_data *plat_data; + const struct dw_hdmi_cec_wake_ops *cec_ops; struct dw_hdcp *hdcp; int vic; @@ -321,7 +279,6 @@ struct drm_display_mode previous_mode; struct i2c_adapter *ddc; - struct cec_adapter *cec_adap; void __iomem *regs; bool sink_is_hdmi; bool sink_has_audio; @@ -333,8 +290,13 @@ struct delayed_work work; struct workqueue_struct *workqueue; + struct pinctrl *pinctrl; + struct pinctrl_state *default_state; + struct pinctrl_state *unwedge_state; + struct mutex mutex; /* for state below and previous_mode */ enum drm_connector_force force; /* mutex-protected force state */ + struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ bool disabled; /* DRM has disabled our bridge */ bool bridge_is_on; /* indicates the bridge is on */ bool rxsense; /* rxsense state */ @@ -357,14 +319,15 @@ void (*enable_audio)(struct dw_hdmi *hdmi); void (*disable_audio)(struct dw_hdmi *hdmi); + struct mutex cec_notifier_mutex; struct cec_notifier *cec_notifier; + struct cec_adapter *cec_adap; - bool initialized; /* hdmi is enabled before bind */ - bool logo_plug_out; /* hdmi is plug out when kernel logo */ hdmi_codec_plugged_cb plugged_cb; struct device *codec_dev; enum drm_connector_status last_connector_result; - bool rgb_quant_range_selectable; + bool initialized; /* hdmi is enabled before bind */ + bool logo_plug_out; /* hdmi is plug out when kernel logo */ bool update; bool hdr2sdr; /* from hdr to sdr */ }; @@ -446,6 +409,8 @@ static void repo_hpd_event(struct work_struct *p_work) { struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work); + enum drm_connector_status status = hdmi->hpd_state ? + connector_status_connected : connector_status_disconnected; u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0); mutex_lock(&hdmi->mutex); @@ -460,13 +425,12 @@ change = drm_helper_hpd_irq_event(hdmi->bridge.dev); -#ifdef CONFIG_CEC_NOTIFIER - if (change) { - cec_notifier_repo_cec_hpd(hdmi->cec_notifier, - hdmi->hpd_state, - ktime_get()); - } -#endif + if (change && hdmi->cec_adap && + hdmi->cec_adap->devnode.registered) + cec_queue_pin_hpd_event(hdmi->cec_adap, + hdmi->hpd_state, + ktime_get()); + drm_bridge_hpd_notify(&hdmi->bridge, status); } } @@ -544,6 +508,13 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) { + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, + HDMI_PHY_I2CM_INT_ADDR); + + hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + HDMI_PHY_I2CM_CTLINT_ADDR); + /* Software reset */ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ); @@ -570,11 +541,82 @@ dw_hdmi_i2c_set_divs(hdmi); } +static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi) +{ + /* If no unwedge state then give up */ + if (!hdmi->unwedge_state) + return false; + + dev_info(hdmi->dev, "Attempting to unwedge stuck i2c bus\n"); + + /* + * This is a huge hack to workaround a problem where the dw_hdmi i2c + * bus could sometimes get wedged. Once wedged there doesn't appear + * to be any way to unwedge it (including the HDMI_I2CM_SOFTRSTZ) + * other than pulsing the SDA line. + * + * We appear to be able to pulse the SDA line (in the eyes of dw_hdmi) + * by: + * 1. Remux the pin as a GPIO output, driven low. + * 2. Wait a little while. 1 ms seems to work, but we'll do 10. + * 3. Immediately jump to remux the pin as dw_hdmi i2c again. + * + * At the moment of remuxing, the line will still be low due to its + * recent stint as an output, but then it will be pulled high by the + * (presumed) external pullup. dw_hdmi seems to see this as a rising + * edge and that seems to get it out of its jam. + * + * This wedging was only ever seen on one TV, and only on one of + * its HDMI ports. It happened when the TV was powered on while the + * device was plugged in. A scope trace shows the TV bringing both SDA + * and SCL low, then bringing them both back up at roughly the same + * time. Presumably this confuses dw_hdmi because it saw activity but + * no real STOP (maybe it thinks there's another master on the bus?). + * Giving it a clean rising edge of SDA while SCL is already high + * presumably makes dw_hdmi see a STOP which seems to bring dw_hdmi out + * of its stupor. + * + * Note that after coming back alive, transfers seem to immediately + * resume, so if we unwedge due to a timeout we should wait a little + * longer for our transfer to finish, since it might have just started + * now. + */ + pinctrl_select_state(hdmi->pinctrl, hdmi->unwedge_state); + msleep(10); + pinctrl_select_state(hdmi->pinctrl, hdmi->default_state); + + return true; +} + +static int dw_hdmi_i2c_wait(struct dw_hdmi *hdmi) +{ + struct dw_hdmi_i2c *i2c = hdmi->i2c; + int stat; + + stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); + if (!stat) { + /* If we can't unwedge, return timeout */ + if (!dw_hdmi_i2c_unwedge(hdmi)) + return -EAGAIN; + + /* We tried to unwedge; give it another chance */ + stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); + if (!stat) + return -EAGAIN; + } + + /* Check for error condition on the bus */ + if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) + return -EIO; + + return 0; +} + static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, unsigned char *buf, unsigned int length) { struct dw_hdmi_i2c *i2c = hdmi->i2c; - int stat, retry, i; + int ret, retry, i; bool read_edid = false; if (!i2c->is_regaddr) { @@ -608,7 +650,7 @@ hdmi->plat_data->set_ddc_io(data, false); return -EPERM; } - reinit_completion(&i2c->cmp); + if (i2c->is_segment) { if (read_edid) hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8_EXT, @@ -624,17 +666,16 @@ hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, HDMI_I2CM_OPERATION); } - stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); - if (!stat) { + + ret = dw_hdmi_i2c_wait(hdmi); + if (ret == -EAGAIN) { dev_dbg(hdmi->dev, "ddc read time out\n"); hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, HDMI_I2CM_OPERATION); retry -= 10; continue; - } - /* Check for error condition on the bus */ - if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) { + } else if (ret == -EIO) { dev_dbg(hdmi->dev, "ddc read err\n"); hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, @@ -667,7 +708,7 @@ unsigned char *buf, unsigned int length) { struct dw_hdmi_i2c *i2c = hdmi->i2c; - int stat, retry; + int ret, retry; if (!i2c->is_regaddr) { /* Use the first write byte as register address */ @@ -692,22 +733,20 @@ hdmi->plat_data->set_ddc_io(data, false); return -EPERM; } + reinit_completion(&i2c->cmp); hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE, HDMI_I2CM_OPERATION); - stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); - if (!stat) { + ret = dw_hdmi_i2c_wait(hdmi); + if (ret == -EAGAIN) { dev_dbg(hdmi->dev, "ddc write time out\n"); hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, HDMI_I2CM_OPERATION); retry -= 10; continue; - } - - /* Check for error condition on the bus */ - if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) { + } else if (ret == -EIO) { dev_dbg(hdmi->dev, "ddc write err\n"); hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, @@ -866,8 +905,14 @@ /* nshift factor = 0 */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); - hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | - HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + /* Use automatic CTS generation mode when CTS is not set */ + if (cts) + hdmi_writeb(hdmi, ((cts >> 16) & + HDMI_AUD_CTS3_AUDCTS19_16_MASK) | + HDMI_AUD_CTS3_CTS_MANUAL, + HDMI_AUD_CTS3); + else + hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3); hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); @@ -989,29 +1034,58 @@ return hdmi_compute_n(hdmi, pixel_clk, sample_rate); } +/* + * When transmitting IEC60958 linear PCM audio, these registers allow to + * configure the channel status information of all the channel status + * bits in the IEC60958 frame. For the moment this configuration is only + * used when the I2S audio interface, General Purpose Audio (GPA), + * or AHB audio DMA (AHBAUDDMA) interface is active + * (for S/PDIF interface this information comes from the stream). + */ +void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, + u8 *channel_status) +{ + /* + * Set channel status register for frequency and word length. + * Use default values for other registers. + */ + hdmi_writeb(hdmi, channel_status[3], HDMI_FC_AUDSCHNLS7); + hdmi_writeb(hdmi, channel_status[4], HDMI_FC_AUDSCHNLS8); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_status); + static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, unsigned long pixel_clk, unsigned int sample_rate) { unsigned long ftdms = pixel_clk; unsigned int n, cts; + u8 config3; u64 tmp; n = hdmi_find_n(hdmi, pixel_clk, sample_rate); - /* - * Compute the CTS value from the N value. Note that CTS and N - * can be up to 20 bits in total, so we need 64-bit math. Also - * note that our TDMS clock is not fully accurate; it is accurate - * to kHz. This can introduce an unnecessary remainder in the - * calculation below, so we don't try to warn about that. - */ - tmp = (u64)ftdms * n; - do_div(tmp, 128 * sample_rate); - cts = tmp; + config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", - __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, - n, cts); + /* Only compute CTS when using internal AHB audio */ + if (config3 & HDMI_CONFIG3_AHBAUDDMA) { + /* + * Compute the CTS value from the N value. Note that CTS and N + * can be up to 20 bits in total, so we need 64-bit math. Also + * note that our TDMS clock is not fully accurate; it is + * accurate to kHz. This can introduce an unnecessary remainder + * in the calculation below, so we don't try to warn about that. + */ + tmp = (u64)ftdms * n; + do_div(tmp, 128 * sample_rate); + cts = tmp; + + dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", + __func__, sample_rate, + ftdms / 1000000, (ftdms / 1000) % 1000, + n, cts); + } else { + cts = 0; + } spin_lock_irq(&hdmi->audio_lock); hdmi->audio_n = n; @@ -1045,6 +1119,42 @@ } EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate); +void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt) +{ + u8 layout; + + mutex_lock(&hdmi->audio_mutex); + + /* + * For >2 channel PCM audio, we need to select layout 1 + * and set an appropriate channel map. + */ + if (cnt > 2) + layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1; + else + layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0; + + hdmi_modb(hdmi, layout, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK, + HDMI_FC_AUDSCONF); + + /* Set the audio infoframes channel count */ + hdmi_modb(hdmi, (cnt - 1) << HDMI_FC_AUDICONF0_CC_OFFSET, + HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0); + + mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_count); + +void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca) +{ + mutex_lock(&hdmi->audio_mutex); + + hdmi_writeb(hdmi, ca, HDMI_FC_AUDICONF2); + + mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_allocation); + static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable) { if (enable) @@ -1052,6 +1162,14 @@ else hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE; hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); +} + +static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi) +{ + if (!hdmi->curr_conn) + return NULL; + + return hdmi->curr_conn->eld; } static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi) @@ -1262,25 +1380,14 @@ static int is_color_space_conversion(struct dw_hdmi *hdmi) { - const struct drm_display_mode mode = hdmi->previous_mode; - bool is_cea_default; + struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; + bool is_input_rgb, is_output_rgb; - is_cea_default = (drm_match_cea_mode(&mode) > 1) && - (hdmi->hdmi_data.quant_range == - HDMI_QUANTIZATION_RANGE_DEFAULT); + is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_in_bus_format); + is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_out_bus_format); - /* - * When output is rgb limited range or default range with - * cea mode, csc should be enabled. - */ - if (hdmi->hdmi_data.enc_in_bus_format != - hdmi->hdmi_data.enc_out_bus_format || - ((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED || - is_cea_default) && - hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format))) - return 1; - - return 0; + return (is_input_rgb != is_output_rgb) || + (is_input_rgb && is_output_rgb && hdmi_data->rgb_limited_range); } static int is_color_space_decimation(struct dw_hdmi *hdmi) @@ -1307,43 +1414,46 @@ return 0; } +static bool is_csc_needed(struct dw_hdmi *hdmi) +{ + return is_color_space_conversion(hdmi) || + is_color_space_decimation(hdmi) || + is_color_space_interpolation(hdmi); +} + +static bool is_rgb_full_to_limited_needed(struct dw_hdmi *hdmi) +{ + if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED || + (!hdmi->hdmi_data.quant_range && hdmi->hdmi_data.rgb_limited_range)) + return true; + + return false; +} + static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) { const u16 (*csc_coeff)[3][4] = &csc_coeff_default; + bool is_input_rgb, is_output_rgb; unsigned i; u32 csc_scale = 1; - int enc_out_rgb, enc_in_rgb; - int color_depth; - enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format); - enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format); - color_depth = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); + is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format); + is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format); - if (is_color_space_conversion(hdmi)) { - if (enc_out_rgb && enc_in_rgb) { - csc_coeff = &csc_coeff_full_to_limited; - csc_scale = 0; - } else if (enc_out_rgb) { - if (hdmi->hdmi_data.enc_out_encoding == - V4L2_YCBCR_ENC_601) - csc_coeff = &csc_coeff_rgb_out_eitu601; - else - csc_coeff = &csc_coeff_rgb_out_eitu709; - } else if (enc_in_rgb) { - if (hdmi->hdmi_data.enc_out_encoding == - V4L2_YCBCR_ENC_601) { - if (color_depth == 10) - csc_coeff = &csc_coeff_rgb_in_eitu601_10bit_limited; - else - csc_coeff = &csc_coeff_rgb_in_eitu601_limited; - } else { - if (color_depth == 10) - csc_coeff = &csc_coeff_rgb_in_eitu709_10bit_limited; - else - csc_coeff = &csc_coeff_rgb_in_eitu709_limited; - } - csc_scale = 0; - } + if (!is_input_rgb && is_output_rgb) { + if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601) + csc_coeff = &csc_coeff_rgb_out_eitu601; + else + csc_coeff = &csc_coeff_rgb_out_eitu709; + } else if (is_input_rgb && !is_output_rgb) { + if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601) + csc_coeff = &csc_coeff_rgb_in_eitu601; + else + csc_coeff = &csc_coeff_rgb_in_eitu709; + csc_scale = 0; + } else if (is_input_rgb && is_output_rgb && + is_rgb_full_to_limited_needed(hdmi)) { + csc_coeff = &csc_coeff_rgb_full_to_rgb_limited; } /* The CSC registers are sequential, alternating MSB then LSB */ @@ -1551,30 +1661,16 @@ } EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write); -static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr) -{ - int val; - - hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); - hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); - hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR); - hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR); - hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ, - HDMI_PHY_I2CM_OPERATION_ADDR); - hdmi_phy_wait_i2c_done(hdmi, 1000); - val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR); - val = (val & 0xff) << 8; - val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff; - return val; -} - /* Filter out invalid setups to avoid configuring SCDC and scrambling */ -static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi) +static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi, + const struct drm_display_info *display) { - struct drm_display_info *display = &hdmi->connector.display_info; - /* Completely disable SCDC support for older controllers */ if (hdmi->version < 0x200a) + return false; + + /* Disable if no DDC bus */ + if (!hdmi->ddc) return false; /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */ @@ -1593,6 +1689,23 @@ return true; } +static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr) +{ + int val; + + hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); + hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); + hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR); + hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR); + hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ, + HDMI_PHY_I2CM_OPERATION_ADDR); + hdmi_phy_wait_i2c_done(hdmi, 1000); + val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR); + val = (val & 0xff) << 8; + val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff; + return val; +} + /* * HDMI2.0 Specifies the following procedure for High TMDS Bit Rates: * - The Source shall suspend transmission of the TMDS clock and data @@ -1606,12 +1719,13 @@ * helper should called right before enabling the TMDS Clock and Data in * the PHY configuration callback. */ -void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi) +void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi, + const struct drm_display_info *display) { unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ - if (dw_hdmi_support_scdc(hdmi)) { + if (dw_hdmi_support_scdc(hdmi, display)) { if (mtmdsclock > HDMI14_MAX_TMDSCLK) drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1); else @@ -1824,7 +1938,8 @@ return 0; } -static int hdmi_phy_configure(struct dw_hdmi *hdmi) +static int hdmi_phy_configure(struct dw_hdmi *hdmi, + const struct drm_display_info *display) { const struct dw_hdmi_phy_data *phy = hdmi->phy.data; const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; @@ -1834,7 +1949,7 @@ dw_hdmi_phy_power_off(hdmi); - dw_hdmi_set_high_tmds_clock_ratio(hdmi); + dw_hdmi_set_high_tmds_clock_ratio(hdmi, display); /* Leave low power consumption mode by asserting SVSRET. */ if (phy->has_svsret) @@ -1848,7 +1963,7 @@ /* Write to the PHY as configured by the platform */ if (pdata->configure_phy) - ret = pdata->configure_phy(hdmi, pdata, mpixelclock); + ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock); else ret = phy->configure(hdmi, pdata, mpixelclock); if (ret) { @@ -1865,7 +1980,8 @@ } static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, - struct drm_display_mode *mode) + const struct drm_display_info *display, + const struct drm_display_mode *mode) { int i, ret; @@ -1874,7 +1990,7 @@ dw_hdmi_phy_sel_data_en_pol(hdmi, 1); dw_hdmi_phy_sel_interface_control(hdmi, 0); - ret = hdmi_phy_configure(hdmi); + ret = hdmi_phy_configure(hdmi, display); if (ret) return ret; } @@ -1977,23 +2093,35 @@ hdmi->hdcp->hdcp_start(hdmi->hdcp); } -static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) +static void hdmi_config_AVI(struct dw_hdmi *hdmi, + const struct drm_connector *connector, + const struct drm_display_mode *mode) { struct hdmi_avi_infoframe frame; u8 val; - bool is_hdmi2 = false; - enum hdmi_quantization_range rgb_quant_range = - hdmi->hdmi_data.quant_range; + bool is_hdmi2; + const struct drm_display_info *info = &connector->display_info; - if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || - hdmi->connector.display_info.hdmi.scdc.supported) - is_hdmi2 = true; + is_hdmi2 = info->hdmi.scdc.supported || (info->color_formats & DRM_COLOR_FORMAT_YCRCB420); + /* Initialise info frame from DRM mode */ - drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2); + drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); - drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range, - hdmi->rgb_quant_range_selectable || is_hdmi2, - is_hdmi2); + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { + /* default range */ + if (!hdmi->hdmi_data.quant_range) + drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, + hdmi->hdmi_data.rgb_limited_range ? + HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL); + else + drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, + hdmi->hdmi_data.quant_range); + } else { + frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; + frame.ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_LIMITED; + } if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) frame.colorspace = HDMI_COLORSPACE_YUV444; @@ -2029,8 +2157,8 @@ else frame.colorimetry = HDMI_COLORIMETRY_ITU_709; frame.extended_colorimetry = - HDMI_EXTENDED_COLORIMETRY_BT2020; - break; + HDMI_EXTENDED_COLORIMETRY_BT2020; + break; default: /* Carries no data */ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; frame.extended_colorimetry = @@ -2054,8 +2182,6 @@ else frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED; } - - frame.scan_mode = HDMI_SCAN_MODE_NONE; /* * The Designware IP uses a different byte format from standard @@ -2128,7 +2254,8 @@ } static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, - struct drm_display_mode *mode) + const struct drm_connector *connector, + const struct drm_display_mode *mode) { struct hdmi_vendor_infoframe frame; u8 buffer[10]; @@ -2189,6 +2316,77 @@ HDMI_FC_DATAUTO0_VSD_MASK); } +static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi, + const struct drm_connector *connector) +{ + const struct drm_connector_state *conn_state = connector->state; + struct hdr_output_metadata *hdr_metadata; + struct hdmi_drm_infoframe frame; + u8 buffer[30]; + ssize_t err; + int i; + + /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */ + if (hdmi->version < 0x211a) { + DRM_ERROR("Not support DRM Infoframe\n"); + return; + } + + if (!hdmi->plat_data->use_drm_infoframe) + return; + + hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE, + HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN); + + if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { + DRM_DEBUG("No need to set HDR metadata in infoframe\n"); + return; + } + + if (!conn_state->hdr_output_metadata) { + DRM_DEBUG("source metadata not set yet\n"); + return; + } + + hdr_metadata = (struct hdr_output_metadata *) + conn_state->hdr_output_metadata->data; + + if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & + BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { + DRM_ERROR("Not support EOTF %d\n", + hdr_metadata->hdmi_metadata_type1.eotf); + return; + } + + err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); + if (err < 0) + return; + + err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err); + return; + } + + hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0); + hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1); + + for (i = 0; i < frame.length; i++) + hdmi_writeb(hdmi, buffer[4 + i], HDMI_FC_DRM_PB0 + i); + + hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP); + /* + * avi and hdr infoframe cannot be sent at the same time + * for compatibility with Huawei TV + */ + msleep(300); + hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE, + HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN); + + DRM_DEBUG("%s eotf %d end\n", __func__, + hdr_metadata->hdmi_metadata_type1.eotf); +} + static unsigned int hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock) { @@ -2215,114 +2413,12 @@ return tmdsclock; } -#define HDR_LSB(n) ((n) & 0xff) -#define HDR_MSB(n) (((n) & 0xff00) >> 8) - -/* Set Dynamic Range and Mastering Infoframe */ -static void hdmi_config_hdr_infoframe(struct dw_hdmi *hdmi) -{ - struct hdmi_drm_infoframe frame; - struct hdr_output_metadata *hdr_metadata; - struct drm_connector_state *conn_state = hdmi->connector.state; - int ret; - - /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */ - if (hdmi->version < 0x211a) { - DRM_ERROR("Not support DRM Infoframe\n"); - return; - } - - hdmi_modb(hdmi, HDMI_FC_PACKET_DRM_TX_DEN, - HDMI_FC_PACKET_DRM_TX_EN_MASK, HDMI_FC_PACKET_TX_EN); - - if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { - DRM_DEBUG("No need to set HDR metadata in infoframe\n"); - return; - } - - if (!conn_state->hdr_output_metadata) { - DRM_DEBUG("source metadata not set yet\n"); - return; - } - - hdr_metadata = (struct hdr_output_metadata *) - conn_state->hdr_output_metadata->data; - - if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & - BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { - DRM_ERROR("Not support EOTF %d\n", - hdr_metadata->hdmi_metadata_type1.eotf); - return; - } - - ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); - if (ret < 0) { - DRM_ERROR("couldn't set HDR metadata in infoframe\n"); - return; - } - - hdmi_writeb(hdmi, 1, HDMI_FC_DRM_HB0); - hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1); - hdmi_writeb(hdmi, frame.eotf, HDMI_FC_DRM_PB0); - hdmi_writeb(hdmi, frame.metadata_type, HDMI_FC_DRM_PB1); - hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[0].x), - HDMI_FC_DRM_PB2); - hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[0].x), - HDMI_FC_DRM_PB3); - hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[0].y), - HDMI_FC_DRM_PB4); - hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[0].y), - HDMI_FC_DRM_PB5); - hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[1].x), - HDMI_FC_DRM_PB6); - hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[1].x), - HDMI_FC_DRM_PB7); - hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[1].y), - HDMI_FC_DRM_PB8); - hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[1].y), - HDMI_FC_DRM_PB9); - hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[2].x), - HDMI_FC_DRM_PB10); - hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[2].x), - HDMI_FC_DRM_PB11); - hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[2].y), - HDMI_FC_DRM_PB12); - hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[2].y), - HDMI_FC_DRM_PB13); - hdmi_writeb(hdmi, HDR_LSB(frame.white_point.x), HDMI_FC_DRM_PB14); - hdmi_writeb(hdmi, HDR_MSB(frame.white_point.x), HDMI_FC_DRM_PB15); - hdmi_writeb(hdmi, HDR_LSB(frame.white_point.y), HDMI_FC_DRM_PB16); - hdmi_writeb(hdmi, HDR_MSB(frame.white_point.y), HDMI_FC_DRM_PB17); - hdmi_writeb(hdmi, HDR_LSB(frame.max_display_mastering_luminance), - HDMI_FC_DRM_PB18); - hdmi_writeb(hdmi, HDR_MSB(frame.max_display_mastering_luminance), - HDMI_FC_DRM_PB19); - hdmi_writeb(hdmi, HDR_LSB(frame.min_display_mastering_luminance), - HDMI_FC_DRM_PB20); - hdmi_writeb(hdmi, HDR_MSB(frame.min_display_mastering_luminance), - HDMI_FC_DRM_PB21); - hdmi_writeb(hdmi, HDR_LSB(frame.max_cll), HDMI_FC_DRM_PB22); - hdmi_writeb(hdmi, HDR_MSB(frame.max_cll), HDMI_FC_DRM_PB23); - hdmi_writeb(hdmi, HDR_LSB(frame.max_fall), HDMI_FC_DRM_PB24); - hdmi_writeb(hdmi, HDR_MSB(frame.max_fall), HDMI_FC_DRM_PB25); - hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP); - /* - * avi and hdr infoframe cannot be sent at the same time - * for compatibility with Huawei TV - */ - msleep(300); - hdmi_modb(hdmi, HDMI_FC_PACKET_DRM_TX_EN, - HDMI_FC_PACKET_DRM_TX_EN_MASK, HDMI_FC_PACKET_TX_EN); - - DRM_DEBUG("%s eotf %d end\n", __func__, - hdr_metadata->hdmi_metadata_type1.eotf); -} - static void hdmi_av_composer(struct dw_hdmi *hdmi, + const struct drm_display_info *display, const struct drm_display_mode *mode) { u8 inv_val, bytes; - struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi; + const struct drm_hdmi_info *hdmi_info = &display->hdmi; struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; unsigned int vdisplay, hdisplay; @@ -2338,6 +2434,7 @@ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) vmode->mtmdsclock /= 2; + dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock); if (hdmi->update) return; @@ -2408,13 +2505,10 @@ vblank /= 2; v_de_vs /= 2; vsync_len /= 2; - } else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == - DRM_MODE_FLAG_3D_FRAME_PACKING) { - vdisplay += mode->vtotal; } /* Scrambling Control */ - if (dw_hdmi_support_scdc(hdmi)) { + if (dw_hdmi_support_scdc(hdmi, display)) { if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK || (hdmi_info->scdc.scrambling.low_rates && hdmi->scramble_low_rates)) { @@ -2512,25 +2606,26 @@ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); - /* Enable csc path */ - if (is_color_space_conversion(hdmi)) { - hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; - hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); - } - /* Enable pixel repetition path */ if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) { hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE; hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); } - /* Enable color space conversion if needed */ - if (is_color_space_conversion(hdmi)) + /* Enable csc path */ + if (is_csc_needed(hdmi)) { + hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; + hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH, HDMI_MC_FLOWCTRL); - else + } else { + hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CSCCLK_DISABLE; + hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS, HDMI_MC_FLOWCTRL); + } } /* Workaround to clear the overflow condition */ @@ -2553,6 +2648,8 @@ * iteration for others. * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing * the workaround with a single iteration. + * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have + * been identified as needing the workaround with a single iteration. */ switch (hdmi->version) { @@ -2560,9 +2657,11 @@ count = 4; break; case 0x131a: + case 0x132a: case 0x200a: case 0x201a: case 0x211a: + case 0x212a: count = 1; break; default: @@ -2583,7 +2682,7 @@ HDMI_IH_MUTE_FC_STAT2); } -static void dw_hdmi_force_output_pattern(struct dw_hdmi *hdmi, struct drm_display_mode *mode) +static void dw_hdmi_force_output_pattern(struct dw_hdmi *hdmi, const struct drm_display_mode *mode) { /* force output black */ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { @@ -2615,7 +2714,9 @@ } } -static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) +static int dw_hdmi_setup(struct dw_hdmi *hdmi, + const struct drm_connector *connector, + const struct drm_display_mode *mode) { int ret; void *data = hdmi->plat_data->phy_data; @@ -2680,11 +2781,14 @@ else hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; + if (hdmi->plat_data->get_quant_range) hdmi->hdmi_data.quant_range = hdmi->plat_data->get_quant_range(data); - else - hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; + + hdmi->hdmi_data.rgb_limited_range = hdmi->sink_is_hdmi && + drm_default_rgb_quant_range(mode) == + HDMI_QUANTIZATION_RANGE_LIMITED; if (!hdmi->sink_is_hdmi) hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_FULL; @@ -2702,7 +2806,7 @@ dw_hdmi_force_output_pattern(hdmi, mode); /* HDMI Initialization Step B.1 */ - hdmi_av_composer(hdmi, mode); + hdmi_av_composer(hdmi, &connector->display_info, mode); /* HDMI Initialization Step B.3 */ dw_hdmi_enable_video_path(hdmi); @@ -2720,9 +2824,9 @@ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__); /* HDMI Initialization Step F - Configure AVI InfoFrame */ - hdmi_config_AVI(hdmi, mode); - hdmi_config_vendor_specific_infoframe(hdmi, mode); - hdmi_config_hdr_infoframe(hdmi); + hdmi_config_AVI(hdmi, connector, mode); + hdmi_config_vendor_specific_infoframe(hdmi, connector, mode); + hdmi_config_drm_infoframe(hdmi, connector); } else { dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); } @@ -2739,6 +2843,7 @@ hdmi->hdmi_data.video_mode.previous_tmdsclock != hdmi->hdmi_data.video_mode.mtmdsclock) { ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, + &connector->display_info, &hdmi->previous_mode); if (ret) return ret; @@ -2758,16 +2863,6 @@ } return 0; -} - -static void dw_hdmi_setup_i2c(struct dw_hdmi *hdmi) -{ - hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, - HDMI_PHY_I2CM_INT_ADDR); - - hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | - HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, - HDMI_PHY_I2CM_CTLINT_ADDR); } static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) @@ -2824,7 +2919,12 @@ static void dw_hdmi_poweron(struct dw_hdmi *hdmi) { hdmi->bridge_is_on = true; - dw_hdmi_setup(hdmi, &hdmi->previous_mode); + + /* + * The curr_conn field is guaranteed to be valid here, as this function + * is only be called when !hdmi->disabled. + */ + dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); } static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) @@ -2886,11 +2986,8 @@ hdmi->rxsense); } -static enum drm_connector_status -dw_hdmi_connector_detect(struct drm_connector *connector, bool force) +static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi) { - struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, - connector); enum drm_connector_status result; if (!hdmi->force_logo) { @@ -2902,20 +2999,56 @@ } result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + mutex_lock(&hdmi->mutex); + if (result != hdmi->last_connector_result) { + dev_dbg(hdmi->dev, "read_hpd result: %d", result); + handle_plugged_change(hdmi, + result == connector_status_connected); + hdmi->last_connector_result = result; + } + mutex_unlock(&hdmi->mutex); + if (result == connector_status_connected) extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true); else extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); - mutex_lock(&hdmi->mutex); - if (result != hdmi->last_connector_result) { - dev_dbg(hdmi->dev, "read_hpd result: %d", result); - handle_plugged_change(hdmi, - result == connector_status_connected); - hdmi->last_connector_result = result; - } - mutex_unlock(&hdmi->mutex); return result; +} + +static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi, + struct drm_connector *connector) +{ + struct edid *edid; + + if (!hdmi->ddc) + return NULL; + + edid = drm_get_edid(connector, hdmi->ddc); + if (!edid) { + dev_dbg(hdmi->dev, "failed to get edid\n"); + return NULL; + } + + dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", + edid->width_cm, edid->height_cm); + + hdmi->support_hdmi = drm_detect_hdmi_monitor(edid); + hdmi->sink_has_audio = drm_detect_monitor_audio(edid); + + return edid; +} + +/* ----------------------------------------------------------------------------- + * DRM Connector Operations + */ + +static enum drm_connector_status +dw_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + return dw_hdmi_detect(hdmi); } static int @@ -2956,25 +3089,24 @@ struct edid *edid; struct drm_display_mode *mode; struct drm_display_info *info = &connector->display_info; - int i, ret = 0; + void *data = hdmi->plat_data->phy_data; + int i, ret = 0; memset(metedata, 0, sizeof(*metedata)); - if (!hdmi->ddc) - return 0; - - edid = drm_get_edid(connector, hdmi->ddc); +#if 0 + edid = dw_hdmi_get_edid(hdmi, connector); if (edid) { int vic = 0; dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", edid->width_cm, edid->height_cm); - - hdmi->support_hdmi = drm_detect_hdmi_monitor(edid); - hdmi->sink_has_audio = drm_detect_monitor_audio(edid); - hdmi->rgb_quant_range_selectable = drm_rgb_quant_range_selectable(edid); drm_connector_update_edid_property(connector, edid); cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); ret = drm_add_edid_modes(connector, edid); + if (hdmi->plat_data->get_color_changed) + hdmi->plat_data->get_yuv422_format(connector, edid); + if (hdmi->plat_data->get_colorimetry) + hdmi->plat_data->get_colorimetry(data, edid); list_for_each_entry(mode, &connector->probed_modes, head) { vic = drm_match_cea_mode(mode); @@ -2989,10 +3121,9 @@ kfree(edid); } else { +#endif hdmi->support_hdmi = true; hdmi->sink_has_audio = true; - hdmi->rgb_quant_range_selectable = false; - for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { const struct drm_display_mode *ptr = &dw_hdmi_default_modes[i]; @@ -3010,55 +3141,20 @@ info->color_formats = 0; dev_info(hdmi->dev, "failed to get edid\n"); - } +// } dw_hdmi_update_hdr_property(connector); dw_hdmi_check_output_type_changed(hdmi); return ret; } -static int -dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, - struct drm_connector_state *state, - struct drm_property *property, - uint64_t val) +static struct drm_encoder * +dw_hdmi_connector_best_encoder(struct drm_connector *connector) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, - connector); - const struct dw_hdmi_property_ops *ops = - hdmi->plat_data->property_ops; + connector); - if (ops && ops->set_property) - return ops->set_property(connector, state, property, - val, hdmi->plat_data->phy_data); - else - return -EINVAL; -} - -static int -dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, - uint64_t *val) -{ - struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, - connector); - const struct dw_hdmi_property_ops *ops = - hdmi->plat_data->property_ops; - - if (ops && ops->get_property) - return ops->get_property(connector, state, property, - val, hdmi->plat_data->phy_data); - else - return -EINVAL; -} - -static int -dw_hdmi_connector_set_property(struct drm_connector *connector, - struct drm_property *property, uint64_t val) -{ - return dw_hdmi_atomic_connector_set_property(connector, NULL, - property, val); + return hdmi->bridge.encoder; } static bool dw_hdmi_color_changed(struct drm_connector *connector) @@ -3140,22 +3236,6 @@ return false; } -static bool check_hdmi_format_change(struct drm_connector_state *old_state, - struct drm_connector_state *new_state, - struct drm_connector *connector, - struct dw_hdmi *hdmi) -{ - bool hdr_change, color_change; - - hdr_change = check_hdr_color_change(old_state, new_state, hdmi); - color_change = dw_hdmi_color_changed(connector); - - if (hdr_change || color_change) - return true; - - return false; -} - static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, struct drm_atomic_state *state) { @@ -3187,6 +3267,8 @@ if (!vmode->mpixelclock) { u8 val; + hdmi->curr_conn = connector; + if (hdmi->plat_data->get_enc_in_encoding) hdmi->hdmi_data.enc_in_encoding = hdmi->plat_data->get_enc_in_encoding(data); @@ -3210,10 +3292,6 @@ vmode->mtmdsclock /= 2; dw_hdmi_force_output_pattern(hdmi, mode); - - hdmi_clk_regenerator_update_pixel_clock(hdmi); - hdmi_enable_audio_clk(hdmi, hdmi->audio_enable); - drm_scdc_readb(hdmi->ddc, SCDC_TMDS_CONFIG, &val); /* if plug out before hdmi bind, reset hdmi */ @@ -3221,8 +3299,8 @@ hdmi->logo_plug_out = true; } - if (check_hdmi_format_change(old_state, new_state, connector, hdmi) || - hdmi->logo_plug_out) { + if (check_hdr_color_change(old_state, new_state, hdmi) || hdmi->logo_plug_out || + dw_hdmi_color_changed(connector)) { u32 mtmdsclk; if (hdmi->plat_data->update_color_format) @@ -3265,6 +3343,50 @@ return 0; } +static int +dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + const struct dw_hdmi_property_ops *ops = + hdmi->plat_data->property_ops; + + if (ops && ops->set_property) + return ops->set_property(connector, state, property, + val, hdmi->plat_data->phy_data); + else + return -EINVAL; +} + +static int +dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + const struct dw_hdmi_property_ops *ops = + hdmi->plat_data->property_ops; + + if (ops && ops->get_property) + return ops->get_property(connector, state, property, + val, hdmi->plat_data->phy_data); + else + return -EINVAL; +} + +static int +dw_hdmi_connector_set_property(struct drm_connector *connector, + struct drm_property *property, uint64_t val) +{ + return dw_hdmi_atomic_connector_set_property(connector, NULL, + property, val); +} + static void dw_hdmi_connector_atomic_commit(struct drm_connector *connector, struct drm_connector_state *state) { @@ -3272,7 +3394,7 @@ container_of(connector, struct dw_hdmi, connector); if (hdmi->update) { - dw_hdmi_setup(hdmi, &hdmi->previous_mode); + dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); mdelay(50); handle_plugged_change(hdmi, true); hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); @@ -3286,7 +3408,7 @@ return; hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); - dw_hdmi_setup(hdmi, &hdmi->previous_mode); + dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); } EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range); @@ -3302,7 +3424,7 @@ return; hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); - dw_hdmi_setup(hdmi, &hdmi->previous_mode); + dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); } EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type); @@ -3324,7 +3446,11 @@ if (!hdmi->cec) return; - dw_hdmi_hpd_wake_up(hdmi->cec); + if (!hdmi->cec_ops) + return; + + if (hdmi->cec_ops->hpd_wake_up) + hdmi->cec_ops->hpd_wake_up(hdmi->cec); } EXPORT_SYMBOL_GPL(dw_hdmi_set_hpd_wake); @@ -3365,7 +3491,7 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { .get_modes = dw_hdmi_connector_get_modes, - .best_encoder = drm_atomic_helper_best_encoder, + .best_encoder = dw_hdmi_connector_best_encoder, .atomic_check = dw_hdmi_connector_atomic_check, .atomic_commit = dw_hdmi_connector_atomic_commit, }; @@ -3436,7 +3562,7 @@ if (ops && ops->attach_properties) return ops->attach_properties(&hdmi->connector, color, hdmi->version, - hdmi->plat_data->phy_data); + hdmi->plat_data->phy_data, 0); } static void dw_hdmi_destroy_properties(struct dw_hdmi *hdmi) @@ -3449,47 +3575,393 @@ hdmi->plat_data->phy_data); } -static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) +static int dw_hdmi_connector_create(struct dw_hdmi *hdmi) { - struct dw_hdmi *hdmi = bridge->driver_private; - struct drm_encoder *encoder = bridge->encoder; struct drm_connector *connector = &hdmi->connector; - int ret; + struct cec_connector_info conn_info; + struct cec_notifier *notifier; - if (!hdmi->next_bridge) { - connector->interlace_allowed = 1; - connector->polled = DRM_CONNECTOR_POLL_HPD; + if (hdmi->version >= 0x200a) + connector->ycbcr_420_allowed = + hdmi->plat_data->ycbcr_420_allowed; + else + connector->ycbcr_420_allowed = false; - drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); + connector->interlace_allowed = 1; + connector->polled = DRM_CONNECTOR_POLL_HPD; - drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); - drm_connector_attach_encoder(connector, encoder); + drm_connector_init_with_ddc(hdmi->bridge.dev, connector, + &dw_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->ddc); - dw_hdmi_attach_properties(hdmi); + /* + * drm_connector_attach_max_bpc_property() requires the + * connector to have a state. + */ + drm_atomic_helper_connector_reset(connector); - return 0; - } + drm_connector_attach_max_bpc_property(connector, 8, 16); - hdmi->next_bridge->encoder = bridge->encoder; - ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge); - if (ret) { - DRM_ERROR("Failed to attach bridge with dw-hdmi\n"); - return ret; - } + if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe) + drm_object_attach_property(&connector->base, + connector->dev->mode_config.hdr_output_metadata_property, 0); - bridge->next = hdmi->next_bridge; + drm_connector_attach_encoder(connector, hdmi->bridge.encoder); + + dw_hdmi_attach_properties(hdmi); + + cec_fill_conn_info_from_drm(&conn_info, connector); + + notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info); + if (!notifier) + return -ENOMEM; + + mutex_lock(&hdmi->cec_notifier_mutex); + hdmi->cec_notifier = notifier; + mutex_unlock(&hdmi->cec_notifier_mutex); return 0; } +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +/* + * Possible output formats : + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48, + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36, + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30, + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24, + * - MEDIA_BUS_FMT_YUV16_1X48, + * - MEDIA_BUS_FMT_RGB161616_1X48, + * - MEDIA_BUS_FMT_UYVY12_1X24, + * - MEDIA_BUS_FMT_YUV12_1X36, + * - MEDIA_BUS_FMT_RGB121212_1X36, + * - MEDIA_BUS_FMT_UYVY10_1X20, + * - MEDIA_BUS_FMT_YUV10_1X30, + * - MEDIA_BUS_FMT_RGB101010_1X30, + * - MEDIA_BUS_FMT_UYVY8_1X16, + * - MEDIA_BUS_FMT_YUV8_1X24, + * - MEDIA_BUS_FMT_RGB888_1X24, + */ + +/* Can return a maximum of 11 possible output formats for a mode/connector */ +#define MAX_OUTPUT_SEL_FORMATS 11 + +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_display_info *info = &conn->display_info; + struct drm_display_mode *mode = &crtc_state->mode; + u8 max_bpc = conn_state->max_requested_bpc; + bool is_hdmi2_sink = info->hdmi.scdc.supported || + (info->color_formats & DRM_COLOR_FORMAT_YCRCB420); + u32 *output_fmts; + unsigned int i = 0; + + *num_output_fmts = 0; + + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; + + /* If dw-hdmi is the first or only bridge, avoid negociating with ourselves */ + if (list_is_singular(&bridge->encoder->bridge_chain) || + list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) { + *num_output_fmts = 1; + output_fmts[0] = MEDIA_BUS_FMT_FIXED; + + return output_fmts; + } + + /* + * If the current mode enforces 4:2:0, force the output but format + * to 4:2:0 and do not add the YUV422/444/RGB formats + */ + if (conn->ycbcr_420_allowed && + (drm_mode_is_420_only(info, mode) || + (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) { + + /* Order bus formats from 16bit to 8bit if supported */ + if (max_bpc >= 16 && info->bpc == 16 && + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)) + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48; + + if (max_bpc >= 12 && info->bpc >= 12 && + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)) + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36; + + if (max_bpc >= 10 && info->bpc >= 10 && + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)) + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; + + /* Default 8bit fallback */ + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + + *num_output_fmts = i; + + return output_fmts; + } + + /* + * Order bus formats from 16bit to 8bit and from YUV422 to RGB + * if supported. In any case the default RGB888 format is added + */ + + /* Default 8bit RGB fallback */ + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + + if (max_bpc >= 16 && info->bpc == 16) { + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + } + + if (max_bpc >= 12 && info->bpc >= 12) { + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + } + + if (max_bpc >= 10 && info->bpc >= 10) { + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + } + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + + *num_output_fmts = i; + + return output_fmts; +} + +/* + * Possible input formats : + * - MEDIA_BUS_FMT_RGB888_1X24 + * - MEDIA_BUS_FMT_YUV8_1X24 + * - MEDIA_BUS_FMT_UYVY8_1X16 + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24 + * - MEDIA_BUS_FMT_RGB101010_1X30 + * - MEDIA_BUS_FMT_YUV10_1X30 + * - MEDIA_BUS_FMT_UYVY10_1X20 + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30 + * - MEDIA_BUS_FMT_RGB121212_1X36 + * - MEDIA_BUS_FMT_YUV12_1X36 + * - MEDIA_BUS_FMT_UYVY12_1X24 + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36 + * - MEDIA_BUS_FMT_RGB161616_1X48 + * - MEDIA_BUS_FMT_YUV16_1X48 + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48 + */ + +/* Can return a maximum of 3 possible input formats for an output format */ +#define MAX_INPUT_SEL_FORMATS 3 + +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + unsigned int i = 0; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + switch (output_fmt) { + /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */ + case MEDIA_BUS_FMT_FIXED: + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + break; + /* 8bit */ + case MEDIA_BUS_FMT_RGB888_1X24: + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + break; + case MEDIA_BUS_FMT_YUV8_1X24: + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + break; + + /* 10bit */ + case MEDIA_BUS_FMT_RGB101010_1X30: + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + break; + case MEDIA_BUS_FMT_YUV10_1X30: + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + break; + + /* 12bit */ + case MEDIA_BUS_FMT_RGB121212_1X36: + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + break; + case MEDIA_BUS_FMT_YUV12_1X36: + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + break; + case MEDIA_BUS_FMT_UYVY12_1X24: + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + break; + + /* 16bit */ + case MEDIA_BUS_FMT_RGB161616_1X48: + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + break; + case MEDIA_BUS_FMT_YUV16_1X48: + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + break; + + /*YUV 4:2:0 */ + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + input_fmts[i++] = output_fmt; + break; + } + + *num_input_fmts = i; + + if (*num_input_fmts == 0) { + kfree(input_fmts); + input_fmts = NULL; + } + + return input_fmts; +} + +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + void *data = hdmi->plat_data->phy_data; + + if (bridge_state->output_bus_cfg.format == MEDIA_BUS_FMT_FIXED) { + if (hdmi->plat_data->get_output_bus_format) + hdmi->hdmi_data.enc_out_bus_format = + hdmi->plat_data->get_output_bus_format(data); + else + hdmi->hdmi_data.enc_out_bus_format = + MEDIA_BUS_FMT_RGB888_1X24; + + if (hdmi->plat_data->get_input_bus_format) + hdmi->hdmi_data.enc_in_bus_format = + hdmi->plat_data->get_input_bus_format(data); + else if (hdmi->plat_data->input_bus_format) + hdmi->hdmi_data.enc_in_bus_format = + hdmi->plat_data->input_bus_format; + else + hdmi->hdmi_data.enc_in_bus_format = + MEDIA_BUS_FMT_RGB888_1X24; + } else { + hdmi->hdmi_data.enc_out_bus_format = + bridge_state->output_bus_cfg.format; + + hdmi->hdmi_data.enc_in_bus_format = + bridge_state->input_bus_cfg.format; + + dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n", + bridge_state->input_bus_cfg.format, + bridge_state->output_bus_cfg.format); + } + + return 0; +} + +static int dw_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + int ret; + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + if (hdmi->next_bridge) { + hdmi->next_bridge->encoder = bridge->encoder; + ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags); + if (ret) { + DRM_ERROR("Failed to attach bridge with dw-hdmi\n"); + return ret; + } + + return 0; + } + + return dw_hdmi_connector_create(hdmi); +} + +static void dw_hdmi_bridge_detach(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->cec_notifier_mutex); + cec_notifier_conn_unregister(hdmi->cec_notifier); + hdmi->cec_notifier = NULL; + mutex_unlock(&hdmi->cec_notifier_mutex); +} + static enum drm_mode_status dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, const struct drm_display_mode *mode) { struct dw_hdmi *hdmi = bridge->driver_private; - struct drm_connector *connector = &hdmi->connector; + const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; enum drm_mode_status mode_status = MODE_OK; if (hdmi->next_bridge) @@ -3498,15 +3970,16 @@ if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD) && hdmi->hdr2sdr) return MODE_OK; - if (hdmi->plat_data->mode_valid) - mode_status = hdmi->plat_data->mode_valid(connector, mode); + if (pdata->mode_valid) + mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info, + mode); return mode_status; } static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *orig_mode, - struct drm_display_mode *mode) + const struct drm_display_mode *orig_mode, + const struct drm_display_mode *mode) { struct dw_hdmi *hdmi = bridge->driver_private; @@ -3518,7 +3991,8 @@ mutex_unlock(&hdmi->mutex); } -static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) +static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) { struct dw_hdmi *hdmi = bridge->driver_private; void *data = hdmi->plat_data->phy_data; @@ -3526,6 +4000,7 @@ mutex_lock(&hdmi->mutex); hdmi->disabled = true; handle_plugged_change(hdmi, false); + hdmi->curr_conn = NULL; dw_hdmi_update_power(hdmi); dw_hdmi_update_phy_mask(hdmi); if (hdmi->plat_data->dclk_set) @@ -3538,12 +4013,19 @@ mutex_unlock(&hdmi->i2c->lock); } -static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) +static void dw_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) { struct dw_hdmi *hdmi = bridge->driver_private; + struct drm_atomic_state *state = old_state->base.state; + struct drm_connector *connector; + + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); mutex_lock(&hdmi->mutex); hdmi->disabled = false; + hdmi->curr_conn = connector; if (hdmi->plat_data->dclk_set) hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, true, 0); dw_hdmi_update_power(hdmi); @@ -3552,13 +4034,47 @@ mutex_unlock(&hdmi->mutex); } +static enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + return dw_hdmi_detect(hdmi); +} + +static struct edid *dw_hdmi_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + return dw_hdmi_get_edid(hdmi, connector); +} + static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { + .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, .attach = dw_hdmi_bridge_attach, - .enable = dw_hdmi_bridge_enable, - .disable = dw_hdmi_bridge_disable, + .detach = dw_hdmi_bridge_detach, + .atomic_check = dw_hdmi_bridge_atomic_check, + .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts, + .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts, + .atomic_enable = dw_hdmi_bridge_atomic_enable, + .atomic_disable = dw_hdmi_bridge_atomic_disable, .mode_set = dw_hdmi_bridge_mode_set, .mode_valid = dw_hdmi_bridge_mode_valid, + .detect = dw_hdmi_bridge_detect, + .get_edid = dw_hdmi_bridge_get_edid, }; + +void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap) +{ + hdmi->cec_adap = adap; +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_cec_adap); + +/* ----------------------------------------------------------------------------- + * IRQ Handling + */ static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi) { @@ -3668,9 +4184,11 @@ phy_stat & HDMI_PHY_HPD, phy_stat & HDMI_PHY_RX_SENSE); - if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) - cec_notifier_set_phys_addr(hdmi->cec_notifier, - CEC_PHYS_ADDR_INVALID); + if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) { + mutex_lock(&hdmi->cec_notifier_mutex); + cec_notifier_phys_addr_invalidate(hdmi->cec_notifier); + mutex_unlock(&hdmi->cec_notifier_mutex); + } } check_hdmi_irq(hdmi, intr_stat, phy_int_pol); @@ -3813,6 +4331,22 @@ .max_register = HDMI_I2CM_SCDC_UPDATE1 << 2, }; +static void dw_hdmi_init_hw(struct dw_hdmi *hdmi) +{ + initialize_hdmi_ih_mutes(hdmi); + + /* + * Reset HDMI DDC I2C master controller and mute I2CM interrupts. + * Even if we are using a separate i2c adapter doing this doesn't + * hurt. + */ + if (hdmi->i2c) + dw_hdmi_i2c_init(hdmi); + + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); +} + static int dw_hdmi_status_show(struct seq_file *s, void *v) { struct dw_hdmi *hdmi = s->private; @@ -3871,22 +4405,22 @@ } val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN); - if (!(val & HDMI_FC_PACKET_DRM_TX_EN_MASK)) { + if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) { seq_puts(s, "Off\n"); return 0; } switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) { - case TRADITIONAL_GAMMA_SDR: + case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: seq_puts(s, "SDR"); break; - case TRADITIONAL_GAMMA_HDR: + case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: seq_puts(s, "HDR"); break; - case SMPTE_ST2084: + case HDMI_EOTF_SMPTE_ST2084: seq_puts(s, "ST2084"); break; - case HLG: + case HDMI_EOTF_BT_2100_HLG: seq_puts(s, "HLG"); break; default: @@ -4163,9 +4697,22 @@ return 0; } -static struct dw_hdmi * -__dw_hdmi_probe(struct platform_device *pdev, - const struct dw_hdmi_plat_data *plat_data) +void +dw_hdmi_cec_wake_ops_register(struct dw_hdmi *hdmi, const struct dw_hdmi_cec_wake_ops *cec_ops) +{ + if (!cec_ops || !hdmi) + return; + + hdmi->cec_ops = cec_ops; +} +EXPORT_SYMBOL_GPL(dw_hdmi_cec_wake_ops_register); + + +/* ----------------------------------------------------------------------------- + * Probe/remove API, used from platforms based on the DRM bridge API. + */ +struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; @@ -4191,7 +4738,6 @@ hdmi->connector.stereo_allowed = 1; hdmi->plat_data = plat_data; hdmi->dev = dev; - hdmi->audio_enable = true; hdmi->sample_rate = 48000; hdmi->disabled = true; hdmi->rxsense = true; @@ -4201,6 +4747,7 @@ mutex_init(&hdmi->mutex); mutex_init(&hdmi->audio_mutex); + mutex_init(&hdmi->cec_notifier_mutex); spin_lock_init(&hdmi->audio_lock); ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); @@ -4344,7 +4891,6 @@ } init_hpd_work(hdmi); - initialize_hdmi_ih_mutes(hdmi); irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -4359,12 +4905,6 @@ if (ret) goto err_iahb; - hdmi->cec_notifier = cec_notifier_get(dev); - if (!hdmi->cec_notifier) { - ret = -ENOMEM; - goto err_iahb; - } - /* * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator * N and cts values before enabling phy @@ -4373,6 +4913,24 @@ /* If DDC bus is not specified, try to register HDMI I2C bus */ if (!hdmi->ddc) { + /* Look for (optional) stuff related to unwedging */ + hdmi->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(hdmi->pinctrl)) { + hdmi->unwedge_state = + pinctrl_lookup_state(hdmi->pinctrl, "unwedge"); + hdmi->default_state = + pinctrl_lookup_state(hdmi->pinctrl, "default"); + + if (IS_ERR(hdmi->default_state) || + IS_ERR(hdmi->unwedge_state)) { + if (!IS_ERR(hdmi->unwedge_state)) + dev_warn(dev, + "Unwedge requires default pinctrl\n"); + hdmi->default_state = NULL; + hdmi->unwedge_state = NULL; + } + } + hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); if (IS_ERR(hdmi->ddc)) hdmi->ddc = NULL; @@ -4388,8 +4946,12 @@ hdmi->i2c->scl_low_ns = 4916; } + dw_hdmi_init_hw(hdmi); + hdmi->bridge.driver_private = hdmi; hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; + hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID + | DRM_BRIDGE_OP_HPD; #ifdef CONFIG_OF hdmi->bridge.of_node = pdev->dev.of_node; #endif @@ -4418,16 +4980,6 @@ hdmi->sink_has_audio = true; } - dw_hdmi_setup_i2c(hdmi); - if (hdmi->phy.ops->setup_hpd) - hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); - - if (hdmi->version >= 0x200a) - hdmi->connector.ycbcr_420_allowed = - hdmi->plat_data->ycbcr_420_allowed; - else - hdmi->connector.ycbcr_420_allowed = false; - memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; pdevinfo.id = PLATFORM_DEVID_AUTO; @@ -4442,7 +4994,7 @@ audio.base = hdmi->regs; audio.irq = irq; audio.hdmi = hdmi; - audio.eld = hdmi->connector.eld; + audio.get_eld = hdmi_audio_get_eld; hdmi->enable_audio = dw_hdmi_ahb_audio_enable; hdmi->disable_audio = dw_hdmi_ahb_audio_disable; @@ -4455,9 +5007,9 @@ struct dw_hdmi_i2s_audio_data audio; audio.hdmi = hdmi; + audio.get_eld = hdmi_audio_get_eld; audio.write = hdmi_writeb; audio.read = hdmi_readb; - audio.mod = hdmi_modb; hdmi->enable_audio = dw_hdmi_i2s_audio_enable; hdmi->disable_audio = dw_hdmi_i2s_audio_disable; @@ -4510,9 +5062,7 @@ goto err_iahb; } - /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ - if (hdmi->i2c) - dw_hdmi_i2c_init(hdmi); + drm_bridge_add(&hdmi->bridge); dw_hdmi_register_debugfs(dev, hdmi); @@ -4526,26 +5076,22 @@ return hdmi; err_iahb: - if (hdmi->i2c) { - i2c_del_adapter(&hdmi->i2c->adap); - hdmi->ddc = NULL; - } - - if (hdmi->cec_notifier) - cec_notifier_put(hdmi->cec_notifier); - clk_disable_unprepare(hdmi->iahb_clk); if (hdmi->cec_clk) clk_disable_unprepare(hdmi->cec_clk); err_isfr: clk_disable_unprepare(hdmi->isfr_clk); err_res: - i2c_put_adapter(hdmi->ddc); + if (hdmi->i2c) + i2c_del_adapter(&hdmi->i2c->adap); + else + i2c_put_adapter(hdmi->ddc); return ERR_PTR(ret); } +EXPORT_SYMBOL_GPL(dw_hdmi_probe); -static void __dw_hdmi_remove(struct dw_hdmi *hdmi) +void dw_hdmi_remove(struct dw_hdmi *hdmi) { if (hdmi->irq) disable_irq(hdmi->irq); @@ -4555,6 +5101,8 @@ destroy_workqueue(hdmi->workqueue); debugfs_remove_recursive(hdmi->debugfs_dir); + + drm_bridge_remove(&hdmi->bridge); if (hdmi->audio && !IS_ERR(hdmi->audio)) platform_device_unregister(hdmi->audio); @@ -4574,9 +5122,6 @@ if (hdmi->bridge.encoder) hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); - if (hdmi->cec_notifier) - cec_notifier_put(hdmi->cec_notifier); - clk_disable_unprepare(hdmi->iahb_clk); clk_disable_unprepare(hdmi->isfr_clk); if (hdmi->cec_clk) @@ -4586,31 +5131,6 @@ i2c_del_adapter(&hdmi->i2c->adap); else i2c_put_adapter(hdmi->ddc); -} - -/* ----------------------------------------------------------------------------- - * Probe/remove API, used from platforms based on the DRM bridge API. - */ -struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - const struct dw_hdmi_plat_data *plat_data) -{ - struct dw_hdmi *hdmi; - - hdmi = __dw_hdmi_probe(pdev, plat_data); - if (IS_ERR(hdmi)) - return hdmi; - - drm_bridge_add(&hdmi->bridge); - - return hdmi; -} -EXPORT_SYMBOL_GPL(dw_hdmi_probe); - -void dw_hdmi_remove(struct dw_hdmi *hdmi) -{ - drm_bridge_remove(&hdmi->bridge); - - __dw_hdmi_remove(hdmi); } EXPORT_SYMBOL_GPL(dw_hdmi_remove); @@ -4624,13 +5144,13 @@ struct dw_hdmi *hdmi; int ret; - hdmi = __dw_hdmi_probe(pdev, plat_data); + hdmi = dw_hdmi_probe(pdev, plat_data); if (IS_ERR(hdmi)) return hdmi; - ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL); + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); if (ret) { - __dw_hdmi_remove(hdmi); + dw_hdmi_remove(hdmi); DRM_ERROR("Failed to initialize bridge with drm\n"); return ERR_PTR(ret); } @@ -4644,7 +5164,7 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) { - __dw_hdmi_remove(hdmi); + dw_hdmi_remove(hdmi); } EXPORT_SYMBOL_GPL(dw_hdmi_unbind); @@ -4673,12 +5193,10 @@ } } -void dw_hdmi_suspend(struct device *dev, struct dw_hdmi *hdmi) +void dw_hdmi_suspend(struct dw_hdmi *hdmi) { - if (!hdmi) { - dev_warn(dev, "Hdmi has not been initialized\n"); + if (!hdmi) return; - } mutex_lock(&hdmi->mutex); @@ -4699,18 +5217,16 @@ disable_irq(hdmi->irq); cancel_delayed_work(&hdmi->work); flush_workqueue(hdmi->workqueue); - pinctrl_pm_select_sleep_state(dev); + pinctrl_pm_select_sleep_state(hdmi->dev); } EXPORT_SYMBOL_GPL(dw_hdmi_suspend); -void dw_hdmi_resume(struct device *dev, struct dw_hdmi *hdmi) +void dw_hdmi_resume(struct dw_hdmi *hdmi) { - if (!hdmi) { - dev_warn(dev, "Hdmi has not been initialized\n"); + if (!hdmi) return; - } - pinctrl_pm_select_default_state(dev); + pinctrl_pm_select_default_state(hdmi->dev); mutex_lock(&hdmi->mutex); dw_hdmi_reg_initial(hdmi); if (hdmi->i2c) -- Gitblit v1.6.2