From patchwork Mon Jun 1 06:17:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581401 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C190314F6 for ; Mon, 1 Jun 2020 06:24:37 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9729D20734 for ; Mon, 1 Jun 2020 06:24:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="dEgdRn+x" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 9729D20734 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=IMXoqdsuxnCDmMa53XxZASk1PNhU8oC48LVvf8te6j0=; b=dEgdRn+xIU4mUIXwJkcEKixBaL qU+gwAONCvw2ucpGNEARE2P0ZVNmQW+ufwI7a4ZwvLGGW6vPdkK8bl8yPSXhwHPCV+3pJbFdkh9zi BMkdSYjJ9nm3ruE0/vKaCv2WHCGNZPyPyBXqBqr7qSUEkdFvldpnsZYYElvgkqlsJTVW4jVGQT5Pb 5xh37F62QXJzqcsT4qrXbjsC7RrOLLtrJs0Yxj37YmQDK69ReOQ7ThdsQu45yeydc81ghib7eww+6 U0roYrFA9jN0NtAvdhxs2guXePGQ6TFIYxBqRGvhsZMh+pT/z44me7h03srsua+wgaaY7jj49iDYf mr9hSHkg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfds6-0008FT-VC; Mon, 01 Jun 2020 06:24:30 +0000 Received: from inva020.nxp.com ([92.121.34.13]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdqy-0007O4-D8; Mon, 01 Jun 2020 06:23:30 +0000 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 8E66D1A0630; Mon, 1 Jun 2020 08:23:18 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 1DE451A0632; Mon, 1 Jun 2020 08:23:12 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id B8CC8402DF; Mon, 1 Jun 2020 14:23:04 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 1/7] drm/rockchip: prepare common code for cdns and rk dpi/dp driver Date: Mon, 1 Jun 2020 14:17:31 +0800 Message-Id: X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232321_138073_F3DBD83D X-CRM114-Status: GOOD ( 15.94 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.13 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu - Extracted common fields from cdn_dp_device to a new cdns_mhdp_device structure which will be used by two separate drivers later on. - Moved some datatypes (audio_format, audio_info, vic_pxl_encoding_format, video_info) from cdn-dp-core.c to cdn-dp-reg.h. - Changed prefixes from cdn_dp to cdns_mhdp cdn -> cdns to match the other Cadence's drivers dp -> mhdp to distinguish it from a "just a DP" as the IP underneath this registers map can be a HDMI (which is internally different, but the interface for commands, events is pretty much the same). - Modified cdn-dp-core.c to use the new driver structure and new function names. - writel and readl are replaced by cdns_mhdp_bus_write and cdns_mhdp_bus_read. Signed-off-by: Damian Kos Signed-off-by: Sandor Yu --- drivers/gpu/drm/rockchip/cdn-dp-core.c | 240 ++++++------ drivers/gpu/drm/rockchip/cdn-dp-core.h | 44 +-- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 488 +++++++++++++------------ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 133 +++++-- 4 files changed, 483 insertions(+), 422 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index eed594bd38d3..b6aa21779608 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -26,7 +26,7 @@ #include "rockchip_drm_vop.h" #define connector_to_dp(c) \ - container_of(c, struct cdn_dp_device, connector) + container_of(c, struct cdn_dp_device, mhdp.connector.base) #define encoder_to_dp(c) \ container_of(c, struct cdn_dp_device, encoder) @@ -61,17 +61,18 @@ MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids); static int cdn_dp_grf_write(struct cdn_dp_device *dp, unsigned int reg, unsigned int val) { + struct device *dev = dp->mhdp.dev; int ret; ret = clk_prepare_enable(dp->grf_clk); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to prepare_enable grf clock\n"); + DRM_DEV_ERROR(dev, "Failed to prepare_enable grf clock\n"); return ret; } ret = regmap_write(dp->grf, reg, val); if (ret) { - DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret); + DRM_DEV_ERROR(dev, "Could not write to GRF: %d\n", ret); return ret; } @@ -82,24 +83,25 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp, static int cdn_dp_clk_enable(struct cdn_dp_device *dp) { + struct device *dev = dp->mhdp.dev; int ret; unsigned long rate; ret = clk_prepare_enable(dp->pclk); if (ret < 0) { - DRM_DEV_ERROR(dp->dev, "cannot enable dp pclk %d\n", ret); + DRM_DEV_ERROR(dev, "cannot enable dp pclk %d\n", ret); goto err_pclk; } ret = clk_prepare_enable(dp->core_clk); if (ret < 0) { - DRM_DEV_ERROR(dp->dev, "cannot enable core_clk %d\n", ret); + DRM_DEV_ERROR(dev, "cannot enable core_clk %d\n", ret); goto err_core_clk; } - ret = pm_runtime_get_sync(dp->dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) { - DRM_DEV_ERROR(dp->dev, "cannot get pm runtime %d\n", ret); + DRM_DEV_ERROR(dev, "cannot get pm runtime %d\n", ret); goto err_pm_runtime_get; } @@ -112,18 +114,18 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) rate = clk_get_rate(dp->core_clk); if (!rate) { - DRM_DEV_ERROR(dp->dev, "get clk rate failed\n"); + DRM_DEV_ERROR(dev, "get clk rate failed\n"); ret = -EINVAL; goto err_set_rate; } - cdn_dp_set_fw_clk(dp, rate); - cdn_dp_clock_reset(dp); + cdns_mhdp_set_fw_clk(&dp->mhdp, rate); + cdns_mhdp_clock_reset(&dp->mhdp); return 0; err_set_rate: - pm_runtime_put(dp->dev); + pm_runtime_put(dev); err_pm_runtime_get: clk_disable_unprepare(dp->core_clk); err_core_clk: @@ -134,7 +136,7 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) static void cdn_dp_clk_disable(struct cdn_dp_device *dp) { - pm_runtime_put_sync(dp->dev); + pm_runtime_put_sync(dp->mhdp.dev); clk_disable_unprepare(dp->pclk); clk_disable_unprepare(dp->core_clk); } @@ -167,7 +169,7 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1); + ret = drm_dp_dpcd_read(&dp->mhdp.dp.aux, DP_SINK_COUNT, &value, 1); if (ret) return ret; @@ -191,12 +193,13 @@ static struct cdn_dp_port *cdn_dp_connected_port(struct cdn_dp_device *dp) static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) { + struct device *dev = dp->mhdp.dev; unsigned long timeout = jiffies + msecs_to_jiffies(CDN_DPCD_TIMEOUT_MS); struct cdn_dp_port *port; u8 sink_count = 0; if (dp->active_port < 0 || dp->active_port >= dp->ports) { - DRM_DEV_ERROR(dp->dev, "active_port is wrong!\n"); + DRM_DEV_ERROR(dev, "active_port is wrong!\n"); return false; } @@ -218,7 +221,7 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) usleep_range(5000, 10000); } - DRM_DEV_ERROR(dp->dev, "Get sink capability timed out\n"); + DRM_DEV_ERROR(dev, "Get sink capability timed out\n"); return false; } @@ -260,7 +263,8 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector) mutex_lock(&dp->lock); edid = dp->edid; if (edid) { - DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n", + DRM_DEV_DEBUG_KMS(dp->mhdp.dev, + "got edid: width[%d] x height[%d]\n", edid->width_cm, edid->height_cm); dp->sink_has_audio = drm_detect_monitor_audio(edid); @@ -278,7 +282,8 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct cdn_dp_device *dp = connector_to_dp(connector); - struct drm_display_info *display_info = &dp->connector.display_info; + struct drm_display_info *display_info = + &dp->mhdp.connector.base.display_info; u32 requested, actual, rate, sink_max, source_max = 0; u8 lanes, bpc; @@ -301,11 +306,11 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, requested = mode->clock * bpc * 3 / 1000; source_max = dp->lanes; - sink_max = drm_dp_max_lane_count(dp->dpcd); + sink_max = drm_dp_max_lane_count(dp->mhdp.dp.dpcd); lanes = min(source_max, sink_max); - source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE); - sink_max = drm_dp_max_link_rate(dp->dpcd); + source_max = CDNS_DP_MAX_LINK_RATE; + sink_max = drm_dp_max_link_rate(dp->mhdp.dp.dpcd); rate = min(source_max, sink_max); actual = rate * lanes / 100; @@ -314,7 +319,7 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, actual = actual * 8 / 10; if (requested > actual) { - DRM_DEV_DEBUG_KMS(dp->dev, + DRM_DEV_DEBUG_KMS(dp->mhdp.dev, "requested=%d, actual=%d, clock=%d\n", requested, actual, mode->clock); return MODE_CLOCK_HIGH; @@ -334,59 +339,62 @@ static int cdn_dp_firmware_init(struct cdn_dp_device *dp) const u32 *iram_data, *dram_data; const struct firmware *fw = dp->fw; const struct cdn_firmware_header *hdr; + struct device *dev = dp->mhdp.dev; hdr = (struct cdn_firmware_header *)fw->data; if (fw->size != le32_to_cpu(hdr->size_bytes)) { - DRM_DEV_ERROR(dp->dev, "firmware is invalid\n"); + DRM_DEV_ERROR(dev, "firmware is invalid\n"); return -EINVAL; } iram_data = (const u32 *)(fw->data + hdr->header_size); dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size); - ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size, - dram_data, hdr->dram_size); + ret = cdns_mhdp_load_firmware(&dp->mhdp, iram_data, hdr->iram_size, + dram_data, hdr->dram_size); if (ret) return ret; - ret = cdn_dp_set_firmware_active(dp, true); + ret = cdns_mhdp_set_firmware_active(&dp->mhdp, true); if (ret) { - DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret); + DRM_DEV_ERROR(dev, "active ucpu failed: %d\n", ret); return ret; } - return cdn_dp_event_config(dp); + return cdns_mhdp_event_config(&dp->mhdp); } static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) { + struct cdns_mhdp_device *mhdp = &dp->mhdp; int ret; if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, + ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, mhdp->dp.dpcd, DP_RECEIVER_CAP_SIZE); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "Failed to get caps %d\n", ret); return ret; } kfree(dp->edid); - dp->edid = drm_do_get_edid(&dp->connector, - cdn_dp_get_edid_block, dp); + dp->edid = drm_do_get_edid(&mhdp->connector.base, + cdns_mhdp_get_edid_block, mhdp); return 0; } static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) { + struct device *dev = dp->mhdp.dev; union extcon_property_value property; int ret; if (!port->phy_enabled) { ret = phy_power_on(port->phy); if (ret) { - DRM_DEV_ERROR(dp->dev, "phy power on failed: %d\n", + DRM_DEV_ERROR(dev, "phy power on failed: %d\n", ret); goto err_phy; } @@ -396,28 +404,28 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, DPTX_HPD_SEL_MASK | DPTX_HPD_SEL); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to write HPD_SEL %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to write HPD_SEL %d\n", ret); goto err_power_on; } - ret = cdn_dp_get_hpd_status(dp); + ret = cdns_mhdp_read_hpd(&dp->mhdp); if (ret <= 0) { if (!ret) - DRM_DEV_ERROR(dp->dev, "hpd does not exist\n"); + DRM_DEV_ERROR(dev, "hpd does not exist\n"); goto err_power_on; } ret = extcon_get_property(port->extcon, EXTCON_DISP_DP, EXTCON_PROP_USB_TYPEC_POLARITY, &property); if (ret) { - DRM_DEV_ERROR(dp->dev, "get property failed\n"); + DRM_DEV_ERROR(dev, "get property failed\n"); goto err_power_on; } port->lanes = cdn_dp_get_port_lanes(port); - ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval); + ret = cdns_mhdp_set_host_cap(&dp->mhdp, property.intval); if (ret) { - DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n", + DRM_DEV_ERROR(dev, "set host capabilities failed: %d\n", ret); goto err_power_on; } @@ -427,7 +435,7 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) err_power_on: if (phy_power_off(port->phy)) - DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret); + DRM_DEV_ERROR(dev, "phy power off failed: %d", ret); else port->phy_enabled = false; @@ -445,7 +453,8 @@ static int cdn_dp_disable_phy(struct cdn_dp_device *dp, if (port->phy_enabled) { ret = phy_power_off(port->phy); if (ret) { - DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret); + DRM_DEV_ERROR(dp->mhdp.dev, + "phy power off failed: %d", ret); return ret; } } @@ -469,16 +478,16 @@ static int cdn_dp_disable(struct cdn_dp_device *dp) ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, DPTX_HPD_SEL_MASK | DPTX_HPD_DEL); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to clear hpd sel %d\n", + DRM_DEV_ERROR(dp->mhdp.dev, "Failed to clear hpd sel %d\n", ret); return ret; } - cdn_dp_set_firmware_active(dp, false); + cdns_mhdp_set_firmware_active(&dp->mhdp, false); cdn_dp_clk_disable(dp); dp->active = false; - dp->max_lanes = 0; - dp->max_rate = 0; + dp->mhdp.dp.rate = 0; + dp->mhdp.dp.num_lanes = 0; if (!dp->connected) { kfree(dp->edid); dp->edid = NULL; @@ -491,11 +500,11 @@ static int cdn_dp_enable(struct cdn_dp_device *dp) { int ret, i, lanes; struct cdn_dp_port *port; + struct device *dev = dp->mhdp.dev; port = cdn_dp_connected_port(dp); if (!port) { - DRM_DEV_ERROR(dp->dev, - "Can't enable without connection\n"); + DRM_DEV_ERROR(dev, "Can't enable without connection\n"); return -ENODEV; } @@ -508,7 +517,7 @@ static int cdn_dp_enable(struct cdn_dp_device *dp) ret = cdn_dp_firmware_init(dp); if (ret) { - DRM_DEV_ERROR(dp->dev, "firmware init failed: %d", ret); + DRM_DEV_ERROR(dp->mhdp.dev, "firmware init failed: %d", ret); goto err_clk_disable; } @@ -542,8 +551,9 @@ static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *adjusted) { struct cdn_dp_device *dp = encoder_to_dp(encoder); - struct drm_display_info *display_info = &dp->connector.display_info; - struct video_info *video = &dp->video_info; + struct drm_display_info *display_info = + &dp->mhdp.connector.base.display_info; + struct video_info *video = &dp->mhdp.video_info; switch (display_info->bpc) { case 10: @@ -561,20 +571,20 @@ static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder, video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); - memcpy(&dp->mode, adjusted, sizeof(*mode)); + memcpy(&dp->mhdp.mode, adjusted, sizeof(*mode)); } static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) { u8 link_status[DP_LINK_STATUS_SIZE]; struct cdn_dp_port *port = cdn_dp_connected_port(dp); - u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd); + u8 sink_lanes = drm_dp_max_lane_count(dp->mhdp.dp.dpcd); - if (!port || !dp->max_rate || !dp->max_lanes) + if (!port || !dp->mhdp.dp.rate || !dp->mhdp.dp.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, - DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read(&dp->mhdp.dp.aux, DP_LANE0_1_STATUS, link_status, + DP_LINK_STATUS_SIZE)) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -586,15 +596,16 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) static void cdn_dp_encoder_enable(struct drm_encoder *encoder) { struct cdn_dp_device *dp = encoder_to_dp(encoder); + struct device *dev = dp->mhdp.dev; int ret, val; - ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder); + ret = drm_of_encoder_active_endpoint_id(dev->of_node, encoder); if (ret < 0) { - DRM_DEV_ERROR(dp->dev, "Could not get vop id, %d", ret); + DRM_DEV_ERROR(dev, "Could not get vop id, %d", ret); return; } - DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n", + DRM_DEV_DEBUG_KMS(dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG"); if (ret) val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16); @@ -609,33 +620,33 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) ret = cdn_dp_enable(dp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to enable encoder %d\n", + DRM_DEV_ERROR(dev, "Failed to enable encoder %d\n", ret); goto out; } if (!cdn_dp_check_link_status(dp)) { - ret = cdn_dp_train_link(dp); + ret = cdns_mhdp_train_link(&dp->mhdp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed link train %d\n", ret); + DRM_DEV_ERROR(dev, "Failed link train %d\n", ret); goto out; } } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + ret = cdns_mhdp_set_video_status(&dp->mhdp, CONTROL_VIDEO_IDLE); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to idle video %d\n", ret); goto out; } - ret = cdn_dp_config_video(dp); + ret = cdns_mhdp_config_video(&dp->mhdp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to config video %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to config video %d\n", ret); goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + ret = cdns_mhdp_set_video_status(&dp->mhdp, CONTROL_VIDEO_VALID); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to valid video %d\n", ret); goto out; } out: @@ -651,7 +662,8 @@ static void cdn_dp_encoder_disable(struct drm_encoder *encoder) if (dp->active) { ret = cdn_dp_disable(dp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to disable encoder %d\n", + DRM_DEV_ERROR(dp->mhdp.dev, + "Failed to disable encoder %d\n", ret); } } @@ -695,7 +707,7 @@ static const struct drm_encoder_funcs cdn_dp_encoder_funcs = { static int cdn_dp_parse_dt(struct cdn_dp_device *dp) { - struct device *dev = dp->dev; + struct device *dev = dp->mhdp.dev; struct device_node *np = dev->of_node; struct platform_device *pdev = to_platform_device(dev); struct resource *res; @@ -707,10 +719,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dp->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(dp->regs)) { + dp->mhdp.regs_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dp->mhdp.regs_base)) { DRM_DEV_ERROR(dev, "ioremap reg failed\n"); - return PTR_ERR(dp->regs); + return PTR_ERR(dp->mhdp.regs_base); } dp->core_clk = devm_clk_get(dev, "core-clk"); @@ -725,10 +737,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp) return PTR_ERR(dp->pclk); } - dp->spdif_clk = devm_clk_get(dev, "spdif"); - if (IS_ERR(dp->spdif_clk)) { + dp->mhdp.spdif_clk = devm_clk_get(dev, "spdif"); + if (IS_ERR(dp->mhdp.spdif_clk)) { DRM_DEV_ERROR(dev, "cannot get spdif_clk\n"); - return PTR_ERR(dp->spdif_clk); + return PTR_ERR(dp->mhdp.spdif_clk); } dp->grf_clk = devm_clk_get(dev, "grf"); @@ -737,10 +749,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp) return PTR_ERR(dp->grf_clk); } - dp->spdif_rst = devm_reset_control_get(dev, "spdif"); - if (IS_ERR(dp->spdif_rst)) { + dp->mhdp.spdif_rst = devm_reset_control_get(dev, "spdif"); + if (IS_ERR(dp->mhdp.spdif_rst)) { DRM_DEV_ERROR(dev, "no spdif reset control found\n"); - return PTR_ERR(dp->spdif_rst); + return PTR_ERR(dp->mhdp.spdif_rst); } dp->dptx_rst = devm_reset_control_get(dev, "dptx"); @@ -787,7 +799,7 @@ static int cdn_dp_audio_hw_params(struct device *dev, void *data, audio.format = AFMT_I2S; break; case HDMI_SPDIF: - audio.format = AFMT_SPDIF; + audio.format = AFMT_SPDIF_INT; break; default: DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt); @@ -795,9 +807,9 @@ static int cdn_dp_audio_hw_params(struct device *dev, void *data, goto out; } - ret = cdn_dp_audio_config(dp, &audio); + ret = cdns_mhdp_audio_config(&dp->mhdp, &audio); if (!ret) - dp->audio_info = audio; + dp->mhdp.audio_info = audio; out: mutex_unlock(&dp->lock); @@ -813,9 +825,9 @@ static void cdn_dp_audio_shutdown(struct device *dev, void *data) if (!dp->active) goto out; - ret = cdn_dp_audio_stop(dp, &dp->audio_info); + ret = cdns_mhdp_audio_stop(&dp->mhdp, &dp->mhdp.audio_info); if (!ret) - dp->audio_info.format = AFMT_UNUSED; + dp->mhdp.audio_info.format = AFMT_UNUSED; out: mutex_unlock(&dp->lock); } @@ -832,7 +844,7 @@ static int cdn_dp_audio_digital_mute(struct device *dev, void *data, goto out; } - ret = cdn_dp_audio_mute(dp, enable); + ret = cdns_mhdp_audio_mute(&dp->mhdp, enable); out: mutex_unlock(&dp->lock); @@ -844,7 +856,8 @@ static int cdn_dp_audio_get_eld(struct device *dev, void *data, { struct cdn_dp_device *dp = dev_get_drvdata(dev); - memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len)); + memcpy(buf, dp->mhdp.connector.base.eld, + min(sizeof(dp->mhdp.connector.base.eld), len)); return 0; } @@ -866,11 +879,11 @@ static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp, .max_i2s_channels = 8, }; - dp->audio_pdev = platform_device_register_data( - dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, - &codec_data, sizeof(codec_data)); + dp->mhdp.audio_pdev = platform_device_register_data( + dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, + &codec_data, sizeof(codec_data)); - return PTR_ERR_OR_ZERO(dp->audio_pdev); + return PTR_ERR_OR_ZERO(dp->mhdp.audio_pdev); } static int cdn_dp_request_firmware(struct cdn_dp_device *dp) @@ -878,6 +891,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp) int ret; unsigned long timeout = jiffies + msecs_to_jiffies(CDN_FW_TIMEOUT_MS); unsigned long sleep = 1000; + struct device *dev = dp->mhdp.dev; WARN_ON(!mutex_is_locked(&dp->lock)); @@ -888,13 +902,13 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp) mutex_unlock(&dp->lock); while (time_before(jiffies, timeout)) { - ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev); + ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dev); if (ret == -ENOENT) { msleep(sleep); sleep *= 2; continue; } else if (ret) { - DRM_DEV_ERROR(dp->dev, + DRM_DEV_ERROR(dev, "failed to request firmware: %d\n", ret); goto out; } @@ -904,7 +918,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp) goto out; } - DRM_DEV_ERROR(dp->dev, "Timed out trying to load firmware\n"); + DRM_DEV_ERROR(dev, "Timed out trying to load firmware\n"); ret = -ETIMEDOUT; out: mutex_lock(&dp->lock); @@ -915,8 +929,9 @@ static void cdn_dp_pd_event_work(struct work_struct *work) { struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device, event_work); - struct drm_connector *connector = &dp->connector; + struct drm_connector *connector = &dp->mhdp.connector.base; enum drm_connector_status old_status; + struct device *dev = dp->mhdp.dev; int ret; @@ -933,44 +948,45 @@ static void cdn_dp_pd_event_work(struct work_struct *work) /* Not connected, notify userspace to disable the block */ if (!cdn_dp_connected_port(dp)) { - DRM_DEV_INFO(dp->dev, "Not connected. Disabling cdn\n"); + DRM_DEV_INFO(dev, "Not connected. Disabling cdn\n"); dp->connected = false; /* Connected but not enabled, enable the block */ } else if (!dp->active) { - DRM_DEV_INFO(dp->dev, "Connected, not enabled. Enabling cdn\n"); + DRM_DEV_INFO(dev, "Connected, not enabled. Enabling cdn\n"); ret = cdn_dp_enable(dp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Enable dp failed %d\n", ret); + DRM_DEV_ERROR(dev, "Enable dp failed %d\n", ret); dp->connected = false; } /* Enabled and connected to a dongle without a sink, notify userspace */ } else if (!cdn_dp_check_sink_connection(dp)) { - DRM_DEV_INFO(dp->dev, "Connected without sink. Assert hpd\n"); + DRM_DEV_INFO(dev, "Connected without sink. Assert hpd\n"); dp->connected = false; /* Enabled and connected with a sink, re-train if requested */ } else if (!cdn_dp_check_link_status(dp)) { - unsigned int rate = dp->max_rate; - unsigned int lanes = dp->max_lanes; - struct drm_display_mode *mode = &dp->mode; + unsigned int rate = dp->mhdp.dp.rate; + unsigned int lanes = dp->mhdp.dp.num_lanes; + struct drm_display_mode *mode = &dp->mhdp.mode; - DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n"); - ret = cdn_dp_train_link(dp); + DRM_DEV_INFO(dev, "Connected with sink. Re-train link\n"); + ret = cdns_mhdp_train_link(&dp->mhdp); if (ret) { dp->connected = false; - DRM_DEV_ERROR(dp->dev, "Train link failed %d\n", ret); + DRM_DEV_ERROR(dev, "Train link failed %d\n", ret); goto out; } /* If training result is changed, update the video config */ if (mode->clock && - (rate != dp->max_rate || lanes != dp->max_lanes)) { - ret = cdn_dp_config_video(dp); + (rate != dp->mhdp.dp.rate || + lanes != dp->mhdp.dp.num_lanes)) { + ret = cdns_mhdp_config_video(&dp->mhdp); if (ret) { dp->connected = false; - DRM_DEV_ERROR(dp->dev, + DRM_DEV_ERROR(dev, "Failed to config video %d\n", ret); } @@ -1039,7 +1055,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs); - connector = &dp->connector; + connector = &dp->mhdp.connector.base; connector->polled = DRM_CONNECTOR_POLL_HPD; connector->dpms = DRM_MODE_DPMS_OFF; @@ -1063,7 +1079,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) port = dp->port[i]; port->event_nb.notifier_call = cdn_dp_pd_event; - ret = devm_extcon_register_notifier(dp->dev, port->extcon, + ret = devm_extcon_register_notifier(dp->mhdp.dev, port->extcon, EXTCON_DISP_DP, &port->event_nb); if (ret) { @@ -1090,7 +1106,7 @@ static void cdn_dp_unbind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); struct drm_encoder *encoder = &dp->encoder; - struct drm_connector *connector = &dp->connector; + struct drm_connector *connector = &dp->mhdp.connector.base; cancel_work_sync(&dp->event_work); cdn_dp_encoder_disable(encoder); @@ -1150,7 +1166,7 @@ static int cdn_dp_probe(struct platform_device *pdev) dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); if (!dp) return -ENOMEM; - dp->dev = dev; + dp->mhdp.dev = dev; match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node); dp_data = (struct cdn_dp_data *)match->data; @@ -1194,8 +1210,8 @@ static int cdn_dp_remove(struct platform_device *pdev) { struct cdn_dp_device *dp = platform_get_drvdata(pdev); - platform_device_unregister(dp->audio_pdev); - cdn_dp_suspend(dp->dev); + platform_device_unregister(dp->mhdp.audio_pdev); + cdn_dp_suspend(dp->mhdp.dev); component_del(&pdev->dev, &cdn_dp_component_ops); return 0; @@ -1205,7 +1221,7 @@ static void cdn_dp_shutdown(struct platform_device *pdev) { struct cdn_dp_device *dp = platform_get_drvdata(pdev); - cdn_dp_suspend(dp->dev); + cdn_dp_suspend(dp->mhdp.dev); } static const struct dev_pm_ops cdn_dp_pm_ops = { diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 81ac9b658a70..d5dcb75b2398 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -11,39 +11,11 @@ #include #include +#include "cdn-dp-reg.h" #include "rockchip_drm_drv.h" #define MAX_PHY 2 -enum audio_format { - AFMT_I2S = 0, - AFMT_SPDIF = 1, - AFMT_UNUSED, -}; - -struct audio_info { - enum audio_format format; - int sample_rate; - int channels; - int sample_width; -}; - -enum vic_pxl_encoding_format { - PXL_RGB = 0x1, - YCBCR_4_4_4 = 0x2, - YCBCR_4_2_2 = 0x4, - YCBCR_4_2_0 = 0x8, - Y_ONLY = 0x10, -}; - -struct video_info { - bool h_sync_polarity; - bool v_sync_polarity; - bool interlaced; - int color_depth; - enum vic_pxl_encoding_format color_fmt; -}; - struct cdn_firmware_header { u32 size_bytes; /* size of the entire header+image(s) in bytes */ u32 header_size; /* size of just the header in bytes */ @@ -62,12 +34,9 @@ struct cdn_dp_port { }; struct cdn_dp_device { - struct device *dev; + struct cdns_mhdp_device mhdp; struct drm_device *drm_dev; - struct drm_connector connector; struct drm_encoder encoder; - struct drm_display_mode mode; - struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; @@ -77,29 +46,20 @@ struct cdn_dp_device { bool suspended; const struct firmware *fw; /* cdn dp firmware */ - unsigned int fw_version; /* cdn fw version */ bool fw_loaded; - void __iomem *regs; struct regmap *grf; struct clk *core_clk; struct clk *pclk; - struct clk *spdif_clk; struct clk *grf_clk; - struct reset_control *spdif_rst; struct reset_control *dptx_rst; struct reset_control *apb_rst; struct reset_control *core_rst; - struct audio_info audio_info; - struct video_info video_info; struct cdn_dp_port *port[MAX_PHY]; u8 ports; - u8 max_lanes; - unsigned int max_rate; u8 lanes; int active_port; - u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; }; #endif /* _CDN_DP_CORE_H */ diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 7361c07cb4a7..f7ccf93fe5e1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -4,6 +4,7 @@ * Author: Chris Zhong */ +#include #include #include #include @@ -14,19 +15,29 @@ #include "cdn-dp-core.h" #include "cdn-dp-reg.h" -#define CDN_DP_SPDIF_CLK 200000000 +#define CDNS_DP_SPDIF_CLK 200000000 #define FW_ALIVE_TIMEOUT_US 1000000 #define MAILBOX_RETRY_US 1000 #define MAILBOX_TIMEOUT_US 5000000 #define LINK_TRAINING_RETRY_MS 20 #define LINK_TRAINING_TIMEOUT_MS 500 -void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk) +u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset) { - writel(clk / 1000000, dp->regs + SW_CLK_H); + return readl(mhdp->regs_base + offset); } -void cdn_dp_clock_reset(struct cdn_dp_device *dp) +void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset) +{ + writel(val, mhdp->regs_base + offset); +} + +void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk) +{ + cdns_mhdp_bus_write(clk / 1000000, mhdp, SW_CLK_H); +} + +void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp) { u32 val; @@ -42,16 +53,16 @@ void cdn_dp_clock_reset(struct cdn_dp_device *dp) DPTX_SYS_CLK_EN | CFG_DPTX_VIF_CLK_RSTN_EN | CFG_DPTX_VIF_CLK_EN; - writel(val, dp->regs + SOURCE_DPTX_CAR); + cdns_mhdp_bus_write(val, mhdp, SOURCE_DPTX_CAR); val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN; - writel(val, dp->regs + SOURCE_PHY_CAR); + cdns_mhdp_bus_write(val, mhdp, SOURCE_PHY_CAR); val = SOURCE_PKT_SYS_RSTN_EN | SOURCE_PKT_SYS_CLK_EN | SOURCE_PKT_DATA_RSTN_EN | SOURCE_PKT_DATA_CLK_EN; - writel(val, dp->regs + SOURCE_PKT_CAR); + cdns_mhdp_bus_write(val, mhdp, SOURCE_PKT_CAR); val = SPDIF_CDR_CLK_RSTN_EN | SPDIF_CDR_CLK_EN | @@ -59,53 +70,52 @@ void cdn_dp_clock_reset(struct cdn_dp_device *dp) SOURCE_AIF_SYS_CLK_EN | SOURCE_AIF_CLK_RSTN_EN | SOURCE_AIF_CLK_EN; - writel(val, dp->regs + SOURCE_AIF_CAR); + cdns_mhdp_bus_write(val, mhdp, SOURCE_AIF_CAR); val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN | SOURCE_CIPHER_SYS_CLK_EN | SOURCE_CIPHER_CHAR_CLK_RSTN_EN | SOURCE_CIPHER_CHAR_CLK_EN; - writel(val, dp->regs + SOURCE_CIPHER_CAR); + cdns_mhdp_bus_write(val, mhdp, SOURCE_CIPHER_CAR); val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN | SOURCE_CRYPTO_SYS_CLK_EN; - writel(val, dp->regs + SOURCE_CRYPTO_CAR); + cdns_mhdp_bus_write(val, mhdp, SOURCE_CRYPTO_CAR); /* enable Mailbox and PIF interrupt */ - writel(0, dp->regs + APB_INT_MASK); + cdns_mhdp_bus_write(0, mhdp, APB_INT_MASK); } -static int cdn_dp_mailbox_read(struct cdn_dp_device *dp) +static int mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) { int val, ret; - ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR, + ret = readx_poll_timeout(readl, mhdp->regs_base + MAILBOX_EMPTY_ADDR, val, !val, MAILBOX_RETRY_US, MAILBOX_TIMEOUT_US); if (ret < 0) return ret; - return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff; + return cdns_mhdp_bus_read(mhdp, MAILBOX0_RD_DATA) & 0xff; } -static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val) +static int mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val) { int ret, full; - ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR, + ret = readx_poll_timeout(readl, mhdp->regs_base + MAILBOX_FULL_ADDR, full, !full, MAILBOX_RETRY_US, MAILBOX_TIMEOUT_US); if (ret < 0) return ret; - writel(val, dp->regs + MAILBOX0_WR_DATA); + cdns_mhdp_bus_write(val, mhdp, MAILBOX0_WR_DATA); return 0; } -static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, - u8 module_id, u8 opcode, - u16 req_size) +int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp, + u8 module_id, u8 opcode, u16 req_size) { u32 mbox_size, i; u8 header[4]; @@ -113,14 +123,14 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, /* read the header of the message */ for (i = 0; i < 4; i++) { - ret = cdn_dp_mailbox_read(dp); + ret = mhdp_mailbox_read(mhdp); if (ret < 0) return ret; header[i] = ret; } - mbox_size = (header[2] << 8) | header[3]; + mbox_size = get_unaligned_be16(header + 2); if (opcode != header[0] || module_id != header[1] || req_size != mbox_size) { @@ -129,7 +139,7 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, * clear the mailbox by reading its contents. */ for (i = 0; i < mbox_size; i++) - if (cdn_dp_mailbox_read(dp) < 0) + if (mhdp_mailbox_read(mhdp) < 0) break; return -EINVAL; @@ -138,14 +148,14 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, return 0; } -static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, - u8 *buff, u16 buff_size) +int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp, + u8 *buff, u16 buff_size) { u32 i; int ret; for (i = 0; i < buff_size; i++) { - ret = cdn_dp_mailbox_read(dp); + ret = mhdp_mailbox_read(mhdp); if (ret < 0) return ret; @@ -155,25 +165,24 @@ static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, return 0; } -static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id, - u8 opcode, u16 size, u8 *message) +int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id, + u8 opcode, u16 size, u8 *message) { u8 header[4]; int ret, i; header[0] = opcode; header[1] = module_id; - header[2] = (size >> 8) & 0xff; - header[3] = size & 0xff; + put_unaligned_be16(size, header + 2); for (i = 0; i < 4; i++) { - ret = cdp_dp_mailbox_write(dp, header[i]); + ret = mhdp_mailbox_write(mhdp, header[i]); if (ret) return ret; } for (i = 0; i < size; i++) { - ret = cdp_dp_mailbox_write(dp, message[i]); + ret = mhdp_mailbox_write(mhdp, message[i]); if (ret) return ret; } @@ -181,146 +190,136 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id, return 0; } -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val) +int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val) { - u8 msg[6]; - - msg[0] = (addr >> 8) & 0xff; - msg[1] = addr & 0xff; - msg[2] = (val >> 24) & 0xff; - msg[3] = (val >> 16) & 0xff; - msg[4] = (val >> 8) & 0xff; - msg[5] = val & 0xff; - return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER, - sizeof(msg), msg); + u8 msg[8]; + + put_unaligned_be32(addr, msg); + put_unaligned_be32(val, msg + 4); + + return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, + GENERAL_WRITE_REGISTER, sizeof(msg), msg); } -static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, - u8 start_bit, u8 bits_no, u32 val) +int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, + u8 start_bit, u8 bits_no, u32 val) { u8 field[8]; - field[0] = (addr >> 8) & 0xff; - field[1] = addr & 0xff; + put_unaligned_be16(addr, field); field[2] = start_bit; field[3] = bits_no; - field[4] = (val >> 24) & 0xff; - field[5] = (val >> 16) & 0xff; - field[6] = (val >> 8) & 0xff; - field[7] = val & 0xff; + put_unaligned_be32(val, field + 4); - return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD, - sizeof(field), field); + return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_WRITE_FIELD, sizeof(field), field); } -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len) +int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, + u32 addr, u8 *data, u16 len) { u8 msg[5], reg[5]; int ret; - msg[0] = (len >> 8) & 0xff; - msg[1] = len & 0xff; - msg[2] = (addr >> 16) & 0xff; - msg[3] = (addr >> 8) & 0xff; - msg[4] = addr & 0xff; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD, - sizeof(msg), msg); + put_unaligned_be16(len, msg); + put_unaligned_be24(addr, msg + 2); + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_DPCD, sizeof(msg), msg); if (ret) goto err_dpcd_read; - ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, - DPTX_READ_DPCD, - sizeof(reg) + len); + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_DPCD, + sizeof(reg) + len); if (ret) goto err_dpcd_read; - ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); if (ret) goto err_dpcd_read; - ret = cdn_dp_mailbox_read_receive(dp, data, len); + ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len); err_dpcd_read: return ret; } -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value) +int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value) { u8 msg[6], reg[5]; int ret; - msg[0] = 0; - msg[1] = 1; - msg[2] = (addr >> 16) & 0xff; - msg[3] = (addr >> 8) & 0xff; - msg[4] = addr & 0xff; + put_unaligned_be16(1, msg); + put_unaligned_be24(addr, msg + 2); msg[5] = value; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD, - sizeof(msg), msg); + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_WRITE_DPCD, sizeof(msg), msg); if (ret) goto err_dpcd_write; - ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, - DPTX_WRITE_DPCD, sizeof(reg)); + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, + DPTX_WRITE_DPCD, sizeof(reg)); if (ret) goto err_dpcd_write; - ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); if (ret) goto err_dpcd_write; - if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) + if (addr != get_unaligned_be24(reg + 2)) ret = -EINVAL; err_dpcd_write: if (ret) - DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "dpcd write failed: %d\n", ret); return ret; } -int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, - u32 i_size, const u32 *d_mem, u32 d_size) +int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, + u32 i_size, const u32 *d_mem, u32 d_size) { u32 reg; int i, ret; /* reset ucpu before load firmware*/ - writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, - dp->regs + APB_CTRL); + cdns_mhdp_bus_write(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, + mhdp, APB_CTRL); for (i = 0; i < i_size; i += 4) - writel(*i_mem++, dp->regs + ADDR_IMEM + i); + cdns_mhdp_bus_write(*i_mem++, mhdp, ADDR_IMEM + i); for (i = 0; i < d_size; i += 4) - writel(*d_mem++, dp->regs + ADDR_DMEM + i); + cdns_mhdp_bus_write(*d_mem++, mhdp, ADDR_DMEM + i); /* un-reset ucpu */ - writel(0, dp->regs + APB_CTRL); + cdns_mhdp_bus_write(0, mhdp, APB_CTRL); /* check the keep alive register to make sure fw working */ - ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE, + ret = readx_poll_timeout(readl, mhdp->regs_base + KEEP_ALIVE, reg, reg, 2000, FW_ALIVE_TIMEOUT_US); if (ret < 0) { - DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n", + DRM_DEV_ERROR(mhdp->dev, "failed to loaded the FW reg = %x\n", reg); return -EINVAL; } - reg = readl(dp->regs + VER_L) & 0xff; - dp->fw_version = reg; - reg = readl(dp->regs + VER_H) & 0xff; - dp->fw_version |= reg << 8; - reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff; - dp->fw_version |= reg << 16; - reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff; - dp->fw_version |= reg << 24; + reg = cdns_mhdp_bus_read(mhdp, VER_L) & 0xff; + mhdp->fw_version = reg; + reg = cdns_mhdp_bus_read(mhdp, VER_H) & 0xff; + mhdp->fw_version |= reg << 8; + reg = cdns_mhdp_bus_read(mhdp, VER_LIB_L_ADDR) & 0xff; + mhdp->fw_version |= reg << 16; + reg = cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) & 0xff; + mhdp->fw_version |= reg << 24; - DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version); + DRM_DEV_DEBUG(mhdp->dev, "firmware version: %x\n", mhdp->fw_version); return 0; } -int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) +int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) { u8 msg[5]; int ret, i; @@ -332,14 +331,14 @@ int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) msg[4] = enable ? FW_ACTIVE : FW_STANDBY; for (i = 0; i < sizeof(msg); i++) { - ret = cdp_dp_mailbox_write(dp, msg[i]); + ret = mhdp_mailbox_write(mhdp, msg[i]); if (ret) goto err_set_firmware_active; } /* read the firmware state */ for (i = 0; i < sizeof(msg); i++) { - ret = cdn_dp_mailbox_read(dp); + ret = mhdp_mailbox_read(mhdp); if (ret < 0) goto err_set_firmware_active; @@ -350,17 +349,17 @@ int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) err_set_firmware_active: if (ret < 0) - DRM_DEV_ERROR(dp->dev, "set firmware active failed\n"); + DRM_DEV_ERROR(mhdp->dev, "set firmware active failed\n"); return ret; } -int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip) +int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) { u8 msg[8]; int ret; - msg[0] = CDN_DP_MAX_LINK_RATE; - msg[1] = lanes | SCRAMBLER_EN; + msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); + msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; msg[2] = VOLTAGE_LEVEL_2; msg[3] = PRE_EMPHASIS_LEVEL_3; msg[4] = PTS1 | PTS2 | PTS3 | PTS4; @@ -368,73 +367,73 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip) msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; msg[7] = ENHANCED; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, - DPTX_SET_HOST_CAPABILITIES, - sizeof(msg), msg); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_SET_HOST_CAPABILITIES, + sizeof(msg), msg); if (ret) goto err_set_host_cap; - ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL, - AUX_HOST_INVERT); + ret = cdns_mhdp_reg_write(mhdp, DP_AUX_SWAP_INVERSION_CONTROL, + AUX_HOST_INVERT); err_set_host_cap: if (ret) - DRM_DEV_ERROR(dp->dev, "set host cap failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret); return ret; } -int cdn_dp_event_config(struct cdn_dp_device *dp) +int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp) { u8 msg[5]; int ret; memset(msg, 0, sizeof(msg)); - msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING; + msg[0] = MHDP_EVENT_ENABLE_HPD | MHDP_EVENT_ENABLE_TRAINING; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT, - sizeof(msg), msg); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_ENABLE_EVENT, sizeof(msg), msg); if (ret) - DRM_DEV_ERROR(dp->dev, "set event config failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "set event config failed: %d\n", ret); return ret; } -u32 cdn_dp_get_event(struct cdn_dp_device *dp) +u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp) { - return readl(dp->regs + SW_EVENTS0); + return cdns_mhdp_bus_read(mhdp, SW_EVENTS0); } -int cdn_dp_get_hpd_status(struct cdn_dp_device *dp) +int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp) { u8 status; int ret; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE, + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, GENERAL_GET_HPD_STATE, 0, NULL); if (ret) goto err_get_hpd; - ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, - DPTX_HPD_STATE, sizeof(status)); + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL, + GENERAL_GET_HPD_STATE, sizeof(status)); if (ret) goto err_get_hpd; - ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status)); + ret = cdns_mhdp_mailbox_read_receive(mhdp, &status, sizeof(status)); if (ret) goto err_get_hpd; return status; err_get_hpd: - DRM_DEV_ERROR(dp->dev, "get hpd status failed: %d\n", ret); + DRM_ERROR("read hpd failed: %d\n", ret); return ret; } -int cdn_dp_get_edid_block(void *data, u8 *edid, +int cdns_mhdp_get_edid_block(void *data, u8 *edid, unsigned int block, size_t length) { - struct cdn_dp_device *dp = data; + struct cdns_mhdp_device *mhdp = data; u8 msg[2], reg[2], i; int ret; @@ -442,22 +441,23 @@ int cdn_dp_get_edid_block(void *data, u8 *edid, msg[0] = block / 2; msg[1] = block % 2; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID, - sizeof(msg), msg); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_GET_EDID, sizeof(msg), msg); if (ret) continue; - ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, - DPTX_GET_EDID, - sizeof(reg) + length); + ret = cdns_mhdp_mailbox_validate_receive(mhdp, + MB_MODULE_ID_DP_TX, + DPTX_GET_EDID, + sizeof(reg) + length); if (ret) continue; - ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); if (ret) continue; - ret = cdn_dp_mailbox_read_receive(dp, edid, length); + ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length); if (ret) continue; @@ -466,13 +466,13 @@ int cdn_dp_get_edid_block(void *data, u8 *edid, } if (ret) - DRM_DEV_ERROR(dp->dev, "get block[%d] edid failed: %d\n", block, - ret); + DRM_DEV_ERROR(mhdp->dev, "get block[%d] edid failed: %d\n", + block, ret); return ret; } -static int cdn_dp_training_start(struct cdn_dp_device *dp) +static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp) { unsigned long timeout; u8 msg, event[2]; @@ -481,26 +481,27 @@ static int cdn_dp_training_start(struct cdn_dp_device *dp) msg = LINK_TRAINING_RUN; /* start training */ - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, - sizeof(msg), &msg); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, + sizeof(msg), &msg); if (ret) goto err_training_start; timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); while (time_before(jiffies, timeout)) { msleep(LINK_TRAINING_RETRY_MS); - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, - DPTX_READ_EVENT, 0, NULL); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_EVENT, 0, NULL); if (ret) goto err_training_start; - ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, - DPTX_READ_EVENT, - sizeof(event)); + ret = cdns_mhdp_mailbox_validate_receive(mhdp, + MB_MODULE_ID_DP_TX, + DPTX_READ_EVENT, + sizeof(event)); if (ret) goto err_training_start; - ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event)); + ret = cdns_mhdp_mailbox_read_receive(mhdp, event, sizeof(event)); if (ret) goto err_training_start; @@ -511,77 +512,77 @@ static int cdn_dp_training_start(struct cdn_dp_device *dp) ret = -ETIMEDOUT; err_training_start: - DRM_DEV_ERROR(dp->dev, "training failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret); return ret; } -static int cdn_dp_get_training_status(struct cdn_dp_device *dp) +static int cdns_mhdp_get_training_status(struct cdns_mhdp_device *mhdp) { u8 status[10]; int ret; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, - 0, NULL); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, + 0, NULL); if (ret) goto err_get_training_status; - ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, - DPTX_READ_LINK_STAT, - sizeof(status)); + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_LINK_STAT, + sizeof(status)); if (ret) goto err_get_training_status; - ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status)); + ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status)); if (ret) goto err_get_training_status; - dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]); - dp->max_lanes = status[1]; + mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]); + mhdp->dp.num_lanes = status[1]; err_get_training_status: if (ret) - DRM_DEV_ERROR(dp->dev, "get training status failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n", ret); return ret; } -int cdn_dp_train_link(struct cdn_dp_device *dp) +int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp) { int ret; - ret = cdn_dp_training_start(dp); + ret = cdns_mhdp_training_start(mhdp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n", ret); return ret; } - ret = cdn_dp_get_training_status(dp); + ret = cdns_mhdp_get_training_status(mhdp); if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n", ret); return ret; } - DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->max_rate, - dp->max_lanes); + DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.rate, + mhdp->dp.num_lanes); return ret; } -int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active) +int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active) { u8 msg; int ret; msg = !!active; - ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO, - sizeof(msg), &msg); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO, + sizeof(msg), &msg); if (ret) - DRM_DEV_ERROR(dp->dev, "set video status failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "set video status failed: %d\n", ret); return ret; } -static int cdn_dp_get_msa_misc(struct video_info *video, - struct drm_display_mode *mode) +static int mhdp_get_msa_misc(struct video_info *video, + struct drm_display_mode *mode) { u32 msa_misc; u8 val[2] = {0}; @@ -627,10 +628,10 @@ static int cdn_dp_get_msa_misc(struct video_info *video, return msa_misc; } -int cdn_dp_config_video(struct cdn_dp_device *dp) +int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp) { - struct video_info *video = &dp->video_info; - struct drm_display_mode *mode = &dp->mode; + struct video_info *video = &mhdp->video_info; + struct drm_display_mode *mode = &mhdp->mode; u64 symbol; u32 val, link_rate, rem; u8 bit_per_pix, tu_size_reg = TU_SIZE; @@ -639,13 +640,13 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? (video->color_depth * 2) : (video->color_depth * 3); - link_rate = dp->max_rate / 1000; + link_rate = mhdp->dp.rate / 1000; - ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); + ret = cdns_mhdp_reg_write(mhdp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); if (ret) goto err_config_video; - ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0); + ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, 0); if (ret) goto err_config_video; @@ -659,13 +660,13 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) do { tu_size_reg += 2; symbol = tu_size_reg * mode->clock * bit_per_pix; - do_div(symbol, dp->max_lanes * link_rate * 8); + do_div(symbol, mhdp->dp.num_lanes * link_rate * 8); rem = do_div(symbol, 1000); if (tu_size_reg > 64) { ret = -EINVAL; - DRM_DEV_ERROR(dp->dev, + DRM_DEV_ERROR(mhdp->dev, "tu error, clk:%d, lanes:%d, rate:%d\n", - mode->clock, dp->max_lanes, link_rate); + mode->clock, mhdp->dp.num_lanes, link_rate); goto err_config_video; } } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || @@ -673,16 +674,16 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) val = symbol + (tu_size_reg << 8); val |= TU_CNT_RST_EN; - ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val); + ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_TU, val); if (ret) goto err_config_video; /* set the FIFO Buffer size */ val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; - val /= (dp->max_lanes * link_rate); + val /= (mhdp->dp.num_lanes * link_rate); val = div_u64(8 * (symbol + 1), bit_per_pix) - val; val += 2; - ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); + ret = cdns_mhdp_reg_write(mhdp, DP_VC_TABLE(15), val); switch (video->color_depth) { case 6: @@ -703,136 +704,136 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) }; val += video->color_fmt << 8; - ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val); + ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_PXL_REPR, val); if (ret) goto err_config_video; val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0; val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0; - ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val); + ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_SP, val); if (ret) goto err_config_video; val = (mode->hsync_start - mode->hdisplay) << 16; val |= mode->htotal - mode->hsync_end; - ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val); + ret = cdns_mhdp_reg_write(mhdp, DP_FRONT_BACK_PORCH, val); if (ret) goto err_config_video; val = mode->hdisplay * bit_per_pix / 8; - ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val); + ret = cdns_mhdp_reg_write(mhdp, DP_BYTE_COUNT, val); if (ret) goto err_config_video; val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); - ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val); + ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_0, val); if (ret) goto err_config_video; val = mode->hsync_end - mode->hsync_start; val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15); - ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val); + ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_1, val); if (ret) goto err_config_video; val = mode->vtotal; val |= (mode->vtotal - mode->vsync_start) << 16; - ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val); + ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_0, val); if (ret) goto err_config_video; val = mode->vsync_end - mode->vsync_start; val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15); - ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val); + ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_1, val); if (ret) goto err_config_video; - val = cdn_dp_get_msa_misc(video, mode); - ret = cdn_dp_reg_write(dp, MSA_MISC, val); + val = mhdp_get_msa_misc(video, mode); + ret = cdns_mhdp_reg_write(mhdp, MSA_MISC, val); if (ret) goto err_config_video; - ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1); + ret = cdns_mhdp_reg_write(mhdp, STREAM_CONFIG, 1); if (ret) goto err_config_video; val = mode->hsync_end - mode->hsync_start; val |= mode->hdisplay << 16; - ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val); + ret = cdns_mhdp_reg_write(mhdp, DP_HORIZONTAL, val); if (ret) goto err_config_video; val = mode->vdisplay; val |= (mode->vtotal - mode->vsync_start) << 16; - ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val); + ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_0, val); if (ret) goto err_config_video; val = mode->vtotal; - ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val); + ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_1, val); if (ret) goto err_config_video; - ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, 0); + ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 2, 1, 0); err_config_video: if (ret) - DRM_DEV_ERROR(dp->dev, "config video failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "config video failed: %d\n", ret); return ret; } -int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) +int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, struct audio_info *audio) { int ret; - ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0); + ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0); if (ret) { - DRM_DEV_ERROR(dp->dev, "audio stop failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret); return ret; } - writel(0, dp->regs + SPDIF_CTRL_ADDR); + cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR); /* clearn the audio config and reset */ - writel(0, dp->regs + AUDIO_SRC_CNTL); - writel(0, dp->regs + AUDIO_SRC_CNFG); - writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL); - writel(0, dp->regs + AUDIO_SRC_CNTL); + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG); + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL); + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); /* reset smpl2pckt component */ - writel(0, dp->regs + SMPL2PKT_CNTL); - writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL); - writel(0, dp->regs + SMPL2PKT_CNTL); + cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL); + cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); /* reset FIFO */ - writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL); - writel(0, dp->regs + FIFO_CNTL); + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL); + cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL); - if (audio->format == AFMT_SPDIF) - clk_disable_unprepare(dp->spdif_clk); + if (audio->format == AFMT_SPDIF_INT) + clk_disable_unprepare(mhdp->spdif_clk); return 0; } -int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable) +int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable) { - int ret; + int ret = true; - ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable); + ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable); if (ret) - DRM_DEV_ERROR(dp->dev, "audio mute failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret); return ret; } -static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, - struct audio_info *audio) +static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, + struct audio_info *audio) { int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; u32 val; if (audio->channels == 2) { - if (dp->max_lanes == 1) + if (mhdp->dp.num_lanes == 1) sub_pckt_num = 2; else sub_pckt_num = 4; @@ -842,15 +843,15 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, i2s_port_en_val = 3; } - writel(0x0, dp->regs + SPDIF_CTRL_ADDR); + cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR); - writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); + cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); val = MAX_NUM_CH(audio->channels); val |= NUM_OF_I2S_PORTS(audio->channels); val |= AUDIO_TYPE_LPCM; val |= CFG_SUB_PCKT_NUM(sub_pckt_num); - writel(val, dp->regs + SMPL2PKT_CNFG); + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); if (audio->sample_width == 16) val = 0; @@ -862,7 +863,7 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, val |= AUDIO_CH_NUM(audio->channels); val |= I2S_DEC_PORT_EN(i2s_port_en_val); val |= TRANS_SMPL_WIDTH_32; - writel(val, dp->regs + AUDIO_SRC_CNFG); + cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG); for (i = 0; i < (audio->channels + 1) / 2; i++) { if (audio->sample_width == 16) @@ -871,7 +872,7 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, val = (0x0b << 8) | (0x0b << 20); val |= ((2 * i) << 4) | ((2 * i + 1) << 16); - writel(val, dp->regs + STTS_BIT_CH(i)); + cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i)); } switch (audio->sample_rate) { @@ -905,56 +906,57 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, break; } val |= 4; - writel(val, dp->regs + COM_CH_STTS_BITS); + cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS); - writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); - writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL); + cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); + cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL); } -static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) +static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp) { u32 val; - writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); + cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); - writel(val, dp->regs + SMPL2PKT_CNFG); - writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); + cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; - writel(val, dp->regs + SPDIF_CTRL_ADDR); + cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR); - clk_prepare_enable(dp->spdif_clk); - clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK); + clk_prepare_enable(mhdp->spdif_clk); + clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK); } -int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio) +int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, + struct audio_info *audio) { int ret; /* reset the spdif clk before config */ - if (audio->format == AFMT_SPDIF) { - reset_control_assert(dp->spdif_rst); - reset_control_deassert(dp->spdif_rst); + if (audio->format == AFMT_SPDIF_INT) { + reset_control_assert(mhdp->spdif_rst); + reset_control_deassert(mhdp->spdif_rst); } - ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC); + ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC); if (ret) goto err_audio_config; - ret = cdn_dp_reg_write(dp, CM_CTRL, 0); + ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0); if (ret) goto err_audio_config; if (audio->format == AFMT_I2S) - cdn_dp_audio_config_i2s(dp, audio); - else if (audio->format == AFMT_SPDIF) - cdn_dp_audio_config_spdif(dp); + cdns_mhdp_audio_config_i2s(mhdp, audio); + else if (audio->format == AFMT_SPDIF_INT) + cdns_mhdp_audio_config_spdif(mhdp); - ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); + ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); err_audio_config: if (ret) - DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n", ret); + DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret); return ret; } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h index 441248b7a79e..3c1235ff8e1c 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -7,6 +7,8 @@ #ifndef _CDN_DP_REG_H #define _CDN_DP_REG_H +#include +#include #include #define ADDR_IMEM 0x10000 @@ -308,17 +310,22 @@ #define MB_SIZE_LSB_ID 3 #define MB_DATA_ID 4 -#define MB_MODULE_ID_DP_TX 0x01 +#define MB_MODULE_ID_DP_TX 0x01 +#define MB_MODULE_ID_HDMI_TX 0x03 #define MB_MODULE_ID_HDCP_TX 0x07 #define MB_MODULE_ID_HDCP_RX 0x08 #define MB_MODULE_ID_HDCP_GENERAL 0x09 -#define MB_MODULE_ID_GENERAL 0x0a +#define MB_MODULE_ID_GENERAL 0x0A /* general opcode */ #define GENERAL_MAIN_CONTROL 0x01 #define GENERAL_TEST_ECHO 0x02 #define GENERAL_BUS_SETTINGS 0x03 #define GENERAL_TEST_ACCESS 0x04 +#define GENERAL_WRITE_REGISTER 0x05 +#define GENERAL_WRITE_FIELD 0x06 +#define GENERAL_READ_REGISTER 0x07 +#define GENERAL_GET_HPD_STATE 0x11 #define DPTX_SET_POWER_MNG 0x00 #define DPTX_SET_HOST_CAPABILITIES 0x01 @@ -342,8 +349,8 @@ #define FW_STANDBY 0 #define FW_ACTIVE 1 -#define DPTX_EVENT_ENABLE_HPD BIT(0) -#define DPTX_EVENT_ENABLE_TRAINING BIT(1) +#define MHDP_EVENT_ENABLE_HPD BIT(0) +#define MHDP_EVENT_ENABLE_TRAINING BIT(1) #define LINK_TRAINING_NOT_ACTIVE 0 #define LINK_TRAINING_RUN 1 @@ -387,7 +394,7 @@ #define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT BIT(7) #define TU_SIZE 30 -#define CDN_DP_MAX_LINK_RATE DP_LINK_BW_5_4 +#define CDNS_DP_MAX_LINK_RATE 540000 /* audio */ #define AUDIO_PACK_EN BIT(8) @@ -451,24 +458,100 @@ enum vic_bt_type { BT_709 = 0x1, }; -void cdn_dp_clock_reset(struct cdn_dp_device *dp); - -void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk); -int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, - u32 i_size, const u32 *d_mem, u32 d_size); -int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable); -int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip); -int cdn_dp_event_config(struct cdn_dp_device *dp); -u32 cdn_dp_get_event(struct cdn_dp_device *dp); -int cdn_dp_get_hpd_status(struct cdn_dp_device *dp); -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value); -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len); -int cdn_dp_get_edid_block(void *dp, u8 *edid, - unsigned int block, size_t length); -int cdn_dp_train_link(struct cdn_dp_device *dp); -int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active); -int cdn_dp_config_video(struct cdn_dp_device *dp); -int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio); -int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable); -int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio); +enum audio_format { + AFMT_I2S = 0, + AFMT_SPDIF_INT = 1, + AFMT_SPDIF_EXT = 2, + AFMT_UNUSED, +}; + +struct audio_info { + enum audio_format format; + int sample_rate; + int channels; + int sample_width; + int connector_type; +}; + +enum vic_pxl_encoding_format { + PXL_RGB = 0x1, + YCBCR_4_4_4 = 0x2, + YCBCR_4_2_2 = 0x4, + YCBCR_4_2_0 = 0x8, + Y_ONLY = 0x10, +}; + +struct video_info { + bool h_sync_polarity; + bool v_sync_polarity; + bool interlaced; + int color_depth; + enum vic_pxl_encoding_format color_fmt; +}; + +struct cdns_mhdp_bridge { + struct cdns_mhdp_device *mhdp; + struct drm_bridge base; + int pbn; + int8_t stream_id; + struct cdns_mhdp_connector *connector; + bool is_active; +}; + +struct cdns_mhdp_connector { + struct drm_connector base; + struct cdns_mhdp_bridge *bridge; +}; + +struct cdns_mhdp_device { + struct device *dev; + struct cdns_mhdp_connector connector; + + void __iomem *regs_base; + struct reset_control *spdif_rst; + + struct video_info video_info; + struct drm_display_mode mode; + + struct platform_device *audio_pdev; + struct audio_info audio_info; + struct clk *spdif_clk; + + unsigned int fw_version; + + union { + struct _dp_data { + u32 rate; + u8 num_lanes; + struct drm_dp_aux aux; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + } dp; + }; +}; + +int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); +int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); +void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp); +void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk); +int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, + u32 i_size, const u32 *d_mem, u32 d_size); +int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable); +int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip); +int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp); +u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp); +int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp); +int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value); +int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, + u32 addr, u8 *data, u16 len); +int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid, + unsigned int block, size_t length); +int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp); +int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active); +int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp); +int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, + struct audio_info *audio); +int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable); +int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, + struct audio_info *audio); + #endif /* _CDN_DP_REG_H */ From patchwork Mon Jun 1 06:17:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581395 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CBEA5139A for ; Mon, 1 Jun 2020 06:23:48 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A619920734 for ; Mon, 1 Jun 2020 06:23:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="mYDZibtF" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org A619920734 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=ULhMQJnUba/McLa+v0CwngL8jBPA+sBdeQfyzDoKTyw=; b=mYDZibtF/svsFPgKIKsOHfyT9W CohiaeSNkTDvIAdzqpMkR6iyguzU58NYeUDwWqTcsgIrBtWEfHl4bM6kleZQkqkaK7DGE4738DMds 6oWnBjWexwI43Gj7AQzGWYNv6FKlgPWF5oP1RpXsOx9q7gGMgf9O5JmYOct/3evqEyNxy2jdoigwO w6eT1+0k0WrhM0ThJQcdl/P0oohcMSyDLDMkCvh+v3Jnc6hXq31ee9dQPJjuKmwLibtgjrQPrq11e YovvKm21CD21GKTJRhIf5ptehSSFHIxSoohskHiat34t8MXxlvQTEMVeevh3Ol49I1OqolYvKQay1 /6/Pa62w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdrJ-0007dj-47; Mon, 01 Jun 2020 06:23:41 +0000 Received: from inva021.nxp.com ([92.121.34.21]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdqy-0007O5-N4; Mon, 01 Jun 2020 06:23:26 +0000 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 85D322004D7; Mon, 1 Jun 2020 08:23:19 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 71D232004B9; Mon, 1 Jun 2020 08:23:13 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id 44904402E8; Mon, 1 Jun 2020 14:23:06 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 2/7] drm: bridge: cadence: Create cadence fold Date: Mon, 1 Jun 2020 14:17:32 +0800 Message-Id: X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232321_171213_EA16A7A5 X-CRM114-Status: GOOD ( 18.27 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.21 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu Create new directory drm/bridge/cadence. Cadence MHDP DP and HDMI bridge dirver will added later. drm/rockchip/cdn-dp-reg.c will separate to three files. - cdns-mhdp-common.c: Provide basic MHDP register read/write via mailbox. public firmware load, event, edid and HPD functions that will be refered in MHDP DP and HDMI drivers. - cdns-hdp-audio.c: MHDP Audio specific functions - cdns-mhdp-dp.c: MHDP DP specific functions, DPCD and link traning functions. Signed-off-by: Sandor Yu --- drivers/gpu/drm/bridge/Kconfig | 2 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/cadence/Kconfig | 7 + drivers/gpu/drm/bridge/cadence/Makefile | 3 + .../gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 198 +++++++++ .../cadence/cdns-mhdp-common.c} | 418 ++++-------------- .../gpu/drm/bridge/cadence/cdns-mhdp-common.h | 23 + drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c | 174 ++++++++ drivers/gpu/drm/rockchip/Kconfig | 1 + drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 2 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 +- .../drm/bridge/cdns-mhdp.h | 9 +- 13 files changed, 494 insertions(+), 348 deletions(-) create mode 100644 drivers/gpu/drm/bridge/cadence/Kconfig create mode 100644 drivers/gpu/drm/bridge/cadence/Makefile create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c rename drivers/gpu/drm/{rockchip/cdn-dp-reg.c => bridge/cadence/cdns-mhdp-common.c} (64%) create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c rename drivers/gpu/drm/rockchip/cdn-dp-reg.h => include/drm/bridge/cdns-mhdp.h (98%) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index aaed2347ace9..c764150fb228 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -184,6 +184,8 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" +source "drivers/gpu/drm/bridge/cadence/Kconfig" + source "drivers/gpu/drm/bridge/synopsys/Kconfig" endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 6fb062b5b0f0..5d61c1d43b67 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o obj-y += analogix/ +obj-y += cadence/ obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig new file mode 100644 index 000000000000..48c1b0f77dc6 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -0,0 +1,7 @@ +config DRM_CDNS_MHDP + tristate "Cadence MHDP COMMON API driver" + select DRM_KMS_HELPER + select DRM_PANEL_BRIDGE + depends on OF + help + Support Cadence MHDP API library. diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile new file mode 100644 index 000000000000..ddb2ba4fb852 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-audio.o cdns-mhdp-dp.o +obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c new file mode 100644 index 000000000000..8f51de0672ab --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cdns-mhdp-common.h" + +#define CDNS_DP_SPDIF_CLK 200000000 + +int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, struct audio_info *audio) +{ + int ret; + + ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret); + return ret; + } + + cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR); + + /* clearn the audio config and reset */ + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG); + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL); + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); + + /* reset smpl2pckt component */ + cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL); + cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); + + /* reset FIFO */ + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL); + cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL); + + if (audio->format == AFMT_SPDIF_INT) + clk_disable_unprepare(mhdp->spdif_clk); + + return 0; +} +EXPORT_SYMBOL(cdns_mhdp_audio_stop); + +int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable) +{ + int ret = true; + + ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable); + if (ret) + DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(cdns_mhdp_audio_mute); + +static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, + struct audio_info *audio) +{ + int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; + u32 val; + + if (audio->channels == 2) { + if (mhdp->dp.num_lanes == 1) + sub_pckt_num = 2; + else + sub_pckt_num = 4; + + i2s_port_en_val = 1; + } else if (audio->channels == 4) { + i2s_port_en_val = 3; + } + + cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR); + + cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); + + val = MAX_NUM_CH(audio->channels); + val |= NUM_OF_I2S_PORTS(audio->channels); + val |= AUDIO_TYPE_LPCM; + val |= CFG_SUB_PCKT_NUM(sub_pckt_num); + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); + + if (audio->sample_width == 16) + val = 0; + else if (audio->sample_width == 24) + val = 1 << 9; + else + val = 2 << 9; + + val |= AUDIO_CH_NUM(audio->channels); + val |= I2S_DEC_PORT_EN(i2s_port_en_val); + val |= TRANS_SMPL_WIDTH_32; + cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG); + + for (i = 0; i < (audio->channels + 1) / 2; i++) { + if (audio->sample_width == 16) + val = (0x02 << 8) | (0x02 << 20); + else if (audio->sample_width == 24) + val = (0x0b << 8) | (0x0b << 20); + + val |= ((2 * i) << 4) | ((2 * i + 1) << 16); + cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i)); + } + + switch (audio->sample_rate) { + case 32000: + val = SAMPLING_FREQ(3) | + ORIGINAL_SAMP_FREQ(0xc); + break; + case 44100: + val = SAMPLING_FREQ(0) | + ORIGINAL_SAMP_FREQ(0xf); + break; + case 48000: + val = SAMPLING_FREQ(2) | + ORIGINAL_SAMP_FREQ(0xd); + break; + case 88200: + val = SAMPLING_FREQ(8) | + ORIGINAL_SAMP_FREQ(0x7); + break; + case 96000: + val = SAMPLING_FREQ(0xa) | + ORIGINAL_SAMP_FREQ(5); + break; + case 176400: + val = SAMPLING_FREQ(0xc) | + ORIGINAL_SAMP_FREQ(3); + break; + case 192000: + val = SAMPLING_FREQ(0xe) | + ORIGINAL_SAMP_FREQ(1); + break; + } + val |= 4; + cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS); + + cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); + cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL); +} + +static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp) +{ + u32 val; + + cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); + + val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); + cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); + + val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; + cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR); + + clk_prepare_enable(mhdp->spdif_clk); + clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK); +} + +int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, + struct audio_info *audio) +{ + int ret; + + /* reset the spdif clk before config */ + if (audio->format == AFMT_SPDIF_INT) { + reset_control_assert(mhdp->spdif_rst); + reset_control_deassert(mhdp->spdif_rst); + } + + ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC); + if (ret) + goto err_audio_config; + + ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0); + if (ret) + goto err_audio_config; + + if (audio->format == AFMT_I2S) + cdns_mhdp_audio_config_i2s(mhdp, audio); + else if (audio->format == AFMT_SPDIF_INT) + cdns_mhdp_audio_config_spdif(mhdp); + + ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); + +err_audio_config: + if (ret) + DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret); + return ret; +} +EXPORT_SYMBOL(cdns_mhdp_audio_config); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c similarity index 64% rename from drivers/gpu/drm/rockchip/cdn-dp-reg.c rename to drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c index f7ccf93fe5e1..efb39cf5f48b 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c @@ -11,16 +11,14 @@ #include #include #include - -#include "cdn-dp-core.h" -#include "cdn-dp-reg.h" +#include +#include +#include #define CDNS_DP_SPDIF_CLK 200000000 #define FW_ALIVE_TIMEOUT_US 1000000 #define MAILBOX_RETRY_US 1000 #define MAILBOX_TIMEOUT_US 5000000 -#define LINK_TRAINING_RETRY_MS 20 -#define LINK_TRAINING_TIMEOUT_MS 500 u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset) { @@ -36,6 +34,7 @@ void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk) { cdns_mhdp_bus_write(clk / 1000000, mhdp, SW_CLK_H); } +EXPORT_SYMBOL(cdns_mhdp_set_fw_clk); void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp) { @@ -85,6 +84,26 @@ void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp) /* enable Mailbox and PIF interrupt */ cdns_mhdp_bus_write(0, mhdp, APB_INT_MASK); } +EXPORT_SYMBOL(cdns_mhdp_clock_reset); + +bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp) +{ + u32 alive, newalive; + u8 retries_left = 50; + + alive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE); + + while (retries_left--) { + udelay(2); + + newalive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE); + if (alive == newalive) + continue; + return true; + } + return false; +} +EXPORT_SYMBOL(cdns_mhdp_check_alive); static int mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) { @@ -190,6 +209,51 @@ int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id, return 0; } +int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr) +{ + u8 msg[4], resp[8]; + u32 val; + int ret; + + if (addr == 0) { + ret = -EINVAL; + goto err_reg_read; + } + + put_unaligned_be32(addr, msg); + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, + GENERAL_READ_REGISTER, + sizeof(msg), msg); + if (ret) + goto err_reg_read; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL, + GENERAL_READ_REGISTER, + sizeof(resp)); + if (ret) + goto err_reg_read; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, resp, sizeof(resp)); + if (ret) + goto err_reg_read; + + /* Returned address value should be the same as requested */ + if (memcmp(msg, resp, sizeof(msg))) { + ret = -EINVAL; + goto err_reg_read; + } + + val = get_unaligned_be32(resp + 4); + + return val; + +err_reg_read: + DRM_DEV_ERROR(mhdp->dev, "Failed to read register.\n"); + + return ret; +} + int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val) { u8 msg[8]; @@ -215,68 +279,6 @@ int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, DPTX_WRITE_FIELD, sizeof(field), field); } -int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, - u32 addr, u8 *data, u16 len) -{ - u8 msg[5], reg[5]; - int ret; - - put_unaligned_be16(len, msg); - put_unaligned_be24(addr, msg + 2); - - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, - DPTX_READ_DPCD, sizeof(msg), msg); - if (ret) - goto err_dpcd_read; - - ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, - DPTX_READ_DPCD, - sizeof(reg) + len); - if (ret) - goto err_dpcd_read; - - ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); - if (ret) - goto err_dpcd_read; - - ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len); - -err_dpcd_read: - return ret; -} - -int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value) -{ - u8 msg[6], reg[5]; - int ret; - - put_unaligned_be16(1, msg); - put_unaligned_be24(addr, msg + 2); - msg[5] = value; - - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, - DPTX_WRITE_DPCD, sizeof(msg), msg); - if (ret) - goto err_dpcd_write; - - ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, - DPTX_WRITE_DPCD, sizeof(reg)); - if (ret) - goto err_dpcd_write; - - ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); - if (ret) - goto err_dpcd_write; - - if (addr != get_unaligned_be24(reg + 2)) - ret = -EINVAL; - -err_dpcd_write: - if (ret) - DRM_DEV_ERROR(mhdp->dev, "dpcd write failed: %d\n", ret); - return ret; -} - int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, u32 i_size, const u32 *d_mem, u32 d_size) { @@ -318,6 +320,7 @@ int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, return 0; } +EXPORT_SYMBOL(cdns_mhdp_load_firmware); int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) { @@ -352,6 +355,7 @@ int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) DRM_DEV_ERROR(mhdp->dev, "set firmware active failed\n"); return ret; } +EXPORT_SYMBOL(cdns_mhdp_set_firmware_active); int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) { @@ -381,6 +385,7 @@ int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret); return ret; } +EXPORT_SYMBOL(cdns_mhdp_set_host_cap); int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp) { @@ -398,11 +403,13 @@ int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp) return ret; } +EXPORT_SYMBOL(cdns_mhdp_event_config); u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp) { return cdns_mhdp_bus_read(mhdp, SW_EVENTS0); } +EXPORT_SYMBOL(cdns_mhdp_get_event); int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp) { @@ -429,6 +436,7 @@ int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp) DRM_ERROR("read hpd failed: %d\n", ret); return ret; } +EXPORT_SYMBOL(cdns_mhdp_read_hpd); int cdns_mhdp_get_edid_block(void *data, u8 *edid, unsigned int block, size_t length) @@ -471,100 +479,7 @@ int cdns_mhdp_get_edid_block(void *data, u8 *edid, return ret; } - -static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp) -{ - unsigned long timeout; - u8 msg, event[2]; - int ret; - - msg = LINK_TRAINING_RUN; - - /* start training */ - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, - sizeof(msg), &msg); - if (ret) - goto err_training_start; - - timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); - while (time_before(jiffies, timeout)) { - msleep(LINK_TRAINING_RETRY_MS); - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, - DPTX_READ_EVENT, 0, NULL); - if (ret) - goto err_training_start; - - ret = cdns_mhdp_mailbox_validate_receive(mhdp, - MB_MODULE_ID_DP_TX, - DPTX_READ_EVENT, - sizeof(event)); - if (ret) - goto err_training_start; - - ret = cdns_mhdp_mailbox_read_receive(mhdp, event, sizeof(event)); - if (ret) - goto err_training_start; - - if (event[1] & EQ_PHASE_FINISHED) - return 0; - } - - ret = -ETIMEDOUT; - -err_training_start: - DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret); - return ret; -} - -static int cdns_mhdp_get_training_status(struct cdns_mhdp_device *mhdp) -{ - u8 status[10]; - int ret; - - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, - 0, NULL); - if (ret) - goto err_get_training_status; - - ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, - DPTX_READ_LINK_STAT, - sizeof(status)); - if (ret) - goto err_get_training_status; - - ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status)); - if (ret) - goto err_get_training_status; - - mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]); - mhdp->dp.num_lanes = status[1]; - -err_get_training_status: - if (ret) - DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n", ret); - return ret; -} - -int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp) -{ - int ret; - - ret = cdns_mhdp_training_start(mhdp); - if (ret) { - DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n", ret); - return ret; - } - - ret = cdns_mhdp_get_training_status(mhdp); - if (ret) { - DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n", ret); - return ret; - } - - DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.rate, - mhdp->dp.num_lanes); - return ret; -} +EXPORT_SYMBOL(cdns_mhdp_get_edid_block); int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active) { @@ -580,6 +495,7 @@ int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active) return ret; } +EXPORT_SYMBOL(cdns_mhdp_set_video_status); static int mhdp_get_msa_misc(struct video_info *video, struct drm_display_mode *mode) @@ -781,182 +697,4 @@ int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp) DRM_DEV_ERROR(mhdp->dev, "config video failed: %d\n", ret); return ret; } - -int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, struct audio_info *audio) -{ - int ret; - - ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0); - if (ret) { - DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret); - return ret; - } - - cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR); - - /* clearn the audio config and reset */ - cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); - cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG); - cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL); - cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); - - /* reset smpl2pckt component */ - cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); - cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL); - cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); - - /* reset FIFO */ - cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL); - cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL); - - if (audio->format == AFMT_SPDIF_INT) - clk_disable_unprepare(mhdp->spdif_clk); - - return 0; -} - -int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable) -{ - int ret = true; - - ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable); - if (ret) - DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret); - - return ret; -} - -static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, - struct audio_info *audio) -{ - int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; - u32 val; - - if (audio->channels == 2) { - if (mhdp->dp.num_lanes == 1) - sub_pckt_num = 2; - else - sub_pckt_num = 4; - - i2s_port_en_val = 1; - } else if (audio->channels == 4) { - i2s_port_en_val = 3; - } - - cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR); - - cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); - - val = MAX_NUM_CH(audio->channels); - val |= NUM_OF_I2S_PORTS(audio->channels); - val |= AUDIO_TYPE_LPCM; - val |= CFG_SUB_PCKT_NUM(sub_pckt_num); - cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); - - if (audio->sample_width == 16) - val = 0; - else if (audio->sample_width == 24) - val = 1 << 9; - else - val = 2 << 9; - - val |= AUDIO_CH_NUM(audio->channels); - val |= I2S_DEC_PORT_EN(i2s_port_en_val); - val |= TRANS_SMPL_WIDTH_32; - cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG); - - for (i = 0; i < (audio->channels + 1) / 2; i++) { - if (audio->sample_width == 16) - val = (0x02 << 8) | (0x02 << 20); - else if (audio->sample_width == 24) - val = (0x0b << 8) | (0x0b << 20); - - val |= ((2 * i) << 4) | ((2 * i + 1) << 16); - cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i)); - } - - switch (audio->sample_rate) { - case 32000: - val = SAMPLING_FREQ(3) | - ORIGINAL_SAMP_FREQ(0xc); - break; - case 44100: - val = SAMPLING_FREQ(0) | - ORIGINAL_SAMP_FREQ(0xf); - break; - case 48000: - val = SAMPLING_FREQ(2) | - ORIGINAL_SAMP_FREQ(0xd); - break; - case 88200: - val = SAMPLING_FREQ(8) | - ORIGINAL_SAMP_FREQ(0x7); - break; - case 96000: - val = SAMPLING_FREQ(0xa) | - ORIGINAL_SAMP_FREQ(5); - break; - case 176400: - val = SAMPLING_FREQ(0xc) | - ORIGINAL_SAMP_FREQ(3); - break; - case 192000: - val = SAMPLING_FREQ(0xe) | - ORIGINAL_SAMP_FREQ(1); - break; - } - val |= 4; - cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS); - - cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); - cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL); -} - -static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp) -{ - u32 val; - - cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); - - val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); - cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); - cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); - - val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; - cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR); - - clk_prepare_enable(mhdp->spdif_clk); - clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK); -} - -int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, - struct audio_info *audio) -{ - int ret; - - /* reset the spdif clk before config */ - if (audio->format == AFMT_SPDIF_INT) { - reset_control_assert(mhdp->spdif_rst); - reset_control_deassert(mhdp->spdif_rst); - } - - ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC); - if (ret) - goto err_audio_config; - - ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0); - if (ret) - goto err_audio_config; - - if (audio->format == AFMT_I2S) - cdns_mhdp_audio_config_i2s(mhdp, audio); - else if (audio->format == AFMT_SPDIF_INT) - cdns_mhdp_audio_config_spdif(mhdp); - - ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); - -err_audio_config: - if (ret) - DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret); - return ret; -} +EXPORT_SYMBOL(cdns_mhdp_config_video); diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h new file mode 100644 index 000000000000..1f093a2deaa7 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef _CDNS_MHDP_H_ +#define _CDNS_MHDP_H_ + +#include + +/* mhdp common */ +u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset); +void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset); +int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id, + u8 opcode, u16 size, u8 *message); +int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); +int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp, + u8 *buff, u16 buff_size); +int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp, + u8 module_id, u8 opcode, u16 req_size); +int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); +int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); +int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, + u8 start_bit, u8 bits_no, u32 val); + +#endif diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c new file mode 100644 index 000000000000..c8160f321aca --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "cdns-mhdp-common.h" + +#define LINK_TRAINING_TIMEOUT_MS 500 +#define LINK_TRAINING_RETRY_MS 20 + +int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, + u32 addr, u8 *data, u16 len) +{ + u8 msg[5], reg[5]; + int ret; + + put_unaligned_be16(len, msg); + put_unaligned_be24(addr, msg + 2); + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_DPCD, sizeof(msg), msg); + if (ret) + goto err_dpcd_read; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_DPCD, + sizeof(reg) + len); + if (ret) + goto err_dpcd_read; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); + if (ret) + goto err_dpcd_read; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len); + +err_dpcd_read: + return ret; +} +EXPORT_SYMBOL(cdns_mhdp_dpcd_read); + +int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value) +{ + u8 msg[6], reg[5]; + int ret; + + put_unaligned_be16(1, msg); + put_unaligned_be24(addr, msg + 2); + msg[5] = value; + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_WRITE_DPCD, sizeof(msg), msg); + if (ret) + goto err_dpcd_write; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, + DPTX_WRITE_DPCD, sizeof(reg)); + if (ret) + goto err_dpcd_write; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); + if (ret) + goto err_dpcd_write; + + if (addr != get_unaligned_be24(reg + 2)) + ret = -EINVAL; + +err_dpcd_write: + if (ret) + DRM_DEV_ERROR(mhdp->dev, "dpcd write failed: %d\n", ret); + return ret; +} +EXPORT_SYMBOL(cdns_mhdp_dpcd_write); + +static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp) +{ + unsigned long timeout; + u8 msg, event[2]; + int ret; + + msg = LINK_TRAINING_RUN; + + /* start training */ + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_TRAINING_CONTROL, sizeof(msg), &msg); + if (ret) + goto err_training_start; + + timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); + while (time_before(jiffies, timeout)) { + msleep(LINK_TRAINING_RETRY_MS); + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_EVENT, 0, NULL); + if (ret) + goto err_training_start; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, + MB_MODULE_ID_DP_TX, + DPTX_READ_EVENT, + sizeof(event)); + if (ret) + goto err_training_start; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, event, + sizeof(event)); + if (ret) + goto err_training_start; + + if (event[1] & EQ_PHASE_FINISHED) + return 0; + } + + ret = -ETIMEDOUT; + +err_training_start: + DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret); + return ret; +} + +static int cdns_mhdp_get_training_status(struct cdns_mhdp_device *mhdp) +{ + u8 status[10]; + int ret; + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_LINK_STAT, 0, NULL); + if (ret) + goto err_get_training_status; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, + DPTX_READ_LINK_STAT, + sizeof(status)); + if (ret) + goto err_get_training_status; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status)); + if (ret) + goto err_get_training_status; + + mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]); + mhdp->dp.num_lanes = status[1]; + +err_get_training_status: + if (ret) + DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n", + ret); + return ret; +} + +int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp) +{ + int ret; + + ret = cdns_mhdp_training_start(mhdp); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n", + ret); + return ret; + } + + ret = cdns_mhdp_get_training_status(mhdp); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n", + ret); + return ret; + } + + DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.rate, + mhdp->dp.num_lanes); + return ret; +} +EXPORT_SYMBOL(cdns_mhdp_train_link); diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 310aa1546893..af1e9ac5c5ef 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -30,6 +30,7 @@ config ROCKCHIP_ANALOGIX_DP config ROCKCHIP_CDN_DP bool "Rockchip cdn DP" depends on EXTCON=y || (EXTCON=m && DRM_ROCKCHIP=m) + depends on DRM_CDNS_MHDP help This selects support for Rockchip SoC specific extensions for the cdn DP driver. If you want to enable Dp on diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 17a9e7eb2130..bd013659404f 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -8,7 +8,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index b6aa21779608..06fd82b217b6 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #include "cdn-dp-core.h" -#include "cdn-dp-reg.h" #include "rockchip_drm_vop.h" #define connector_to_dp(c) \ diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index d5dcb75b2398..8b1b15b92503 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -7,11 +7,11 @@ #ifndef _CDN_DP_CORE_H #define _CDN_DP_CORE_H +#include #include #include #include -#include "cdn-dp-reg.h" #include "rockchip_drm_drv.h" #define MAX_PHY 2 diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/include/drm/bridge/cdns-mhdp.h similarity index 98% rename from drivers/gpu/drm/rockchip/cdn-dp-reg.h rename to include/drm/bridge/cdns-mhdp.h index 3c1235ff8e1c..c8170e6048f7 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/include/drm/bridge/cdns-mhdp.h @@ -4,11 +4,12 @@ * Author: Chris Zhong */ -#ifndef _CDN_DP_REG_H -#define _CDN_DP_REG_H +#ifndef CDNS_MHDP_H_ +#define CDNS_MHDP_H_ #include #include +#include #include #define ADDR_IMEM 0x10000 @@ -529,8 +530,6 @@ struct cdns_mhdp_device { }; }; -int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); -int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp); void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk); int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, @@ -554,4 +553,4 @@ int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable); int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, struct audio_info *audio); -#endif /* _CDN_DP_REG_H */ +#endif /* CDNS_MHDP_H_ */ From patchwork Mon Jun 1 06:17:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581399 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5306F139A for ; Mon, 1 Jun 2020 06:24:18 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id F3F4E20734 for ; Mon, 1 Jun 2020 06:24:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="cB1Yce1I" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org F3F4E20734 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=J3WBPLv6HL1x9f4eoeJ5xQpggD9rZFMkdrAyKJzMon8=; b=cB1Yce1IZg9bEtYdC8mgYDOj0o QUkDKvkfYXgfL+9aBpdyirwwyRn1IO67eeXjJWS71Wtqckk0BUwpHJqgsgeMPmFjfOl9djp923CLW xKzWs3lTflABFahjpBPeG1OwhinQmLxBCJgmeTQcFT7+Tp9IY3swI/XRFrW7pvbjkuwKZUKhfisO6 Vhs19wcesL7yjW3tRrPnNX9pkwMJf6zC8/0Mrr9LdADxgLbEDzPqZv00ZQxm000komJb63kN9bOlI rTMZ/Zzqrd3lVtVlpoQO6Yq6pPU7lapYgO/NGUmdtoYugdNGSxKX+EkYvKBMkaQNgKFM770IkRojE 8MvQQsVA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdrk-0007wq-VD; Mon, 01 Jun 2020 06:24:08 +0000 Received: from inva021.nxp.com ([92.121.34.21]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdr0-0007OV-2O; Mon, 01 Jun 2020 06:23:29 +0000 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id C1D682004B9; Mon, 1 Jun 2020 08:23:20 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id C655920047B; Mon, 1 Jun 2020 08:23:14 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id 990EE402B1; Mon, 1 Jun 2020 14:23:07 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 3/7] drm: bridge: cadence: initial support for MHDP DP bridge driver Date: Mon, 1 Jun 2020 14:17:33 +0800 Message-Id: X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232322_534499_A7CF2CD7 X-CRM114-Status: GOOD ( 17.32 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.21 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu This adds initial support for MHDP DP bridge driver. Basic DP functions are supported, that include: -Video mode set on-the-fly -Cable hotplug detect -MAX support resolution to 3096x2160@60fps -Support DP audio -EDID read via AUX Signed-off-by: Sandor Yu --- drivers/gpu/drm/bridge/cadence/Kconfig | 4 + drivers/gpu/drm/bridge/cadence/Makefile | 1 + drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 530 ++++++++++++++++++ .../gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 100 ++++ .../gpu/drm/bridge/cadence/cdns-mhdp-common.c | 42 +- .../gpu/drm/bridge/cadence/cdns-mhdp-common.h | 3 + drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c | 34 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 7 +- include/drm/bridge/cdns-mhdp.h | 52 +- 9 files changed, 740 insertions(+), 33 deletions(-) create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-dp-core.c diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig index 48c1b0f77dc6..b7b8d30b18b6 100644 --- a/drivers/gpu/drm/bridge/cadence/Kconfig +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -5,3 +5,7 @@ config DRM_CDNS_MHDP depends on OF help Support Cadence MHDP API library. + +config DRM_CDNS_DP + tristate "Cadence DP DRM driver" + depends on DRM_CDNS_MHDP diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile index ddb2ba4fb852..cb3c88311a64 100644 --- a/drivers/gpu/drm/bridge/cadence/Makefile +++ b/drivers/gpu/drm/bridge/cadence/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-audio.o cdns-mhdp-dp.o +cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c new file mode 100644 index 000000000000..b2fe8fdc64ed --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cadence Display Port Interface (DP) driver + * + * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdns-mhdp-common.h" + +/* + * This function only implements native DPDC reads and writes + */ +static ssize_t dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(aux->dev); + bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ); + int ret; + + /* Ignore address only message */ + if ((msg->size == 0) || (msg->buffer == NULL)) { + msg->reply = native ? + DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK; + return msg->size; + } + + if (!native) { + dev_err(mhdp->dev, "%s: only native messages supported\n", __func__); + return -EINVAL; + } + + /* msg sanity check */ + if (msg->size > DP_AUX_MAX_PAYLOAD_BYTES) { + dev_err(mhdp->dev, "%s: invalid msg: size(%zu), request(%x)\n", + __func__, msg->size, (unsigned int)msg->request); + return -EINVAL; + } + + if (msg->request == DP_AUX_NATIVE_WRITE) { + const u8 *buf = msg->buffer; + int i; + for (i = 0; i < msg->size; ++i) { + ret = cdns_mhdp_dpcd_write(mhdp, + msg->address + i, buf[i]); + if (!ret) + continue; + + DRM_DEV_ERROR(mhdp->dev, "Failed to write DPCD\n"); + + return ret; + } + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + return msg->size; + } + + if (msg->request == DP_AUX_NATIVE_READ) { + ret = cdns_mhdp_dpcd_read(mhdp, msg->address, msg->buffer, msg->size); + if (ret < 0) + return -EIO; + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + return msg->size; + } + return 0; +} + +static int dp_aux_init(struct cdns_mhdp_device *mhdp, + struct device *dev) +{ + int ret; + + mhdp->dp.aux.name = "imx_dp_aux"; + mhdp->dp.aux.dev = dev; + mhdp->dp.aux.transfer = dp_aux_transfer; + + ret = drm_dp_aux_register(&mhdp->dp.aux); + + return ret; +} + +static int dp_aux_destroy(struct cdns_mhdp_device *mhdp) +{ + drm_dp_aux_unregister(&mhdp->dp.aux); + return 0; +} + +static void dp_pixel_clk_reset(struct cdns_mhdp_device *mhdp) +{ + u32 val; + + /* reset pixel clk */ + val = cdns_mhdp_reg_read(mhdp, SOURCE_HDTX_CAR); + cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, val & 0xFD); + cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, val); +} + +static void cdns_dp_mode_set(struct cdns_mhdp_device *mhdp) +{ + int ret; + + cdns_mhdp_plat_call(mhdp, pclk_rate); + + /* delay for DP FW stable after pixel clock relock */ + msleep(50); + + dp_pixel_clk_reset(mhdp); + + /* Get DP Caps */ + ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, mhdp->dp.dpcd, + DP_RECEIVER_CAP_SIZE); + if (ret < 0) { + DRM_ERROR("Failed to get caps %d\n", ret); + return; + } + + mhdp->dp.rate = drm_dp_max_link_rate(mhdp->dp.dpcd); + mhdp->dp.num_lanes = drm_dp_max_lane_count(mhdp->dp.dpcd); + + /* check the max link rate */ + if (mhdp->dp.rate > CDNS_DP_MAX_LINK_RATE) + mhdp->dp.rate = CDNS_DP_MAX_LINK_RATE; + + /* Initialize link rate/num_lanes as panel max link rate/max_num_lanes */ + cdns_mhdp_plat_call(mhdp, phy_set); + + /* Video off */ + ret = cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret); + return; + } + + /* Line swaping */ + mhdp->lane_mapping = mhdp->plat_data->lane_mapping; + cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | mhdp->lane_mapping); + + /* Set DP host capability */ + ret = cdns_mhdp_set_host_cap(mhdp); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to set host cap %d\n", ret); + return; + } + + ret = cdns_mhdp_config_video(mhdp); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to config video %d\n", ret); + return; + } + + return; +} + +/* ----------------------------------------------------------------------------- + * DP TX Setup + */ +static enum drm_connector_status +cdns_dp_connector_detect(struct drm_connector *connector, bool force) +{ + struct cdns_mhdp_device *mhdp = container_of(connector, + struct cdns_mhdp_device, connector.base); + u8 hpd = 0xf; + + hpd = cdns_mhdp_read_hpd(mhdp); + if (hpd == 1) + /* Cable Connected */ + return connector_status_connected; + else if (hpd == 0) + /* Cable Disconnedted */ + return connector_status_disconnected; + else { + /* Cable status unknown */ + DRM_INFO("Unknow cable status, hdp=%u\n", hpd); + return connector_status_unknown; + } +} + +static int cdns_dp_connector_get_modes(struct drm_connector *connector) +{ + struct cdns_mhdp_device *mhdp = container_of(connector, + struct cdns_mhdp_device, connector.base); + int num_modes = 0; + struct edid *edid; + + edid = drm_do_get_edid(&mhdp->connector.base, + cdns_mhdp_get_edid_block, mhdp); + if (edid) { + dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n", + edid->header[0], edid->header[1], + edid->header[2], edid->header[3], + edid->header[4], edid->header[5], + edid->header[6], edid->header[7]); + drm_connector_update_edid_property(connector, edid); + num_modes = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + if (num_modes == 0) + DRM_ERROR("Invalid edid\n"); + return num_modes; +} + +static const struct drm_connector_funcs cdns_dp_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = cdns_dp_connector_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs cdns_dp_connector_helper_funcs = { + .get_modes = cdns_dp_connector_get_modes, +}; + +static int cdns_dp_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + struct drm_encoder *encoder = bridge->encoder; + struct drm_connector *connector = &mhdp->connector.base; + + connector->interlace_allowed = 1; + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, &cdns_dp_connector_helper_funcs); + + drm_connector_init(bridge->dev, connector, &cdns_dp_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} + +static enum drm_mode_status +cdns_dp_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + enum drm_mode_status mode_status = MODE_OK; + + /* We don't support double-clocked modes */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK || + mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_BAD; + + /* MAX support pixel clock rate 594MHz */ + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + + /* 4096x2160 is not supported */ + if (mode->hdisplay > 3840) + return MODE_BAD_HVALUE; + + if (mode->vdisplay > 2160) + return MODE_BAD_VVALUE; + + return mode_status; +} + +static void cdns_dp_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *orig_mode, + const struct drm_display_mode *mode) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + struct drm_display_info *display_info = &mhdp->connector.base.display_info; + struct video_info *video = &mhdp->video_info; + + switch (display_info->bpc) { + case 10: + video->color_depth = 10; + break; + case 6: + video->color_depth = 6; + break; + default: + video->color_depth = 8; + break; + } + + video->color_fmt = PXL_RGB; + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + + DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); + + mutex_lock(&mhdp->lock); + cdns_dp_mode_set(mhdp); + mutex_unlock(&mhdp->lock); +} + +static void cdn_dp_bridge_enable(struct drm_bridge *bridge) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + int ret; + + /* Link trainning */ + ret = cdns_mhdp_train_link(mhdp); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed link train %d\n", ret); + return; + } + + ret = cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret); + return; + } +} + +static void cdn_dp_bridge_disable(struct drm_bridge *bridge) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + + cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); +} + +static const struct drm_bridge_funcs cdns_dp_bridge_funcs = { + .attach = cdns_dp_bridge_attach, + .enable = cdn_dp_bridge_enable, + .disable = cdn_dp_bridge_disable, + .mode_set = cdns_dp_bridge_mode_set, + .mode_valid = cdns_dp_bridge_mode_valid, +}; + +static void hotplug_work_func(struct work_struct *work) +{ + struct cdns_mhdp_device *mhdp = container_of(work, + struct cdns_mhdp_device, hotplug_work.work); + struct drm_connector *connector = &mhdp->connector.base; + + drm_helper_hpd_irq_event(connector->dev); + + if (connector->status == connector_status_connected) { + /* Cable connedted */ + DRM_INFO("HDMI/DP Cable Plug In\n"); + enable_irq(mhdp->irq[IRQ_OUT]); + } else if (connector->status == connector_status_disconnected) { + /* Cable Disconnedted */ + DRM_INFO("HDMI/DP Cable Plug Out\n"); + enable_irq(mhdp->irq[IRQ_IN]); + } +} + +static irqreturn_t cdns_dp_irq_thread(int irq, void *data) +{ + struct cdns_mhdp_device *mhdp = data; + + disable_irq_nosync(irq); + + mod_delayed_work(system_wq, &mhdp->hotplug_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); + + return IRQ_HANDLED; +} + +static int __cdns_dp_probe(struct platform_device *pdev, + struct cdns_mhdp_device *mhdp) +{ + struct device *dev = &pdev->dev; + struct resource *iores = NULL; + int ret; + + mutex_init(&mhdp->lock); + + INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (iores) { + mhdp->regs_base = devm_ioremap(dev, iores->start, + resource_size(iores)); + if (IS_ERR(mhdp->regs_base)) + return -ENOMEM; + } + + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); + if (mhdp->irq[IRQ_IN] < 0) { + dev_info(dev, "No plug_in irq number\n"); + } + + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); + if (mhdp->irq[IRQ_OUT] < 0) { + dev_info(dev, "No plug_out irq number\n"); + } + + cdns_mhdp_plat_call(mhdp, power_on); + + cdns_mhdp_plat_call(mhdp, firmware_init); + + /* DP FW alive check */ + ret = cdns_mhdp_check_alive(mhdp); + if (ret == false) { + DRM_ERROR("NO dp FW running\n"); + return -ENXIO; + } + + /* DP PHY init before AUX init */ + cdns_mhdp_plat_call(mhdp, phy_set); + + /* Enable Hotplug Detect IRQ thread */ + irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], + NULL, cdns_dp_irq_thread, + IRQF_ONESHOT, dev_name(dev), + mhdp); + + if (ret) { + dev_err(dev, "can't claim irq %d\n", + mhdp->irq[IRQ_IN]); + return -EINVAL; + } + + irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], + NULL, cdns_dp_irq_thread, + IRQF_ONESHOT, dev_name(dev), + mhdp); + + if (ret) { + dev_err(dev, "can't claim irq %d\n", + mhdp->irq[IRQ_OUT]); + return -EINVAL; + } + + if (cdns_mhdp_read_hpd(mhdp)) + enable_irq(mhdp->irq[IRQ_OUT]); + else + enable_irq(mhdp->irq[IRQ_IN]); + + mhdp->bridge.base.driver_private = mhdp; + mhdp->bridge.base.funcs = &cdns_dp_bridge_funcs; +#ifdef CONFIG_OF + mhdp->bridge.base.of_node = dev->of_node; +#endif + + dev_set_drvdata(dev, mhdp); + + /* register audio driver */ + cdns_mhdp_register_audio_driver(dev); + + dp_aux_init(mhdp, dev); + + return 0; +} + +static void __cdns_dp_remove(struct cdns_mhdp_device *mhdp) +{ + dp_aux_destroy(mhdp); + cdns_mhdp_unregister_audio_driver(mhdp->dev); +} + +/* ----------------------------------------------------------------------------- + * Probe/remove API, used from platforms based on the DRM bridge API. + */ +int cdns_dp_probe(struct platform_device *pdev, + struct cdns_mhdp_device *mhdp) +{ + int ret; + + ret = __cdns_dp_probe(pdev, mhdp); + if (ret) + return ret; + + drm_bridge_add(&mhdp->bridge.base); + + return 0; +} +EXPORT_SYMBOL_GPL(cdns_dp_probe); + +void cdns_dp_remove(struct platform_device *pdev) +{ + struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); + + drm_bridge_remove(&mhdp->bridge.base); + + __cdns_dp_remove(mhdp); +} +EXPORT_SYMBOL_GPL(cdns_dp_remove); + +/* ----------------------------------------------------------------------------- + * Bind/unbind API, used from platforms based on the component framework. + */ +int cdns_dp_bind(struct platform_device *pdev, struct drm_encoder *encoder, + struct cdns_mhdp_device *mhdp) +{ + int ret; + + ret = __cdns_dp_probe(pdev, mhdp); + if (ret < 0) + return ret; + + ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL, 0); + if (ret) { + cdns_dp_remove(pdev); + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cdns_dp_bind); + +void cdns_dp_unbind(struct device *dev) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + + __cdns_dp_remove(mhdp); +} +EXPORT_SYMBOL_GPL(cdns_dp_unbind); + +MODULE_AUTHOR("Sandor Yu "); +MODULE_DESCRIPTION("Cadence Display Port transmitter driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cdns-dp"); diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c index 8f51de0672ab..fdd4bcd0d33c 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c @@ -2,6 +2,9 @@ /* * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd * Author: Chris Zhong + * + * Cadence MHDP Audio driver + * */ #include #include @@ -196,3 +199,100 @@ int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, return ret; } EXPORT_SYMBOL(cdns_mhdp_audio_config); + +static int audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + struct audio_info audio = { + .sample_width = params->sample_width, + .sample_rate = params->sample_rate, + .channels = params->channels, + .connector_type = mhdp->connector.base.connector_type, + }; + int ret; + + switch (daifmt->fmt) { + case HDMI_I2S: + audio.format = AFMT_I2S; + break; + case HDMI_SPDIF: + audio.format = AFMT_SPDIF_EXT; + break; + default: + DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt); + ret = -EINVAL; + goto out; + } + + ret = cdns_mhdp_audio_config(mhdp, &audio); + if (!ret) + mhdp->audio_info = audio; + +out: + return ret; +} + +static void audio_shutdown(struct device *dev, void *data) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + int ret; + + ret = cdns_mhdp_audio_stop(mhdp, &mhdp->audio_info); + if (!ret) + mhdp->audio_info.format = AFMT_UNUSED; +} + +static int audio_digital_mute(struct device *dev, void *data, + bool enable) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + int ret; + + ret = cdns_mhdp_audio_mute(mhdp, enable); + + return ret; +} + +static int audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + + memcpy(buf, mhdp->connector.base.eld, + min(sizeof(mhdp->connector.base.eld), len)); + + return 0; +} + +static const struct hdmi_codec_ops audio_codec_ops = { + .hw_params = audio_hw_params, + .audio_shutdown = audio_shutdown, + .digital_mute = audio_digital_mute, + .get_eld = audio_get_eld, +}; + +int cdns_mhdp_register_audio_driver(struct device *dev) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + struct hdmi_codec_pdata codec_data = { + .i2s = 1, + .spdif = 1, + .ops = &audio_codec_ops, + .max_i2s_channels = 8, + }; + + mhdp->audio_pdev = platform_device_register_data( + dev, HDMI_CODEC_DRV_NAME, 1, + &codec_data, sizeof(codec_data)); + + return PTR_ERR_OR_ZERO(mhdp->audio_pdev); +} + +void cdns_mhdp_unregister_audio_driver(struct device *dev) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + + platform_device_unregister(mhdp->audio_pdev); +} diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c index efb39cf5f48b..9fd4546c6914 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c @@ -357,36 +357,6 @@ int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) } EXPORT_SYMBOL(cdns_mhdp_set_firmware_active); -int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) -{ - u8 msg[8]; - int ret; - - msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); - msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; - msg[2] = VOLTAGE_LEVEL_2; - msg[3] = PRE_EMPHASIS_LEVEL_3; - msg[4] = PTS1 | PTS2 | PTS3 | PTS4; - msg[5] = FAST_LT_NOT_SUPPORT; - msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; - msg[7] = ENHANCED; - - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, - DPTX_SET_HOST_CAPABILITIES, - sizeof(msg), msg); - if (ret) - goto err_set_host_cap; - - ret = cdns_mhdp_reg_write(mhdp, DP_AUX_SWAP_INVERSION_CONTROL, - AUX_HOST_INVERT); - -err_set_host_cap: - if (ret) - DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret); - return ret; -} -EXPORT_SYMBOL(cdns_mhdp_set_host_cap); - int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp) { u8 msg[5]; @@ -698,3 +668,15 @@ int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp) return ret; } EXPORT_SYMBOL(cdns_mhdp_config_video); + +int cdns_phy_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val) +{ + return cdns_mhdp_reg_write(mhdp, ADDR_PHY_AFE + (addr << 2), val); +} +EXPORT_SYMBOL(cdns_phy_reg_write); + +u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr) +{ + return cdns_mhdp_reg_read(mhdp, ADDR_PHY_AFE + (addr << 2)); +} +EXPORT_SYMBOL(cdns_phy_reg_read); diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h index 1f093a2deaa7..b122bf5f0bdf 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h @@ -20,4 +20,7 @@ int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, u8 start_bit, u8 bits_no, u32 val); +/* Audio */ +int cdns_mhdp_register_audio_driver(struct device *dev); +void cdns_mhdp_unregister_audio_driver(struct device *dev); #endif diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c index c8160f321aca..8fea072a5568 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c @@ -108,7 +108,9 @@ static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp) if (ret) goto err_training_start; - if (event[1] & EQ_PHASE_FINISHED) + if (event[1] & CLK_RECOVERY_FAILED) + DRM_DEV_ERROR(mhdp->dev, "clock recovery failed\n"); + else if (event[1] & EQ_PHASE_FINISHED) return 0; } @@ -172,3 +174,33 @@ int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp) return ret; } EXPORT_SYMBOL(cdns_mhdp_train_link); + +int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp) +{ + u8 msg[8]; + int ret; + + msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); + msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; + msg[2] = VOLTAGE_LEVEL_2; + msg[3] = PRE_EMPHASIS_LEVEL_3; + msg[4] = PTS1 | PTS2 | PTS3 | PTS4; + msg[5] = FAST_LT_NOT_SUPPORT; + msg[6] = mhdp->lane_mapping; + msg[7] = ENHANCED; + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, + DPTX_SET_HOST_CAPABILITIES, + sizeof(msg), msg); + if (ret) + goto err_set_host_cap; + + ret = cdns_mhdp_reg_write(mhdp, DP_AUX_SWAP_INVERSION_CONTROL, + AUX_HOST_INVERT); + +err_set_host_cap: + if (ret) + DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret); + return ret; +} +EXPORT_SYMBOL(cdns_mhdp_set_host_cap); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 06fd82b217b6..d94d22650899 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -423,7 +423,12 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) } port->lanes = cdn_dp_get_port_lanes(port); - ret = cdns_mhdp_set_host_cap(&dp->mhdp, property.intval); + + if (property.intval) + dp->mhdp.lane_mapping = LANE_MAPPING_FLIPPED; + else + dp->mhdp.lane_mapping = LANE_MAPPING_NORMAL; + ret = cdns_mhdp_set_host_cap(&dp->mhdp); if (ret) { DRM_DEV_ERROR(dev, "set host capabilities failed: %d\n", ret); diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h index c8170e6048f7..6ffb97e17fae 100644 --- a/include/drm/bridge/cdns-mhdp.h +++ b/include/drm/bridge/cdns-mhdp.h @@ -14,6 +14,7 @@ #define ADDR_IMEM 0x10000 #define ADDR_DMEM 0x20000 +#define ADDR_PHY_AFE 0x80000 /* APB CFG addr */ #define APB_CTRL 0 @@ -81,6 +82,10 @@ #define SOURCE_PIF_SW_RESET 0x30834 /* bellow registers need access by mailbox */ + +/* source phy comp */ +#define LANES_CONFIG 0x0814 + /* source car addr */ #define SOURCE_HDTX_CAR 0x0900 #define SOURCE_DPTX_CAR 0x0904 @@ -424,6 +429,16 @@ /* Reference cycles when using lane clock as reference */ #define LANE_REF_CYC 0x8000 +#define HOTPLUG_DEBOUNCE_MS 200 + +#define IRQ_IN 0 +#define IRQ_OUT 1 +#define IRQ_NUM 2 + +#define cdns_mhdp_plat_call(mhdp, operation) \ + (!(mhdp) ? -ENODEV : (((mhdp)->plat_data && (mhdp)->plat_data->operation) ? \ + (mhdp)->plat_data->operation(mhdp) : ENOIOCTLCMD)) + enum voltage_swing_level { VOLTAGE_LEVEL_0, VOLTAGE_LEVEL_1, @@ -504,9 +519,29 @@ struct cdns_mhdp_connector { struct cdns_mhdp_bridge *bridge; }; +struct cdns_plat_data { + /* Vendor PHY support */ + int (*bind)(struct platform_device *pdev, + struct drm_encoder *encoder, + struct cdns_mhdp_device *mhdp); + void (*unbind)(struct device *dev); + + int (*phy_set)(struct cdns_mhdp_device *mhdp); + bool (*phy_video_valid)(struct cdns_mhdp_device *mhdp); + int (*firmware_init)(struct cdns_mhdp_device *mhdp); + void (*pclk_rate)(struct cdns_mhdp_device *mhdp); + + int (*power_on)(struct cdns_mhdp_device *mhdp); + int (*power_off)(struct cdns_mhdp_device *mhdp); + + char *plat_name; + int lane_mapping; +}; + struct cdns_mhdp_device { struct device *dev; struct cdns_mhdp_connector connector; + struct cdns_mhdp_bridge bridge; void __iomem *regs_base; struct reset_control *spdif_rst; @@ -520,6 +555,11 @@ struct cdns_mhdp_device { unsigned int fw_version; + struct delayed_work hotplug_work; + u32 lane_mapping; + bool power_up; + struct mutex lock; + int irq[IRQ_NUM]; union { struct _dp_data { u32 rate; @@ -528,6 +568,8 @@ struct cdns_mhdp_device { u8 dpcd[DP_RECEIVER_CAP_SIZE]; } dp; }; + const struct cdns_plat_data *plat_data; + }; void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp); @@ -535,7 +577,7 @@ void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk); int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, u32 i_size, const u32 *d_mem, u32 d_size); int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable); -int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip); +int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp); int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp); u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp); int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp); @@ -547,10 +589,18 @@ int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid, int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp); int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active); int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp); +bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp); int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, struct audio_info *audio); int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable); int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, struct audio_info *audio); +int cdns_phy_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); +u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); + +/* DP */ +int cdns_dp_bind(struct platform_device *pdev, struct drm_encoder *encoder, + struct cdns_mhdp_device *mhdp); +void cdns_dp_unbind(struct device *dev); #endif /* CDNS_MHDP_H_ */ From patchwork Mon Jun 1 06:17:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581403 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 433E6139A for ; Mon, 1 Jun 2020 06:25:06 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8BB6620734 for ; Mon, 1 Jun 2020 06:25:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="sp7vgps2" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8BB6620734 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:References:In-Reply-To:MIME-Version: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=/tiwfVmoiioeW0alCBJ8W72gpb/WA+xWeOW60uUw6O4=; b=sp7vgps2Z3iFFi TdtWnkdlwN+QY/4PjsUAC5B3IKPIw8gs846DwZwFZYP0xjdfwmttnUCHlcW2v6bk1ECis5Z5YQlO/ /uykPvcL/M4z7EW1oOaCgVgYNJuHUU9sK91uwI2CZ83F7ILjHODcW1TNgmK0FkWXYIZRBXQc111EW FouflcXBt4D1zJ4QVuQ4kVR/EIVcdJ+xovDOrIg8xda4EE6ElBhDq/NKfJz4yKK5m64UidNeSoCoY vWIw3TIuU8D5C3nPaawQYlgEObZGjem4zWJfQYWkN7XHXtv9Vn1ld/j3AjPtxloAXMaJmJ3wDVw7A 8w//1WrH0pMtRE0aLsUg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdsW-0008Us-F7; Mon, 01 Jun 2020 06:24:56 +0000 Received: from inva021.nxp.com ([92.121.34.21]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdr1-0007Ov-FF; Mon, 01 Jun 2020 06:23:31 +0000 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 385A12004B1; Mon, 1 Jun 2020 08:23:22 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 3F7EA2004A1; Mon, 1 Jun 2020 08:23:16 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id EE0E4402A7; Mon, 1 Jun 2020 14:23:08 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 4/7] drm: imx: mhdp: initial support for i.MX8MQ MHDP Displayport Date: Mon, 1 Jun 2020 14:17:34 +0800 Message-Id: <25f0d6e497bc76741a715f03f1c5853c8d77cee5.1590982881.git.Sandor.yu@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: MIME-Version: 1.0 In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232323_969707_EFBDD92A X-CRM114-Status: GOOD ( 15.48 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.21 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu Initial support for i.MX8MQ MHDP Displayport. Add MHDP DP PHY configutation. The features are same as MHDP DP bridge driver. Signed-off-by: Sandor Yu --- drivers/gpu/drm/imx/Kconfig | 1 + drivers/gpu/drm/imx/Makefile | 1 + drivers/gpu/drm/imx/mhdp/Kconfig | 8 + drivers/gpu/drm/imx/mhdp/Makefile | 4 + drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c | 390 ++++++++++++++++++++ drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 130 +++++++ drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 146 ++++++++ 7 files changed, 680 insertions(+) create mode 100644 drivers/gpu/drm/imx/mhdp/Kconfig create mode 100644 drivers/gpu/drm/imx/mhdp/Makefile create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index 6231048aa5aa..4af2f575f04b 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -41,3 +41,4 @@ config DRM_IMX_HDMI Choose this if you want to use HDMI on i.MX6. source "drivers/gpu/drm/imx/dcss/Kconfig" +source "drivers/gpu/drm/imx/mhdp/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index b644deffe948..0b46c46b19a8 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ +obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += mhdp/ diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig new file mode 100644 index 000000000000..c9e07a3bf3df --- /dev/null +++ b/drivers/gpu/drm/imx/mhdp/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config DRM_IMX_CDNS_MHDP + tristate "NXP i.MX MX8 DRM DP" + select DRM_CDNS_MHDP + select DRM_CDNS_DP + help + Choose this if you want to use Displayport on i.MX8. diff --git a/drivers/gpu/drm/imx/mhdp/Makefile b/drivers/gpu/drm/imx/mhdp/Makefile new file mode 100644 index 000000000000..4383e689445a --- /dev/null +++ b/drivers/gpu/drm/imx/mhdp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o +obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c new file mode 100644 index 000000000000..bb694301984d --- /dev/null +++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cadence Display Port Interface (DP) PHY driver + * + * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + */ +#include +#include +#include +#include +#include "cdns-mhdp-phy.h" + +enum dp_link_rate { + RATE_1_6 = 162000, + RATE_2_1 = 216000, + RATE_2_4 = 243000, + RATE_2_7 = 270000, + RATE_3_2 = 324000, + RATE_4_3 = 432000, + RATE_5_4 = 540000, + RATE_8_1 = 810000, +}; + +struct phy_pll_reg { + u16 val[7]; + u32 addr; +}; + +static const struct phy_pll_reg phy_pll_27m_cfg[] = { + /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register address */ + {{ 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E }, CMN_PLL0_VCOCAL_INIT_TMR }, + {{ 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B }, CMN_PLL0_VCOCAL_ITER_TMR }, + {{ 0x30B9, 0x3087, 0x3096, 0x30B4, 0x30B9, 0x3087, 0x30B4 }, CMN_PLL0_VCOCAL_START }, + {{ 0x0077, 0x009F, 0x00B3, 0x00C7, 0x0077, 0x009F, 0x00C7 }, CMN_PLL0_INTDIV }, + {{ 0xF9DA, 0xF7CD, 0xF6C7, 0xF5C1, 0xF9DA, 0xF7CD, 0xF5C1 }, CMN_PLL0_FRACDIV }, + {{ 0x001E, 0x0028, 0x002D, 0x0032, 0x001E, 0x0028, 0x0032 }, CMN_PLL0_HIGH_THR }, + {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_PLL0_DSM_DIAG }, + {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL }, + {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD }, + {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD }, + {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBL_OVRD }, + {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE }, + {{ 0x0043, 0x0043, 0x0043, 0x0042, 0x0043, 0x0043, 0x0042 }, CMN_DIAG_PLL0_CP_TUNE }, + {{ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG }, + {{ 0x0100, 0x0001, 0x0001, 0x0001, 0x0100, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE1 }, + {{ 0x0007, 0x0001, 0x0001, 0x0001, 0x0007, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE2 }, + {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_DIAG_PLL0_TEST_MODE}, + {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL } +}; + +static int link_rate_index(u32 rate) +{ + switch (rate) { + case RATE_1_6: + return 0; + case RATE_2_1: + return 1; + case RATE_2_4: + return 2; + case RATE_2_7: + return 3; + case RATE_3_2: + return 4; + case RATE_4_3: + return 5; + case RATE_5_4: + return 6; + default: + return -1; + } +} + +static void dp_aux_cfg(struct cdns_mhdp_device *mhdp) +{ + /* Power up Aux */ + cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 1); + + cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_1, 0x3); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, 36); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA018); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0000); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x1001); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA098); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA198); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030d); + ndelay(150); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030f); +} + +/* PMA common configuration for 27MHz */ +static void dp_phy_pma_cmn_cfg_27mhz(struct cdns_mhdp_device *mhdp) +{ + u32 num_lanes = mhdp->dp.num_lanes; + u16 val; + int k; + + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); + val &= 0xFFF7; + val |= 0x0008; + cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); + + /* Startup state machine registers */ + cdns_phy_reg_write(mhdp, CMN_SSM_BIAS_TMR, 0x0087); + cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLEN_TMR, 0x001B); + cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLPRE_TMR, 0x0036); + cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLVREF_TMR, 0x001B); + cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLLOCK_TMR, 0x006C); + + /* Current calibration registers */ + cdns_phy_reg_write(mhdp, CMN_ICAL_INIT_TMR, 0x0044); + cdns_phy_reg_write(mhdp, CMN_ICAL_ITER_TMR, 0x0006); + cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_ITER_TMR, 0x0006); + + /* Resistor calibration registers */ + cdns_phy_reg_write(mhdp, CMN_TXPUCAL_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_TXPUCAL_ITER_TMR, 0x0006); + cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_ITER_TMR, 0x0006); + cdns_phy_reg_write(mhdp, CMN_TXPDCAL_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_TXPDCAL_ITER_TMR, 0x0006); + cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_ITER_TMR, 0x0006); + cdns_phy_reg_write(mhdp, CMN_RXCAL_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_RXCAL_ITER_TMR, 0x0006); + cdns_phy_reg_write(mhdp, CMN_RX_ADJ_INIT_TMR, 0x0022); + cdns_phy_reg_write(mhdp, CMN_RX_ADJ_ITER_TMR, 0x0006); + + for (k = 0; k < num_lanes; k = k + 1) { + /* Power state machine registers */ + cdns_phy_reg_write(mhdp, XCVR_PSM_CAL_TMR | (k << 9), 0x016D); + cdns_phy_reg_write(mhdp, XCVR_PSM_A0IN_TMR | (k << 9), 0x016D); + /* Transceiver control and diagnostic registers */ + cdns_phy_reg_write(mhdp, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x00A2); + cdns_phy_reg_write(mhdp, TX_DIAG_BGREF_PREDRV_DELAY | (k << 9), 0x0097); + /* Transmitter receiver detect registers */ + cdns_phy_reg_write(mhdp, TX_RCVDET_EN_TMR | (k << 9), 0x0A8C); + cdns_phy_reg_write(mhdp, TX_RCVDET_ST_TMR | (k << 9), 0x0036); + } +} + +static void dp_phy_pma_cmn_pll0_27mhz(struct cdns_mhdp_device *mhdp) +{ + u32 num_lanes = mhdp->dp.num_lanes; + u32 link_rate = mhdp->dp.rate; + u16 val; + int index, i, k; + + /* + * PLL reference clock source select + * for single ended reference clock val |= 0x0030; + * for differential clock val |= 0x0000; + */ + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); + val &= 0xFF8F; + cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); + + /* for differential clock on the refclk_p and refclk_m off chip pins: + * CMN_DIAG_ACYA[8]=1'b1 + */ + cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100); + + /* DP PLL data rate 0/1 clock divider value */ + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + val &= 0x00FF; + if (link_rate <= RATE_2_7) + val |= 0x2400; + else + val |= 0x1200; + cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); + + /* High speed clock 0/1 div */ + val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL); + val &= 0xFFCC; + if (link_rate <= RATE_2_7) + val |= 0x0011; + cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val); + + for (k = 0; k < num_lanes; k++) { + val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9))); + val = val & 0xCFFF; + if (link_rate <= RATE_2_7) + val |= 0x1000; + cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); + } + + /* DP PHY PLL 27MHz configuration */ + index = link_rate_index(link_rate); + for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++) + cdns_phy_reg_write(mhdp, phy_pll_27m_cfg[i].addr, phy_pll_27m_cfg[i].val[index]); + + /* Transceiver control and diagnostic registers */ + for (k = 0; k < num_lanes; k++) { + val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); + val = val & 0x8FFF; + if (link_rate <= RATE_2_7) + val |= 0x2000; + else + val |= 0x1000; + cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); + } + + for (k = 0; k < num_lanes; k = k + 1) { + /* Power state machine registers */ + cdns_phy_reg_write(mhdp, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC); + cdns_phy_reg_write(mhdp, (TX_PSC_A0 | (k << 9)), 0x6799); + cdns_phy_reg_write(mhdp, (TX_PSC_A1 | (k << 9)), 0x6798); + cdns_phy_reg_write(mhdp, (TX_PSC_A2 | (k << 9)), 0x0098); + cdns_phy_reg_write(mhdp, (TX_PSC_A3 | (k << 9)), 0x0098); + /* Receiver calibration power state definition register */ + val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9)); + val &= 0xFFBB; + cdns_phy_reg_write(mhdp, (RX_PSC_CAL | (k << 9)), val); + val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9)); + val &= 0xFFBB; + cdns_phy_reg_write(mhdp, (RX_PSC_A0 | (k << 9)), val); + } +} + +static void dp_phy_power_down(struct cdns_mhdp_device *mhdp) +{ + u16 val; + int i; + + if (!mhdp->power_up) + return; + + /* Place the PHY lanes in the A3 power state. */ + cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x8); + /* Wait for Power State A3 Ack */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); + if (val & (1 << 7)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait A3 Ack failed\n"); + return; + } + + /* Disable HDP PLL’s data rate and full rate clocks out of PMA. */ + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + val &= ~(1 << 2); + cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); + /* Wait for PLL clock gate ACK */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + if (!(val & (1 << 3))) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait PLL clock gate Ack failed\n"); + return; + } + + /* Disable HDP PLL’s for high speed clocks */ + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + val &= ~(1 << 0); + cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); + /* Wait for PLL disable ACK */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + if (!(val & (1 << 1))) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait PLL disable Ack failed\n"); + return; + } +} + +static int dp_phy_power_up(struct cdns_mhdp_device *mhdp) +{ + u32 val, i; + + /* Enable HDP PLL’s for high speed clocks */ + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + val |= (1 << 0); + cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); + /* Wait for PLL ready ACK */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + if (val & (1 << 1)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait PLL Ack failed\n"); + return -1; + } + + /* Enable HDP PLL’s data rate and full rate clocks out of PMA. */ + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + val |= (1 << 2); + cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); + /* Wait for PLL clock enable ACK */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + if (val & (1 << 3)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait PLL clock enable ACk failed\n"); + return -1; + } + + /* Configure PHY in A2 Mode */ + cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004); + /* Wait for Power State A2 Ack */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); + if (val & (1 << 6)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait A2 Ack failed\n"); + return -1; + } + + /* Configure PHY in A0 mode (PHY must be in the A0 power + * state in order to transmit data) + */ + cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101); + + /* Wait for Power State A0 Ack */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); + if (val & (1 << 4)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait A0 Ack failed\n"); + return -1; + } + + mhdp->power_up = true; + + return 0; +} + +int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *mhdp) +{ + int ret; + + /* Disable phy clock if PHY in power up state */ + dp_phy_power_down(mhdp); + + dp_phy_pma_cmn_cfg_27mhz(mhdp); + + dp_phy_pma_cmn_pll0_27mhz(mhdp); + + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1); + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1); + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1); + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1); + + /* PHY power up */ + ret = dp_phy_power_up(mhdp); + if (ret < 0) + return ret; + + dp_aux_cfg(mhdp); + + return ret; +} diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c new file mode 100644 index 000000000000..2dec2e051be6 --- /dev/null +++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * copyright (c) 2019-2020 nxp semiconductor, inc. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cdns-mhdp-phy.h" +#include "../imx-drm.h" + +struct imx_mhdp_device { + struct cdns_mhdp_device mhdp; + struct drm_encoder encoder; +}; + +static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static struct cdns_plat_data imx8mq_dp_drv_data = { + .plat_name = "imx8mq-dp", + .bind = cdns_dp_bind, + .unbind = cdns_dp_unbind, + .phy_set = cdns_dp_phy_set_imx8mq, + .lane_mapping = 0xc6, +}; + +static const struct of_device_id cdns_mhdp_imx_dt_ids[] = { + { .compatible = "nxp,imx8mq-cdns-dp", + .data = &imx8mq_dp_drv_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids); + +static int cdns_mhdp_imx_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct cdns_plat_data *plat_data; + const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct imx_mhdp_device *imx_mhdp; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + imx_mhdp = devm_kzalloc(&pdev->dev, sizeof(*imx_mhdp), GFP_KERNEL); + if (!imx_mhdp) + return -ENOMEM; + + match = of_match_node(cdns_mhdp_imx_dt_ids, pdev->dev.of_node); + plat_data = match->data; + encoder = &imx_mhdp->encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + drm_encoder_init(drm, encoder, &cdns_mhdp_imx_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + + + imx_mhdp->mhdp.plat_data = plat_data; + imx_mhdp->mhdp.dev = dev; + ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp); + /* + * If cdns_mhdp_bind() fails we'll never call cdns_mhdp_unbind(), + * which would have called the encoder cleanup. Do it manually. + */ + if (ret < 0) + drm_encoder_cleanup(encoder); + + return ret; +} + +static void cdns_mhdp_imx_unbind(struct device *dev, struct device *master, + void *data) +{ + struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); + + imx_mhdp->mhdp.plat_data->unbind(dev); +} + +static const struct component_ops cdns_mhdp_imx_ops = { + .bind = cdns_mhdp_imx_bind, + .unbind = cdns_mhdp_imx_unbind, +}; + +static int cdns_mhdp_imx_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &cdns_mhdp_imx_ops); +} + +static int cdns_mhdp_imx_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &cdns_mhdp_imx_ops); + + return 0; +} + +static struct platform_driver cdns_mhdp_imx_platform_driver = { + .probe = cdns_mhdp_imx_probe, + .remove = cdns_mhdp_imx_remove, + .driver = { + .name = "cdns-mhdp-imx", + .of_match_table = cdns_mhdp_imx_dt_ids, + }, +}; + +module_platform_driver(cdns_mhdp_imx_platform_driver); + +MODULE_AUTHOR("Sandor YU "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cdnsmhdp-imx"); diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h new file mode 100644 index 000000000000..79b1907726db --- /dev/null +++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + * + */ +#ifndef _CDNS_MHDP_PHY_H +#define _CDNS_MHDP_PHY_H + +#include + +#define CMN_SSM_BIAS_TMR 0x0022 +#define CMN_PLLSM0_PLLEN_TMR 0x0029 +#define CMN_PLLSM0_PLLPRE_TMR 0x002A +#define CMN_PLLSM0_PLLVREF_TMR 0x002B +#define CMN_PLLSM0_PLLLOCK_TMR 0x002C +#define CMN_PLLSM0_USER_DEF_CTRL 0x002F +#define CMN_PSM_CLK_CTRL 0x0061 +#define CMN_CDIAG_REFCLK_CTRL 0x0062 +#define CMN_PLL0_VCOCAL_START 0x0081 +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 +#define CMN_PLL0_INTDIV 0x0094 +#define CMN_PLL0_FRACDIV 0x0095 +#define CMN_PLL0_HIGH_THR 0x0096 +#define CMN_PLL0_DSM_DIAG 0x0097 +#define CMN_PLL0_SS_CTRL1 0x0098 +#define CMN_PLL0_SS_CTRL2 0x0099 +#define CMN_ICAL_INIT_TMR 0x00C4 +#define CMN_ICAL_ITER_TMR 0x00C5 +#define CMN_RXCAL_INIT_TMR 0x00D4 +#define CMN_RXCAL_ITER_TMR 0x00D5 +#define CMN_TXPUCAL_CTRL 0x00E0 +#define CMN_TXPUCAL_INIT_TMR 0x00E4 +#define CMN_TXPUCAL_ITER_TMR 0x00E5 +#define CMN_TXPDCAL_CTRL 0x00F0 +#define CMN_TXPDCAL_INIT_TMR 0x00F4 +#define CMN_TXPDCAL_ITER_TMR 0x00F5 +#define CMN_ICAL_ADJ_INIT_TMR 0x0102 +#define CMN_ICAL_ADJ_ITER_TMR 0x0103 +#define CMN_RX_ADJ_INIT_TMR 0x0106 +#define CMN_RX_ADJ_ITER_TMR 0x0107 +#define CMN_TXPU_ADJ_CTRL 0x0108 +#define CMN_TXPU_ADJ_INIT_TMR 0x010A +#define CMN_TXPU_ADJ_ITER_TMR 0x010B +#define CMN_TXPD_ADJ_CTRL 0x010c +#define CMN_TXPD_ADJ_INIT_TMR 0x010E +#define CMN_TXPD_ADJ_ITER_TMR 0x010F +#define CMN_DIAG_PLL0_FBH_OVRD 0x01C0 +#define CMN_DIAG_PLL0_FBL_OVRD 0x01C1 +#define CMN_DIAG_PLL0_OVRD 0x01C2 +#define CMN_DIAG_PLL0_TEST_MODE 0x01C4 +#define CMN_DIAG_PLL0_V2I_TUNE 0x01C5 +#define CMN_DIAG_PLL0_CP_TUNE 0x01C6 +#define CMN_DIAG_PLL0_LF_PROG 0x01C7 +#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01C8 +#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01C9 +#define CMN_DIAG_PLL0_INCLK_CTRL 0x01CA +#define CMN_DIAG_PLL0_PXL_DIVH 0x01CB +#define CMN_DIAG_PLL0_PXL_DIVL 0x01CC +#define CMN_DIAG_HSCLK_SEL 0x01E0 +#define CMN_DIAG_PER_CAL_ADJ 0x01EC +#define CMN_DIAG_CAL_CTRL 0x01ED +#define CMN_DIAG_ACYA 0x01FF +#define XCVR_PSM_RCTRL 0x4001 +#define XCVR_PSM_CAL_TMR 0x4002 +#define XCVR_PSM_A0IN_TMR 0x4003 +#define TX_TXCC_CAL_SCLR_MULT_0 0x4047 +#define TX_TXCC_CPOST_MULT_00_0 0x404C +#define TX_TXCC_MGNFS_MULT_000_0 0x4050 +#define XCVR_DIAG_PLLDRC_CTRL 0x40E0 +#define XCVR_DIAG_PLLDRC_CTRL 0x40E0 +#define XCVR_DIAG_HSCLK_SEL 0x40E1 +#define XCVR_DIAG_BIDI_CTRL 0x40E8 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40F2 +#define XCVR_DIAG_LANE_FCM_EN_MGN 0x40F2 +#define TX_PSC_A0 0x4100 +#define TX_PSC_A1 0x4101 +#define TX_PSC_A2 0x4102 +#define TX_PSC_A3 0x4103 +#define TX_RCVDET_CTRL 0x4120 +#define TX_RCVDET_EN_TMR 0x4122 +#define TX_RCVDET_EN_TMR 0x4122 +#define TX_RCVDET_ST_TMR 0x4123 +#define TX_RCVDET_ST_TMR 0x4123 +#define TX_BIST_CTRL 0x4140 +#define TX_BIST_UDDWR 0x4141 +#define TX_DIAG_TX_CTRL 0x41E0 +#define TX_DIAG_TX_DRV 0x41E1 +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7 +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7 +#define XCVR_PSM_RCTRL_1 0x4201 +#define TX_TXCC_CAL_SCLR_MULT_1 0x4247 +#define TX_TXCC_CPOST_MULT_00_1 0x424C +#define TX_TXCC_MGNFS_MULT_000_1 0x4250 +#define XCVR_DIAG_PLLDRC_CTRL_1 0x42E0 +#define XCVR_DIAG_HSCLK_SEL_1 0x42E1 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_1 0x42F2 +#define TX_RCVDET_EN_TMR_1 0x4322 +#define TX_RCVDET_ST_TMR_1 0x4323 +#define TX_DIAG_ACYA_0 0x41FF +#define TX_DIAG_ACYA_1 0x43FF +#define TX_DIAG_ACYA_2 0x45FF +#define TX_DIAG_ACYA_3 0x47FF +#define TX_ANA_CTRL_REG_1 0x5020 +#define TX_ANA_CTRL_REG_2 0x5021 +#define TXDA_COEFF_CALC 0x5022 +#define TX_DIG_CTRL_REG_1 0x5023 +#define TX_DIG_CTRL_REG_2 0x5024 +#define TXDA_CYA_AUXDA_CYA 0x5025 +#define TX_ANA_CTRL_REG_3 0x5026 +#define TX_ANA_CTRL_REG_4 0x5027 +#define TX_ANA_CTRL_REG_5 0x5029 +#define RX_PSC_A0 0x8000 +#define RX_PSC_CAL 0x8006 +#define PMA_LANE_CFG 0xC000 +#define PIPE_CMN_CTRL1 0xC001 +#define PIPE_CMN_CTRL2 0xC002 +#define PIPE_COM_LOCK_CFG1 0xC003 +#define PIPE_COM_LOCK_CFG2 0xC004 +#define PIPE_RCV_DET_INH 0xC005 +#define PHY_HDP_MODE_CTRL 0xC008 +#define PHY_HDP_CLK_CTL 0xC009 +#define STS 0xC00F +#define PHY_ISO_CMN_CTRL 0xC010 +#define PHY_ISO_CMN_CTRL 0xC010 +#define PHY_HDP_TX_CTL_L0 0xC408 +#define PHY_DP_TX_CTL 0xC408 +#define PHY_HDP_TX_CTL_L1 0xC448 +#define PHY_HDP_TX_CTL_L2 0xC488 +#define PHY_HDP_TX_CTL_L3 0xC4C8 +#define PHY_PMA_CMN_CTRL1 0xC800 +#define PMA_CMN_CTRL1 0xC800 +#define PHY_PMA_ISO_CMN_CTRL 0xC810 +#define PHY_PMA_ISO_PLL_CTRL1 0xC812 +#define PHY_PMA_ISOLATION_CTRL 0xC81F +#define PHY_ISOLATION_CTRL 0xC81F +#define PHY_PMA_ISO_XCVR_CTRL 0xCC11 +#define PHY_PMA_ISO_LINK_MODE 0xCC12 +#define PHY_PMA_ISO_PWRST_CTRL 0xCC13 +#define PHY_PMA_ISO_TX_DATA_LO 0xCC14 +#define PHY_PMA_ISO_TX_DATA_HI 0xCC15 +#define PHY_PMA_ISO_RX_DATA_LO 0xCC16 +#define PHY_PMA_ISO_RX_DATA_HI 0xCC17 + +int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp); +#endif /* _CDNS_MHDP_PHY_H */ From patchwork Mon Jun 1 06:17:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581417 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6718F14F6 for ; Mon, 1 Jun 2020 06:26:39 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 30DF92074B for ; Mon, 1 Jun 2020 06:26:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="Wnia0TTi" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 30DF92074B Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=XEbE2uffdR/HfALs89DmLE+HaQD88Y+H5NMDF267Y2k=; b=Wnia0TTiE4ffPMNYv2keOKntb6 IzX/ld8ndpk/BjH30tXhLCo08nvzKq7MsHONHYwhXUs+TOFVqlRw0T1xVa38a2JUhKO7zjs/FDNf/ ARrcjCZB7NmfPWj0rJmVTnsoWZ/k4o6COKTqwee9eO76VezOsEUxQe/cmUrhM3UKGEcSJmhQUllcX Kats6DKnKlzZEBAvtjcAGJ24kNNJSWeMxSKBzSQOvpvEtACmxfXGgLcnReid/FcDTO1NQQX5PUaaR ZX+f2VuIpVCcjW3s0H7FOUz+piE7qs3u6rkoCd1eVZQC1rXvgG6uLI+dq/jKnrVKFDeFTO+yByv+o g93kF2Kg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdu1-0003hT-Av; Mon, 01 Jun 2020 06:26:29 +0000 Received: from inva020.nxp.com ([92.121.34.13]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdr2-0007Pn-VD; Mon, 01 Jun 2020 06:23:33 +0000 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 7E0E01A0634; Mon, 1 Jun 2020 08:23:23 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 8220D1A062C; Mon, 1 Jun 2020 08:23:17 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id 51C164030C; Mon, 1 Jun 2020 14:23:10 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 5/7] drm: bridge: cadence: Initial support for MHDP HDMI bridge driver Date: Mon, 1 Jun 2020 14:17:35 +0800 Message-Id: X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232325_507820_D19D3299 X-CRM114-Status: GOOD ( 17.29 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.13 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu This adds initial support for cadence MHDP HDMI bridge driver. Basic HDMI functions are supported, that include: -Video mode set on-the-fly -Cable hotplug detect -MAX support resolution to 3096x2160@60fps -HDMI audio -AV infoframe -EDID read -SCDC read Signed-off-by: Sandor Yu --- drivers/gpu/drm/bridge/cadence/Kconfig | 4 + drivers/gpu/drm/bridge/cadence/Makefile | 3 +- .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 600 ++++++++++++++++++ .../gpu/drm/bridge/cadence/cdns-mhdp-common.h | 14 + .../gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 330 ++++++++++ include/drm/bridge/cdns-mhdp.h | 69 ++ 6 files changed, 1019 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig index b7b8d30b18b6..9bc098302837 100644 --- a/drivers/gpu/drm/bridge/cadence/Kconfig +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -9,3 +9,7 @@ config DRM_CDNS_MHDP config DRM_CDNS_DP tristate "Cadence DP DRM driver" depends on DRM_CDNS_MHDP + +config DRM_CDNS_HDMI + tristate "Cadence HDMI DRM driver" + depends on DRM_CDNS_MHDP diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile index cb3c88311a64..1d60166c2bf5 100644 --- a/drivers/gpu/drm/bridge/cadence/Makefile +++ b/drivers/gpu/drm/bridge/cadence/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-audio.o cdns-mhdp-dp.o +cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-audio.o cdns-mhdp-dp.o cdns-mhdp-hdmi.o cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o +cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c new file mode 100644 index 000000000000..5775ed21b734 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cadence High-Definition Multimedia Interface (HDMI) driver + * + * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdns-mhdp-common.h" + +static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) +{ + struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc; + u8 buff = 0; + + /* Default work in HDMI1.4 */ + mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; + + /* check sink support SCDC or not */ + if (scdc->supported != true) { + DRM_INFO("Sink Not Support SCDC\n"); + return; + } + + if (mhdp->hdmi.char_rate > 340000) { + /* + * TMDS Character Rate above 340MHz should working in HDMI2.0 + * Enable scrambling and TMDS_Bit_Clock_Ratio + */ + buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE; + mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; + } else if (scdc->scrambling.low_rates) { + /* + * Enable scrambling and HDMI2.0 when scrambling capability of sink + * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit + */ + buff = SCDC_SCRAMBLING_ENABLE; + mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; + } + + /* TMDS config */ + cdns_hdmi_scdc_write(mhdp, SCDC_TMDS_CONFIG, buff); +} + +static void hdmi_lanes_config(struct cdns_mhdp_device *mhdp) +{ + u32 lane_mapping = mhdp->plat_data->lane_mapping; + /* Line swaping */ + cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | lane_mapping); +} + +static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp, + struct drm_display_mode *mode) +{ + struct hdmi_avi_infoframe frame; + int format = mhdp->video_info.color_fmt; + struct drm_connector_state *conn_state = mhdp->connector.base.state; + struct drm_display_mode *adj_mode; + enum hdmi_quantization_range qr; + u8 buf[32]; + int ret; + + /* Initialise info frame from DRM mode */ + drm_hdmi_avi_infoframe_from_display_mode(&frame, + &mhdp->connector.base, mode); + + switch (format) { + case YCBCR_4_4_4: + frame.colorspace = HDMI_COLORSPACE_YUV444; + break; + case YCBCR_4_2_2: + frame.colorspace = HDMI_COLORSPACE_YUV422; + break; + case YCBCR_4_2_0: + frame.colorspace = HDMI_COLORSPACE_YUV420; + break; + default: + frame.colorspace = HDMI_COLORSPACE_RGB; + break; + } + + drm_hdmi_avi_infoframe_colorspace(&frame, conn_state); + + adj_mode = &mhdp->bridge.base.encoder->crtc->state->adjusted_mode; + + qr = drm_default_rgb_quant_range(adj_mode); + + drm_hdmi_avi_infoframe_quant_range(&frame, &mhdp->connector.base, + adj_mode, qr); + + ret = hdmi_avi_infoframe_check(&frame); + if (WARN_ON(ret)) + return false; + + ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); + if (ret < 0) { + DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); + return -1; + } + + buf[0] = 0; + cdns_mhdp_infoframe_set(mhdp, 0, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AVI); + return 0; +} + +static void hdmi_vendor_info_set(struct cdns_mhdp_device *mhdp, + struct drm_display_mode *mode) +{ + struct hdmi_vendor_infoframe frame; + u8 buf[32]; + int ret; + + /* Initialise vendor frame from DRM mode */ + ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, &mhdp->connector.base, mode); + if (ret < 0) { + DRM_INFO("No vendor infoframe\n"); + return; + } + + ret = hdmi_vendor_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); + if (ret < 0) { + DRM_WARN("Unable to pack vendor infoframe: %d\n", ret); + return; + } + + buf[0] = 0; + cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_VENDOR); +} + +static void hdmi_drm_info_set(struct cdns_mhdp_device *mhdp) +{ + struct drm_connector_state *conn_state; + struct hdmi_drm_infoframe frame; + u8 buf[32]; + int ret; + + conn_state = mhdp->connector.base.state; + + if (!conn_state->hdr_output_metadata) + return; + + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); + if (ret < 0) { + DRM_DEBUG_KMS("couldn't set HDR metadata in infoframe\n"); + return; + } + + ret = hdmi_drm_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); + if (ret < 0) { + DRM_DEBUG_KMS("couldn't pack HDR infoframe\n"); + return; + } + + buf[0] = 0; + cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_DRM); +} + +void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp) +{ + struct drm_display_mode *mode = &mhdp->mode; + int ret; + + /* video mode check */ + if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == 0) + return; + + hdmi_lanes_config(mhdp); + + cdns_mhdp_plat_call(mhdp, pclk_rate); + + /* Delay for HDMI FW stable after pixel clock relock */ + msleep(20); + + cdns_mhdp_plat_call(mhdp, phy_set); + + hdmi_sink_config(mhdp); + + ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type, mhdp->hdmi.char_rate); + if (ret < 0) { + DRM_ERROR("%s, ret = %d\n", __func__, ret); + return; + } + + /* Config GCP */ + if (mhdp->video_info.color_depth == 8) + cdns_hdmi_disable_gcp(mhdp); + else + cdns_hdmi_enable_gcp(mhdp); + + ret = hdmi_avi_info_set(mhdp, mode); + if (ret < 0) { + DRM_ERROR("%s ret = %d\n", __func__, ret); + return; + } + + /* vendor info frame is enabled only for HDMI1.4 4K mode */ + hdmi_vendor_info_set(mhdp, mode); + + hdmi_drm_info_set(mhdp); + + ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info); + if (ret < 0) { + DRM_ERROR("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); + return; + } +} + +static enum drm_connector_status +cdns_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct cdns_mhdp_device *mhdp = + container_of(connector, struct cdns_mhdp_device, connector.base); + u8 hpd = 0xf; + + hpd = cdns_mhdp_read_hpd(mhdp); + if (hpd == 1) + return connector_status_connected; + else if (hpd == 0) + return connector_status_disconnected; + else { + DRM_INFO("Unknow cable status, hdp=%u\n", hpd); + return connector_status_unknown; + } +} + +static int cdns_hdmi_connector_get_modes(struct drm_connector *connector) +{ + struct cdns_mhdp_device *mhdp = + container_of(connector, struct cdns_mhdp_device, connector.base); + int num_modes = 0; + struct edid *edid; + + edid = drm_do_get_edid(&mhdp->connector.base, + cdns_hdmi_get_edid_block, mhdp); + if (edid) { + dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n", + edid->header[0], edid->header[1], + edid->header[2], edid->header[3], + edid->header[4], edid->header[5], + edid->header[6], edid->header[7]); + drm_connector_update_edid_property(connector, edid); + num_modes = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + if (num_modes == 0) + DRM_ERROR("Invalid edid\n"); + return num_modes; +} + +static bool blob_equal(const struct drm_property_blob *a, + const struct drm_property_blob *b) +{ + if (a && b) + return a->length == b->length && + !memcmp(a->data, b->data, a->length); + + return !a == !b; +} + +static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct drm_connector_state *new_con_state = + drm_atomic_get_new_connector_state(state, connector); + struct drm_connector_state *old_con_state = + drm_atomic_get_old_connector_state(state, connector); + struct drm_crtc *crtc = new_con_state->crtc; + struct drm_crtc_state *new_crtc_state; + + if (!blob_equal(new_con_state->hdr_output_metadata, + old_con_state->hdr_output_metadata) || + new_con_state->colorspace != old_con_state->colorspace) { + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + new_crtc_state->mode_changed = + !new_con_state->hdr_output_metadata || + !old_con_state->hdr_output_metadata || + new_con_state->colorspace != old_con_state->colorspace; + } + + return 0; +} + +static const struct drm_connector_funcs cdns_hdmi_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = cdns_hdmi_connector_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs cdns_hdmi_connector_helper_funcs = { + .get_modes = cdns_hdmi_connector_get_modes, + .atomic_check = cdns_hdmi_connector_atomic_check, +}; + +static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + struct drm_mode_config *config = &bridge->dev->mode_config; + struct drm_encoder *encoder = bridge->encoder; + struct drm_connector *connector = &mhdp->connector.base; + + connector->interlace_allowed = 1; + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, &cdns_hdmi_connector_helper_funcs); + + drm_connector_init(bridge->dev, connector, &cdns_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + + drm_object_attach_property(&connector->base, + config->hdr_output_metadata_property, 0); + + if (!drm_mode_create_hdmi_colorspace_property(connector)) + drm_object_attach_property(&connector->base, + connector->colorspace_property, 0); + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} + +static enum drm_mode_status +cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + enum drm_mode_status mode_status = MODE_OK; + int ret; + + /* We don't support double-clocked and Interlaced modes */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK || + mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_BAD; + + /* MAX support pixel clock rate 594MHz */ + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + + /* 4096x2160 is not supported */ + if (mode->hdisplay > 3840 || mode->vdisplay > 2160) + return MODE_BAD_HVALUE; + + /* Check modes supported by PHY */ + mhdp->hdmi.mode_valid = mode; + ret = cdns_mhdp_plat_call(mhdp, phy_video_valid); + if (ret == false) + return MODE_CLOCK_RANGE; + + return mode_status; +} + +static void cdns_hdmi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *orig_mode, + const struct drm_display_mode *mode) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + struct video_info *video = &mhdp->video_info; + + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + + DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); + + mutex_lock(&mhdp->lock); + cdns_hdmi_mode_set(mhdp); + mutex_unlock(&mhdp->lock); +} + +bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct cdns_mhdp_device *mhdp = bridge->driver_private; + struct video_info *video = &mhdp->video_info; + + video->color_depth = 8; + video->color_fmt = PXL_RGB; + + return true; +} + +static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = { + .attach = cdns_hdmi_bridge_attach, + .mode_set = cdns_hdmi_bridge_mode_set, + .mode_valid = cdns_hdmi_bridge_mode_valid, + .mode_fixup = cdns_hdmi_bridge_mode_fixup, +}; + +static void hotplug_work_func(struct work_struct *work) +{ + struct cdns_mhdp_device *mhdp = container_of(work, + struct cdns_mhdp_device, hotplug_work.work); + struct drm_connector *connector = &mhdp->connector.base; + + drm_helper_hpd_irq_event(connector->dev); + + if (connector->status == connector_status_connected) { + DRM_INFO("HDMI Cable Plug In\n"); + enable_irq(mhdp->irq[IRQ_OUT]); + } else if (connector->status == connector_status_disconnected) { + /* Cable Disconnedted */ + DRM_INFO("HDMI Cable Plug Out\n"); + enable_irq(mhdp->irq[IRQ_IN]); + } +} + +static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data) +{ + struct cdns_mhdp_device *mhdp = data; + + disable_irq_nosync(irq); + + mod_delayed_work(system_wq, &mhdp->hotplug_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); + + return IRQ_HANDLED; +} + +static int __cdns_hdmi_probe(struct platform_device *pdev, + struct cdns_mhdp_device *mhdp) +{ + struct device *dev = &pdev->dev; + struct platform_device_info pdevinfo; + struct resource *iores = NULL; + int ret; + + mutex_init(&mhdp->lock); + + INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mhdp->regs_base = devm_ioremap(dev, iores->start, resource_size(iores)); + if (IS_ERR(mhdp->regs_base)) { + dev_err(dev, "No regs_base memory\n"); + return -ENOMEM; + } + + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); + if (mhdp->irq[IRQ_IN] < 0) { + dev_info(dev, "No plug_in irq number\n"); + return -EPROBE_DEFER; + } + + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); + if (mhdp->irq[IRQ_OUT] < 0) { + dev_info(dev, "No plug_out irq number\n"); + return -EPROBE_DEFER; + } + + cdns_mhdp_plat_call(mhdp, power_on); + + /* Initialize FW */ + cdns_mhdp_plat_call(mhdp, firmware_init); + + /* HDMI FW alive check */ + ret = cdns_mhdp_check_alive(mhdp); + if (ret == false) { + dev_err(dev, "NO HDMI FW running\n"); + return -ENXIO; + } + + /* Enable Hotplug Detect thread */ + irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], + NULL, cdns_hdmi_irq_thread, + IRQF_ONESHOT, dev_name(dev), + mhdp); + if (ret < 0) { + dev_err(dev, "can't claim irq %d\n", + mhdp->irq[IRQ_IN]); + return -EINVAL; + } + + irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], + NULL, cdns_hdmi_irq_thread, + IRQF_ONESHOT, dev_name(dev), + mhdp); + if (ret < 0) { + dev_err(dev, "can't claim irq %d\n", + mhdp->irq[IRQ_OUT]); + return -EINVAL; + } + + if (cdns_mhdp_read_hpd(mhdp)) + enable_irq(mhdp->irq[IRQ_OUT]); + else + enable_irq(mhdp->irq[IRQ_IN]); + + mhdp->bridge.base.driver_private = mhdp; + mhdp->bridge.base.funcs = &cdns_hdmi_bridge_funcs; +#ifdef CONFIG_OF + mhdp->bridge.base.of_node = dev->of_node; +#endif + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.parent = dev; + pdevinfo.id = PLATFORM_DEVID_AUTO; + + dev_set_drvdata(dev, mhdp); + + /* register audio driver */ + cdns_mhdp_register_audio_driver(dev); + + return 0; +} + +static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp) +{ + cdns_mhdp_unregister_audio_driver(mhdp->dev); +} + +/* ----------------------------------------------------------------------------- + * Probe/remove API, used from platforms based on the DRM bridge API. + */ +int cdns_hdmi_probe(struct platform_device *pdev, + struct cdns_mhdp_device *mhdp) +{ + int ret; + + ret = __cdns_hdmi_probe(pdev, mhdp); + if (ret < 0) + return ret; + + drm_bridge_add(&mhdp->bridge.base); + + return 0; +} +EXPORT_SYMBOL_GPL(cdns_hdmi_probe); + +void cdns_hdmi_remove(struct platform_device *pdev) +{ + struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); + + drm_bridge_remove(&mhdp->bridge.base); + + __cdns_hdmi_remove(mhdp); +} +EXPORT_SYMBOL_GPL(cdns_hdmi_remove); + +/* ----------------------------------------------------------------------------- + * Bind/unbind API, used from platforms based on the component framework. + */ +int cdns_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, + struct cdns_mhdp_device *mhdp) +{ + int ret; + + ret = __cdns_hdmi_probe(pdev, mhdp); + if (ret) + return ret; + + ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL, 0); + if (ret) { + cdns_hdmi_remove(pdev); + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cdns_hdmi_bind); + +void cdns_hdmi_unbind(struct device *dev) +{ + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + + __cdns_hdmi_remove(mhdp); +} +EXPORT_SYMBOL_GPL(cdns_hdmi_unbind); + +MODULE_AUTHOR("Sandor Yu "); +MODULE_DESCRIPTION("Cadence HDMI transmitter driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cdn-hdmi"); diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h index b122bf5f0bdf..fea648070611 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h @@ -23,4 +23,18 @@ int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, /* Audio */ int cdns_mhdp_register_audio_driver(struct device *dev); void cdns_mhdp_unregister_audio_driver(struct device *dev); + +/* HDMI */ +int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value); +void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, + u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type); +int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, + int protocol, u32 char_rate); +int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp); +int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp); +int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, + struct drm_display_mode *mode, struct video_info *video_info); +int cdns_hdmi_get_edid_block(void *data, u8 *edid, + u32 block, size_t length); + #endif diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c new file mode 100644 index 000000000000..b8826a9ed524 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + * + */ + +#include +#include +#include +#include +#include + +#include "cdns-mhdp-common.h" + +void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, + u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type) +{ + u32 *packet32, len32; + u32 val, i; + + /* invalidate entry */ + val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id); + cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG); + cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN); + + /* flush fifo 1 */ + cdns_mhdp_bus_write(F_FIFO1_FLUSH(1), mhdp, SOURCE_PIF_FIFO1_FLUSH); + + /* write packet into memory */ + packet32 = (u32 *)packet; + len32 = packet_len / 4; + for (i = 0; i < len32; i++) + cdns_mhdp_bus_write(F_DATA_WR(packet32[i]), mhdp, SOURCE_PIF_DATA_WR); + + /* write entry id */ + cdns_mhdp_bus_write(F_WR_ADDR(entry_id), mhdp, SOURCE_PIF_WR_ADDR); + + /* write request */ + cdns_mhdp_bus_write(F_HOST_WR(1), mhdp, SOURCE_PIF_WR_REQ); + + /* update entry */ + val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) | + F_PACKET_TYPE(packet_type) | F_PKT_ALLOC_ADDRESS(entry_id); + cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG); + + cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN); +} + +int cdns_hdmi_get_edid_block(void *data, u8 *edid, + u32 block, size_t length) +{ + struct cdns_mhdp_device *mhdp = data; + u8 msg[2], reg[5], i; + int ret; + + for (i = 0; i < 4; i++) { + msg[0] = block / 2; + msg[1] = block % 2; + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID, + sizeof(msg), msg); + if (ret) + continue; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX, + HDMI_TX_EDID, sizeof(reg) + length); + if (ret) + continue; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); + if (ret) + continue; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length); + if (ret) + continue; + + if ((reg[3] << 8 | reg[4]) == length) + break; + } + + if (ret) + DRM_ERROR("get block[%d] edid failed: %d\n", block, ret); + return ret; +} + +int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data) +{ + u8 msg[4], reg[6]; + int ret; + + msg[0] = 0x54; + msg[1] = addr; + msg[2] = 0; + msg[3] = 1; + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ, + sizeof(msg), msg); + if (ret) + goto err_scdc_read; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX, + HDMI_TX_READ, sizeof(reg)); + if (ret) + goto err_scdc_read; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); + if (ret) + goto err_scdc_read; + + *data = reg[5]; + +err_scdc_read: + if (ret) + DRM_ERROR("scdc read failed: %d\n", ret); + return ret; +} + +int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value) +{ + u8 msg[5], reg[5]; + int ret; + + msg[0] = 0x54; + msg[1] = addr; + msg[2] = 0; + msg[3] = 1; + msg[4] = value; + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE, + sizeof(msg), msg); + if (ret) + goto err_scdc_write; + + ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX, + HDMI_TX_WRITE, sizeof(reg)); + if (ret) + goto err_scdc_write; + + ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); + if (ret) + goto err_scdc_write; + + if (reg[0] != 0) + ret = -EINVAL; + +err_scdc_write: + if (ret) + DRM_ERROR("scdc write failed: %d\n", ret); + return ret; +} + +int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, + int protocol, u32 char_rate) +{ + u32 reg0; + u32 reg1; + u32 val; + int ret; + + /* Set PHY to HDMI data */ + ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, F_SOURCE_PHY_MHDP_SEL(1)); + if (ret < 0) + return ret; + + ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD, + F_HPD_VALID_WIDTH(4) | F_HPD_GLITCH_WIDTH(0)); + if (ret < 0) + return ret; + + /* open CARS */ + ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3); + if (ret < 0) + return ret; + + reg0 = reg1 = 0x7c1f; + if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) { + reg0 = 0; + reg1 = 0xFFFFF; + } + ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0); + if (ret < 0) + return ret; + ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1); + if (ret < 0) + return ret; + + /* set hdmi mode and preemble mode data enable */ + val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) | + F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF); + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); + + return ret; +} + +int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, + struct drm_display_mode *mode, + struct video_info *video_info) +{ + int ret; + u32 val; + u32 vsync_lines = mode->vsync_end - mode->vsync_start; + u32 eof_lines = mode->vsync_start - mode->vdisplay; + u32 sof_lines = mode->vtotal - mode->vsync_end; + u32 hblank = mode->htotal - mode->hdisplay; + u32 hactive = mode->hdisplay; + u32 vblank = mode->vtotal - mode->vdisplay; + u32 vactive = mode->vdisplay; + u32 hfront = mode->hsync_start - mode->hdisplay; + u32 hback = mode->htotal - mode->hsync_end; + u32 vfront = eof_lines; + u32 hsync = hblank - hfront - hback; + u32 vsync = vsync_lines; + u32 vback = sof_lines; + u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) + + ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2); + + ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << 16) + hblank); + if (ret < 0) + return ret; + + ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << 16) + vblank); + if (ret < 0) + return ret; + + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, (vfront << 16) + hfront); + if (ret < 0) + return ret; + + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, (vsync << 16) + hsync); + if (ret < 0) + return ret; + + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, (vback << 16) + hback); + if (ret < 0) + return ret; + + ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, v_h_polarity); + if (ret < 0) + return ret; + + /* Reset Data Enable */ + val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER); + val &= ~F_DATA_EN(1); + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); + if (ret < 0) + return ret; + + /* Set bpc */ + val &= ~F_VIF_DATA_WIDTH(3); + switch (video_info->color_depth) { + case 10: + val |= F_VIF_DATA_WIDTH(1); + break; + case 12: + val |= F_VIF_DATA_WIDTH(2); + break; + case 16: + val |= F_VIF_DATA_WIDTH(3); + break; + case 8: + default: + val |= F_VIF_DATA_WIDTH(0); + break; + } + + /* select color encoding */ + val &= ~F_HDMI_ENCODING(3); + switch (video_info->color_fmt) { + case YCBCR_4_4_4: + val |= F_HDMI_ENCODING(2); + break; + case YCBCR_4_2_2: + val |= F_HDMI_ENCODING(1); + break; + case YCBCR_4_2_0: + val |= F_HDMI_ENCODING(3); + break; + case PXL_RGB: + default: + val |= F_HDMI_ENCODING(0); + break; + } + + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); + if (ret < 0) + return ret; + + /* set data enable */ + val |= F_DATA_EN(1); + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); + + return ret; +} + +int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp) +{ + u32 val; + + val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER); + val &= ~F_GCP_EN(1); + + return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); +} + +int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp) +{ + u32 val; + + val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER); + val |= F_GCP_EN(1); + + return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); +} diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h index 6ffb97e17fae..7902ecb115e2 100644 --- a/include/drm/bridge/cdns-mhdp.h +++ b/include/drm/bridge/cdns-mhdp.h @@ -84,6 +84,7 @@ /* bellow registers need access by mailbox */ /* source phy comp */ +#define PHY_DATA_SEL 0x0818 #define LANES_CONFIG 0x0814 /* source car addr */ @@ -97,6 +98,17 @@ #define SOURCE_CIPHER_CAR 0x0920 #define SOURCE_CRYPTO_CAR 0x0924 +/* mhdp tx_top_comp */ +#define SCHEDULER_H_SIZE 0x1000 +#define SCHEDULER_V_SIZE 0x1004 +#define HDTX_SIGNAL_FRONT_WIDTH 0x100c +#define HDTX_SIGNAL_SYNC_WIDTH 0x1010 +#define HDTX_SIGNAL_BACK_WIDTH 0x1014 +#define HDTX_CONTROLLER 0x1018 +#define HDTX_HPD 0x1020 +#define HDTX_CLOCK_REG_0 0x1024 +#define HDTX_CLOCK_REG_1 0x1028 + /* clock meters addr */ #define CM_CTRL 0x0a00 #define CM_I2S_CTRL 0x0a04 @@ -333,6 +345,7 @@ #define GENERAL_READ_REGISTER 0x07 #define GENERAL_GET_HPD_STATE 0x11 +/* DPTX opcode */ #define DPTX_SET_POWER_MNG 0x00 #define DPTX_SET_HOST_CAPABILITIES 0x01 #define DPTX_GET_EDID 0x02 @@ -352,6 +365,17 @@ #define DPTX_FORCE_LANES 0x10 #define DPTX_HPD_STATE 0x11 +/* HDMI TX opcode */ +#define HDMI_TX_READ 0x00 +#define HDMI_TX_WRITE 0x01 +#define HDMI_TX_UPDATE_READ 0x02 +#define HDMI_TX_EDID 0x03 +#define HDMI_TX_EVENTS 0x04 +#define HDMI_TX_HPD_STATUS 0x05 +#define HDMI_TX_DEBUG_ECHO 0xAA +#define HDMI_TX_TEST 0xBB +#define HDMI_TX_EDID_INTERNAL 0xF0 + #define FW_STANDBY 0 #define FW_ACTIVE 1 @@ -402,6 +426,34 @@ #define TU_SIZE 30 #define CDNS_DP_MAX_LINK_RATE 540000 +#define F_HDMI_ENCODING(x) (((x) & ((1 << 2) - 1)) << 16) +#define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2) +#define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0) +#define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12) +#define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15) +#define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18) +#define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7) +#define F_BCH_EN(x) (((x) & ((1 << 1) - 1)) << 11) +#define F_SOURCE_PHY_MHDP_SEL(x) (((x) & ((1 << 2) - 1)) << 3) +#define F_HPD_VALID_WIDTH(x) (((x) & ((1 << 12) - 1)) << 0) +#define F_HPD_GLITCH_WIDTH(x) (((x) & ((1 << 8) - 1)) << 12) +#define F_HDMI2_CTRL_IL_MODE(x) (((x) & ((1 << 1) - 1)) << 19) +#define F_SOURCE_PHY_LANE0_SWAP(x) (((x) & ((1 << 2) - 1)) << 0) +#define F_SOURCE_PHY_LANE1_SWAP(x) (((x) & ((1 << 2) - 1)) << 2) +#define F_SOURCE_PHY_LANE2_SWAP(x) (((x) & ((1 << 2) - 1)) << 4) +#define F_SOURCE_PHY_LANE3_SWAP(x) (((x) & ((1 << 2) - 1)) << 6) +#define F_SOURCE_PHY_COMB_BYPASS(x) (((x) & ((1 << 1) - 1)) << 21) +#define F_SOURCE_PHY_20_10(x) (((x) & ((1 << 1) - 1)) << 22) +#define F_PKT_ALLOC_ADDRESS(x) (((x) & ((1 << 4) - 1)) << 0) +#define F_ACTIVE_IDLE_TYPE(x) (((x) & ((1 << 1) - 1)) << 17) +#define F_FIFO1_FLUSH(x) (((x) & ((1 << 1) - 1)) << 0) +#define F_PKT_ALLOC_WR_EN(x) (((x) & ((1 << 1) - 1)) << 0) +#define F_DATA_WR(x) (x) +#define F_WR_ADDR(x) (((x) & ((1 << 4) - 1)) << 0) +#define F_HOST_WR(x) (((x) & ((1 << 1) - 1)) << 0) +#define F_TYPE_VALID(x) (((x) & ((1 << 1) - 1)) << 16) +#define F_PACKET_TYPE(x) (((x) & ((1 << 8) - 1)) << 8) + /* audio */ #define AUDIO_PACK_EN BIT(8) #define SAMPLING_FREQ(x) (((x) & 0xf) << 16) @@ -481,6 +533,12 @@ enum audio_format { AFMT_UNUSED, }; +enum { + MODE_DVI, + MODE_HDMI_1_4, + MODE_HDMI_2_0, +}; + struct audio_info { enum audio_format format; int sample_rate; @@ -567,6 +625,11 @@ struct cdns_mhdp_device { struct drm_dp_aux aux; u8 dpcd[DP_RECEIVER_CAP_SIZE]; } dp; + struct _hdmi_data { + u32 char_rate; + u32 hdmi_type; + const struct drm_display_mode *mode_valid; + } hdmi; }; const struct cdns_plat_data *plat_data; @@ -603,4 +666,10 @@ u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); int cdns_dp_bind(struct platform_device *pdev, struct drm_encoder *encoder, struct cdns_mhdp_device *mhdp); void cdns_dp_unbind(struct device *dev); + +/* HDMI */ +int cdns_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, + struct cdns_mhdp_device *mhdp); +void cdns_hdmi_unbind(struct device *dev); + #endif /* CDNS_MHDP_H_ */ From patchwork Mon Jun 1 06:17:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581415 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7423D139A for ; Mon, 1 Jun 2020 06:26:01 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 284EF2074B for ; Mon, 1 Jun 2020 06:26:01 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="YxH80dzR" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 284EF2074B Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=tcui5gNvetnRmskWRNxTkLmkGBvoOVMR9BMGNff9b9Q=; b=YxH80dzR58jyalMRUxATA/jvHy zAXNsdORTmSqTfPKgjw4rQjnjhuSStyJurwTio/dKvjed/XFHmqC2X4wQchK3svq2Bk+t2jtdyEtS gfbQc2U3Hp6Tj5HTD8JLXc6qnfXiVDTyqe1rbkF9PgUf9g/dSPYJpjMnBXHuOmH+7pcRvahjE7NQD Kdf8yCiMuNRmB/CMTbboSlm27uKG9lrg7MSbY1GDgOhahSaH52qBjAjkd34TKR6m/PTzWpGUZ60ZL nlDCf1TxsQ4WoZF47inx57osr5Sxoz2WgXxh5VmNbOGJ1CmaHoCyhWKAJx4JccPmhvv9GehiVm8jZ WLNsyliw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdtT-0003A5-JV; Mon, 01 Jun 2020 06:25:55 +0000 Received: from inva020.nxp.com ([92.121.34.13]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdr4-0007RG-H2; Mon, 01 Jun 2020 06:23:33 +0000 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 15B3B1A0643; Mon, 1 Jun 2020 08:23:25 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 1DBBE1A0631; Mon, 1 Jun 2020 08:23:19 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id A8AFD4031F; Mon, 1 Jun 2020 14:23:11 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 6/7] drm: imx: mhdp: Initial support for i.MX8MQ MHDP HDMI Date: Mon, 1 Jun 2020 14:17:36 +0800 Message-Id: <946044d0c8ab554219487b79ecaaf2b73dc6dcdd.1590982881.git.Sandor.yu@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232327_018847_C1667F80 X-CRM114-Status: GOOD ( 14.75 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.13 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu Add initial support for i.MX8MQ MHDP HDMI. Add MHDP HDMI PHY configuration. The features are same as mhdp hdmi bridge driver. Signed-off-by: Sandor Yu --- drivers/gpu/drm/imx/mhdp/Kconfig | 5 +- drivers/gpu/drm/imx/mhdp/Makefile | 2 +- drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c | 588 ++++++++++++++++++ drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 12 + drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 2 + 5 files changed, 606 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig index c9e07a3bf3df..fc0cf708b900 100644 --- a/drivers/gpu/drm/imx/mhdp/Kconfig +++ b/drivers/gpu/drm/imx/mhdp/Kconfig @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_IMX_CDNS_MHDP - tristate "NXP i.MX MX8 DRM DP" + tristate "NXP i.MX MX8 DRM DP/HDMI" select DRM_CDNS_MHDP select DRM_CDNS_DP + select DRM_CDNS_HDMI help - Choose this if you want to use Displayport on i.MX8. + Choose this if you want to use Displayport/HDMI on i.MX8. diff --git a/drivers/gpu/drm/imx/mhdp/Makefile b/drivers/gpu/drm/imx/mhdp/Makefile index 4383e689445a..ca51a2a9641c 100644 --- a/drivers/gpu/drm/imx/mhdp/Makefile +++ b/drivers/gpu/drm/imx/mhdp/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o +cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o cdns-mhdp-hdmi-phy.o obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c new file mode 100644 index 000000000000..54b01e13a2b6 --- /dev/null +++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cadence High-Definition Multimedia Interface (HDMI) PHY driver + * + * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "cdns-mhdp-phy.h" + +/* HDMI TX clock control settings */ +struct hdmi_ctrl { + u32 pixel_clk_freq_min; + u32 pixel_clk_freq_max; + u32 feedback_factor; + u32 data_range_kbps_min; + u32 data_range_kbps_max; + u32 cmnda_pll0_ip_div; + u32 cmn_ref_clk_dig_div; + u32 ref_clk_divider_scaler; + u32 pll_fb_div_total; + u32 cmnda_pll0_fb_div_low; + u32 cmnda_pll0_fb_div_high; + u32 pixel_div_total; + u32 cmnda_pll0_pxdiv_low; + u32 cmnda_pll0_pxdiv_high; + u32 vco_freq_min; + u32 vco_freq_max; + u32 vco_ring_select; + u32 cmnda_hs_clk_0_sel; + u32 cmnda_hs_clk_1_sel; + u32 hsclk_div_at_xcvr; + u32 hsclk_div_tx_sub_rate; + u32 cmnda_pll0_hs_sym_div_sel; + u32 cmnda_pll0_clk_freq_min; + u32 cmnda_pll0_clk_freq_max; +}; + +/* HDMI TX clock control settings, pixel clock is output */ +static const struct hdmi_ctrl imx8mq_ctrl_table[] = { +/*Minclk Maxclk Fdbak DR_min DR_max ip_d dig DS Totl */ +{ 27000, 27000, 1000, 270000, 270000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x3, 27000, 27000}, +{ 27000, 27000, 1250, 337500, 337500, 0x03, 0x1, 0x1, 300, 0x0EC, 0x03C, 100, 0x030, 0x030, 2700000, 2700000, 0, 2, 2, 2, 4, 0x3, 33750, 33750}, +{ 27000, 27000, 1500, 405000, 405000, 0x03, 0x1, 0x1, 360, 0x11C, 0x048, 120, 0x03A, 0x03A, 3240000, 3240000, 0, 2, 2, 2, 4, 0x3, 40500, 40500}, +{ 27000, 27000, 2000, 540000, 540000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x2, 54000, 54000}, +{ 54000, 54000, 1000, 540000, 540000, 0x03, 0x1, 0x1, 480, 0x17C, 0x060, 80, 0x026, 0x026, 4320000, 4320000, 1, 2, 2, 2, 4, 0x3, 54000, 54000}, +{ 54000, 54000, 1250, 675000, 675000, 0x04, 0x1, 0x1, 400, 0x13C, 0x050, 50, 0x017, 0x017, 2700000, 2700000, 0, 1, 1, 2, 4, 0x2, 67500, 67500}, +{ 54000, 54000, 1500, 810000, 810000, 0x04, 0x1, 0x1, 480, 0x17C, 0x060, 60, 0x01C, 0x01C, 3240000, 3240000, 0, 2, 2, 2, 2, 0x2, 81000, 81000}, +{ 54000, 54000, 2000, 1080000, 1080000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 40, 0x012, 0x012, 2160000, 2160000, 0, 2, 2, 2, 1, 0x1, 108000, 108000}, +{ 74250, 74250, 1000, 742500, 742500, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 80, 0x026, 0x026, 5940000, 5940000, 1, 2, 2, 2, 4, 0x3, 74250, 74250}, +{ 74250, 74250, 1250, 928125, 928125, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 50, 0x017, 0x017, 3712500, 3712500, 1, 1, 1, 2, 4, 0x2, 92812, 92812}, +{ 74250, 74250, 1500, 1113750, 1113750, 0x04, 0x1, 0x1, 660, 0x20C, 0x084, 60, 0x01C, 0x01C, 4455000, 4455000, 1, 2, 2, 2, 2, 0x2, 111375, 111375}, +{ 74250, 74250, 2000, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 40, 0x012, 0x012, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500}, +{ 99000, 99000, 1000, 990000, 990000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 2, 0x2, 99000, 99000}, +{ 99000, 99000, 1250, 1237500, 1237500, 0x03, 0x1, 0x1, 275, 0x0D8, 0x037, 25, 0x00B, 0x00A, 2475000, 2475000, 0, 1, 1, 2, 2, 0x1, 123750, 123750}, +{ 99000, 99000, 1500, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 30, 0x00D, 0x00D, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500}, +{ 99000, 99000, 2000, 1980000, 1980000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 1, 0x1, 198000, 198000}, +{148500, 148500, 1000, 1485000, 1485000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 2, 0x2, 148500, 148500}, +{148500, 148500, 1250, 1856250, 1856250, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 3712500, 3712500, 1, 1, 1, 2, 2, 0x1, 185625, 185625}, +{148500, 148500, 1500, 2227500, 2227500, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 30, 0x00D, 0x00D, 4455000, 4455000, 1, 1, 1, 2, 2, 0x1, 222750, 222750}, +{148500, 148500, 2000, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 1, 0x1, 297000, 297000}, +{198000, 198000, 1000, 1980000, 1980000, 0x03, 0x1, 0x1, 220, 0x0AC, 0x02C, 10, 0x003, 0x003, 1980000, 1980000, 0, 1, 1, 2, 1, 0x0, 198000, 198000}, +{198000, 198000, 1250, 2475000, 2475000, 0x03, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 4950000, 4950000, 1, 1, 1, 2, 2, 0x1, 247500, 247500}, +{198000, 198000, 1500, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 15, 0x006, 0x005, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000}, +{198000, 198000, 2000, 3960000, 3960000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 20, 0x008, 0x008, 3960000, 3960000, 1, 1, 1, 2, 1, 0x0, 396000, 396000}, +{297000, 297000, 1000, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 10, 0x003, 0x003, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000}, +{297000, 297000, 1500, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 15, 0x006, 0x005, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500}, +{297000, 297000, 2000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 20, 0x008, 0x008, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000}, +{594000, 594000, 1000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000}, +{594000, 594000, 750, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 10, 0x003, 0x003, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500}, +{594000, 594000, 625, 3712500, 3712500, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 10, 0x003, 0x003, 3712500, 3712500, 1, 1, 1, 2, 1, 0x0, 371250, 371250}, +{594000, 594000, 500, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 2, 0x1, 297000, 297000}, +}; + +/* HDMI TX PLL tuning settings */ +struct hdmi_pll_tuning { + u32 vco_freq_bin; + u32 vco_freq_min; + u32 vco_freq_max; + u32 volt_to_current_coarse; + u32 volt_to_current; + u32 ndac_ctrl; + u32 pmos_ctrl; + u32 ptat_ndac_ctrl; + u32 feedback_div_total; + u32 charge_pump_gain; + u32 coarse_code; + u32 v2i_code; + u32 vco_cal_code; +}; + +/* HDMI TX PLL tuning settings, pixel clock is output */ +static const struct hdmi_pll_tuning imx8mq_pll_table[] = { +/* bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain Coa V2I CAL */ + { 1, 1980000, 1980000, 0x4, 0x3, 0x0, 0x09, 0x09, 220, 0x42, 160, 5, 183 }, + { 2, 2160000, 2160000, 0x4, 0x3, 0x0, 0x09, 0x09, 240, 0x42, 166, 6, 208 }, + { 3, 2475000, 2475000, 0x5, 0x3, 0x1, 0x00, 0x07, 275, 0x42, 167, 6, 209 }, + { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 300, 0x42, 188, 6, 230 }, + { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 400, 0x4C, 188, 6, 230 }, + { 5, 2970000, 2970000, 0x6, 0x3, 0x1, 0x00, 0x07, 330, 0x42, 183, 6, 225 }, + { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 360, 0x42, 203, 7, 256 }, + { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 480, 0x4C, 203, 7, 256 }, + { 7, 3712500, 3712500, 0x4, 0x3, 0x0, 0x07, 0x0F, 550, 0x4C, 212, 7, 257 }, + { 8, 3960000, 3960000, 0x5, 0x3, 0x0, 0x07, 0x0F, 440, 0x42, 184, 6, 226 }, + { 9, 4320000, 4320000, 0x5, 0x3, 0x1, 0x07, 0x0F, 480, 0x42, 205, 7, 258 }, + { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 495, 0x42, 219, 7, 272 }, + { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 660, 0x4C, 219, 7, 272 }, + { 11, 4950000, 4950000, 0x6, 0x3, 0x1, 0x00, 0x07, 550, 0x42, 213, 7, 258 }, + { 12, 5940000, 5940000, 0x7, 0x3, 0x1, 0x00, 0x07, 660, 0x42, 244, 8, 292 }, +}; + +static void hdmi_arc_config(struct cdns_mhdp_device *mhdp) +{ + u16 txpu_calib_code; + u16 txpd_calib_code; + u16 txpu_adj_calib_code; + u16 txpd_adj_calib_code; + u16 prev_calib_code; + u16 new_calib_code; + u16 rdata; + + /* Power ARC */ + cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 0x0001); + + prev_calib_code = cdns_phy_reg_read(mhdp, TX_DIG_CTRL_REG_2); + txpu_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPUCAL_CTRL); + txpd_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPDCAL_CTRL); + txpu_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPU_ADJ_CTRL); + txpd_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPD_ADJ_CTRL); + + new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2) + + txpu_adj_calib_code + txpd_adj_calib_code; + + if (new_calib_code != prev_calib_code) { + rdata = cdns_phy_reg_read(mhdp, TX_ANA_CTRL_REG_1); + rdata &= 0xDFFF; + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata); + cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, new_calib_code); + mdelay(10); + rdata |= 0x2000; + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata); + udelay(150); + } + + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2098); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0010); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x4001); + mdelay(5); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2198); + mdelay(5); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030D); + udelay(100); + cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030F); +} + +static void hdmi_phy_set_vswing(struct cdns_mhdp_device *mhdp) +{ + const u32 num_lanes = 4; + u32 k; + + for (k = 0; k < num_lanes; k++) { + cdns_phy_reg_write(mhdp, (TX_DIAG_TX_DRV | (k << 9)), 0x7c0); + cdns_phy_reg_write(mhdp, (TX_TXCC_CPOST_MULT_00_0 | (k << 9)), 0x0); + cdns_phy_reg_write(mhdp, (TX_TXCC_CAL_SCLR_MULT_0 | (k << 9)), 0x120); + } +} + +static int hdmi_feedback_factor(struct cdns_mhdp_device *mhdp) +{ + u32 feedback_factor; + + switch (mhdp->video_info.color_fmt) { + case YCBCR_4_2_2: + feedback_factor = 1000; + break; + case YCBCR_4_2_0: + switch (mhdp->video_info.color_depth) { + case 8: + feedback_factor = 500; + break; + case 10: + feedback_factor = 625; + break; + case 12: + feedback_factor = 750; + break; + case 16: + feedback_factor = 1000; + break; + default: + DRM_ERROR("Invalid ColorDepth\n"); + return 0; + } + break; + default: + /* Assume RGB/YUV444 */ + switch (mhdp->video_info.color_depth) { + case 10: + feedback_factor = 1250; + break; + case 12: + feedback_factor = 1500; + break; + case 16: + feedback_factor = 2000; + break; + default: + feedback_factor = 1000; + } + } + return feedback_factor; +} + +static int hdmi_phy_config(struct cdns_mhdp_device *mhdp, + const struct hdmi_ctrl *p_ctrl_table, + const struct hdmi_pll_tuning *p_pll_table, + char pclk_in) +{ + const u32 num_lanes = 4; + u32 val, i, k; + + /* enable PHY isolation mode only for CMN */ + cdns_phy_reg_write(mhdp, PHY_PMA_ISOLATION_CTRL, 0xD000); + + /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers */ + val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_PLL_CTRL1); + val &= 0xFF00; + val |= 0x0012; + cdns_phy_reg_write(mhdp, PHY_PMA_ISO_PLL_CTRL1, val); + + /* assert PHY reset from isolation register */ + cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0000); + /* assert PMA CMN reset */ + cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0000); + + /* register XCVR_DIAG_BIDI_CTRL */ + for (k = 0; k < num_lanes; k++) + cdns_phy_reg_write(mhdp, XCVR_DIAG_BIDI_CTRL | (k << 9), 0x00FF); + + /* Describing Task phy_cfg_hdp */ + + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); + val &= 0xFFF7; + val |= 0x0008; + cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); + + /* PHY Registers */ + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); + val &= 0xCFFF; + val |= p_ctrl_table->cmn_ref_clk_dig_div << 12; + cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); + + val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); + val &= 0x00FF; + val |= 0x1200; + cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); + + /* Common control module control and diagnostic registers */ + val = cdns_phy_reg_read(mhdp, CMN_CDIAG_REFCLK_CTRL); + val &= 0x8FFF; + val |= p_ctrl_table->ref_clk_divider_scaler << 12; + val |= 0x00C0; + cdns_phy_reg_write(mhdp, CMN_CDIAG_REFCLK_CTRL, val); + + /* High speed clock used */ + val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL); + val &= 0xFF00; + val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 0; + val |= (p_ctrl_table->cmnda_hs_clk_1_sel >> 1) << 4; + cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val); + + for (k = 0; k < num_lanes; k++) { + val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9))); + val &= 0xCFFF; + val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 12; + cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); + } + + /* PLL 0 control state machine registers */ + val = p_ctrl_table->vco_ring_select << 12; + cdns_phy_reg_write(mhdp, CMN_PLLSM0_USER_DEF_CTRL, val); + + if (pclk_in == true) + val = 0x30A0; + else { + val = cdns_phy_reg_read(mhdp, CMN_PLL0_VCOCAL_START); + val &= 0xFE00; + val |= p_pll_table->vco_cal_code; + } + cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_START, val); + + cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_INIT_TMR, 0x0064); + cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_ITER_TMR, 0x000A); + + /* Common functions control and diagnostics registers */ + val = p_ctrl_table->cmnda_pll0_hs_sym_div_sel << 8; + val |= p_ctrl_table->cmnda_pll0_ip_div; + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_INCLK_CTRL, val); + + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_OVRD, 0x0000); + + val = p_ctrl_table->cmnda_pll0_fb_div_high; + val |= (1 << 15); + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBH_OVRD, val); + + val = p_ctrl_table->cmnda_pll0_fb_div_low; + val |= (1 << 15); + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBL_OVRD, val); + + if (pclk_in == false) { + val = p_ctrl_table->cmnda_pll0_pxdiv_low; + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVL, val); + + val = p_ctrl_table->cmnda_pll0_pxdiv_high; + val |= (1 << 15); + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVH, val); + } + + val = p_pll_table->volt_to_current_coarse; + val |= (p_pll_table->volt_to_current) << 4; + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_V2I_TUNE, val); + + val = p_pll_table->charge_pump_gain; + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_CP_TUNE, val); + + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_LF_PROG, 0x0008); + + val = p_pll_table->pmos_ctrl; + val |= (p_pll_table->ndac_ctrl) << 8; + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE1, val); + + val = p_pll_table->ptat_ndac_ctrl; + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE2, val); + + if (pclk_in == true) + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0022); + else + cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0020); + cdns_phy_reg_write(mhdp, CMN_PSM_CLK_CTRL, 0x0016); + + /* Transceiver control and diagnostic registers */ + for (k = 0; k < num_lanes; k++) { + val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); + val &= 0xBFFF; + cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); + } + + for (k = 0; k < num_lanes; k++) { + val = cdns_phy_reg_read(mhdp, (TX_DIAG_TX_CTRL | (k << 9))); + val &= 0xFF3F; + val |= (p_ctrl_table->hsclk_div_tx_sub_rate >> 1) << 6; + cdns_phy_reg_write(mhdp, (TX_DIAG_TX_CTRL | (k << 9)), val); + } + + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); + val &= 0xFF8F; + /* + * TODO + * Pixel clock source from CCM val |= 0x0030 + * Pixel clock gererated by PHY(iMX8MQ): + * --single ended reference clock val |= 0x0030; + * --differential clock val |= 0x0000; + */ + val |= 0x0030; + cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); + + /* for differential clock on the refclk_p and + * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 */ + cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100); + + /* Deassert PHY reset */ + cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0001); + cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0003); + + /* Power state machine registers */ + for (k = 0; k < num_lanes; k++) + cdns_phy_reg_write(mhdp, XCVR_PSM_RCTRL | (k << 9), 0xFEFC); + + /* Assert cmn_macro_pwr_en */ + cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0013); + + /* wait for cmn_macro_pwr_en_ack */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_CMN_CTRL); + if (val & (1 << 5)) + break; + msleep(20); + } + if (i == 10) { + DRM_ERROR("PMA ouput macro power up failed\n"); + return false; + } + + /* wait for cmn_ready */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); + if (val & (1 << 0)) + break; + msleep(20); + } + if (i == 10) { + DRM_ERROR("PMA output ready failed\n"); + return false; + } + + for (k = 0; k < num_lanes; k++) { + cdns_phy_reg_write(mhdp, TX_PSC_A0 | (k << 9), 0x6791); + cdns_phy_reg_write(mhdp, TX_PSC_A1 | (k << 9), 0x6790); + cdns_phy_reg_write(mhdp, TX_PSC_A2 | (k << 9), 0x0090); + cdns_phy_reg_write(mhdp, TX_PSC_A3 | (k << 9), 0x0090); + + val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9)); + val &= 0xFFBB; + cdns_phy_reg_write(mhdp, RX_PSC_CAL | (k << 9), val); + + val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9)); + val &= 0xFFBB; + cdns_phy_reg_write(mhdp, RX_PSC_A0 | (k << 9), val); + } + return true; +} + +static int hdmi_phy_cfg_imx8mq(struct cdns_mhdp_device *mhdp, + struct drm_display_mode *mode) +{ + const struct hdmi_ctrl *p_ctrl_table; + const struct hdmi_pll_tuning *p_pll_table; + const u32 refclk_freq_khz = 27000; + const u8 pclk_in = false; + u32 pixel_freq = mode->clock; + u32 vco_freq, char_freq; + u32 div_total, feedback_factor; + u32 i, ret; + + feedback_factor = hdmi_feedback_factor(mhdp); + + char_freq = pixel_freq * feedback_factor / 1000; + + DRM_INFO("Pixel clock: %d KHz, character clock: %d, bpc is %0d-bit.\n", + pixel_freq, char_freq, mhdp->video_info.color_depth); + + /* Get right row from the ctrl_table table. + * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column. + * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */ + for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++) { + if (feedback_factor == imx8mq_ctrl_table[i].feedback_factor && + pixel_freq == imx8mq_ctrl_table[i].pixel_clk_freq_min) { + p_ctrl_table = &imx8mq_ctrl_table[i]; + break; + } + } + if (i == ARRAY_SIZE(imx8mq_ctrl_table)) { + DRM_WARN("Pixel clk (%d KHz) not supported, color depth (%0d-bit)\n", + pixel_freq, mhdp->video_info.color_depth); + return 0; + } + + div_total = p_ctrl_table->pll_fb_div_total; + vco_freq = refclk_freq_khz * div_total / p_ctrl_table->cmnda_pll0_ip_div; + + /* Get right row from the imx8mq_pll_table table. + * Check if vco_freq_khz and feedback_div_total + * column matching with imx8mq_pll_table. */ + for (i = 0; i < ARRAY_SIZE(imx8mq_pll_table); i++) { + if (vco_freq == imx8mq_pll_table[i].vco_freq_min && + div_total == imx8mq_pll_table[i].feedback_div_total) { + p_pll_table = &imx8mq_pll_table[i]; + break; + } + } + if (i == ARRAY_SIZE(imx8mq_pll_table)) { + DRM_WARN("VCO (%d KHz) not supported\n", vco_freq); + return 0; + } + DRM_INFO("VCO frequency is %d KHz\n", vco_freq); + + ret = hdmi_phy_config(mhdp, p_ctrl_table, p_pll_table, pclk_in); + if (ret == false) + return 0; + + return char_freq; +} + +static int hdmi_phy_power_up(struct cdns_mhdp_device *mhdp) +{ + u32 val, i; + + /* set Power State to A2 */ + cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004); + + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1); + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1); + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1); + cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1); + + /* Wait for Power State A2 Ack */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); + if (val & (1 << 6)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait A2 Ack failed\n"); + return -1; + } + + /* Power up ARC */ + hdmi_arc_config(mhdp); + + /* Configure PHY in A0 mode (PHY must be in the A0 power + * state in order to transmit data) + */ + cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0001); + + /* Wait for Power State A0 Ack */ + for (i = 0; i < 10; i++) { + val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); + if (val & (1 << 4)) + break; + msleep(20); + } + if (i == 10) { + dev_err(mhdp->dev, "Wait A0 Ack failed\n"); + return -1; + } + return 0; +} + +bool cdns_hdmi_phy_mode_valid_imx8mq(struct cdns_mhdp_device *mhdp) +{ + u32 rate = mhdp->hdmi.mode_valid->clock; + int i; + + for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++) + if (rate == imx8mq_ctrl_table[i].pixel_clk_freq_min) + return true; + return false; +} + +int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp) +{ + struct drm_display_mode *mode = &mhdp->mode; + int ret; + + /* Check HDMI FW alive before HDMI PHY init */ + ret = cdns_mhdp_check_alive(mhdp); + if (ret == false) { + DRM_ERROR("NO HDMI FW running\n"); + return -ENXIO; + } + + /* Configure PHY */ + mhdp->hdmi.char_rate = hdmi_phy_cfg_imx8mq(mhdp, mode); + if (mhdp->hdmi.char_rate == 0) { + DRM_ERROR("failed to set phy pclock\n"); + return -EINVAL; + } + + ret = hdmi_phy_power_up(mhdp); + if (ret < 0) + return ret; + + hdmi_phy_set_vswing(mhdp); + + return true; +} diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c index 2dec2e051be6..607d0b34b551 100644 --- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c @@ -23,6 +23,15 @@ static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static struct cdns_plat_data imx8mq_hdmi_drv_data = { + .plat_name = "imx8mq-hdmi", + .bind = cdns_hdmi_bind, + .unbind = cdns_hdmi_unbind, + .phy_set = cdns_hdmi_phy_set_imx8mq, + .phy_video_valid = cdns_hdmi_phy_mode_valid_imx8mq, + .lane_mapping = 0xe4, +}; + static struct cdns_plat_data imx8mq_dp_drv_data = { .plat_name = "imx8mq-dp", .bind = cdns_dp_bind, @@ -32,6 +41,9 @@ static struct cdns_plat_data imx8mq_dp_drv_data = { }; static const struct of_device_id cdns_mhdp_imx_dt_ids[] = { + { .compatible = "nxp,imx8mq-cdns-hdmi", + .data = &imx8mq_hdmi_drv_data + }, { .compatible = "nxp,imx8mq-cdns-dp", .data = &imx8mq_dp_drv_data }, diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h index 79b1907726db..3305a27bcec7 100644 --- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h +++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h @@ -143,4 +143,6 @@ #define PHY_PMA_ISO_RX_DATA_HI 0xCC17 int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp); +int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp); +bool cdns_hdmi_phy_mode_valid_imx8mq(struct cdns_mhdp_device *mhdp); #endif /* _CDNS_MHDP_PHY_H */ From patchwork Mon Jun 1 06:17:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandor Yu X-Patchwork-Id: 11581405 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5CAFF139A for ; Mon, 1 Jun 2020 06:25:29 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 39DAD206A4 for ; Mon, 1 Jun 2020 06:25:29 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="D06YuX7+" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 39DAD206A4 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=fapyVE7GnVubHJmBi9RxbbdDKucdc0JseYLXYE/9B+k=; b=D06YuX7+Di4BrUhkJLYTMg4mzp ABODWAiIuQybTpZ+34qZlNFZ23ZXEH6DULrKK/mhV5NNymheGVI50l3WOwwxcSE4Zkx9CdHFv2VUy DWXR0kPDVsiyl8p8ESKz9HdvfpS7+DHjoTMfu6PR8cemCs6ruGRJ52eQMUXCbcWHNmnJjYMMwFAsw oALFLqLqMivxg6txI+J5CXNTyTeu18th2CzIQwhuryQyKXrl4Ip+l6BZIOUpGSGSy4ooIZ1kKhe6o QpT9sNpVEeReVesXGldY1i8d8fkoOFBOCatBwiPx0mls4vbYBuE3eJN2SL1CxoIgtd/ta4lhOhbG+ o82bSRCQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdsy-0002iS-Hy; Mon, 01 Jun 2020 06:25:24 +0000 Received: from inva021.nxp.com ([92.121.34.21]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jfdr5-0007Rz-D7; Mon, 01 Jun 2020 06:23:31 +0000 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id CB2BB20047B; Mon, 1 Jun 2020 08:23:25 +0200 (CEST) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 03B38200480; Mon, 1 Jun 2020 08:23:20 +0200 (CEST) Received: from localhost.localdomain (mega.ap.freescale.net [10.192.208.232]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id 0C01440323; Mon, 1 Jun 2020 14:23:12 +0800 (SGT) From: sandor.yu@nxp.com To: a.hajda@samsung.com, narmstrong@baylibre.com, Laurent.pinchart@ideasonboard.com, jonas@kwiboo.se, jernej.skrabec@siol.net, heiko@sntech.de, hjc@rock-chips.com, Sandor.yu@nxp.com, dkos@cadence.com, dri-devel@lists.freedesktop.org Subject: [PATCH 7/7] dt-bindings: display: Document Cadence MHDP HDMI/DP bindings Date: Mon, 1 Jun 2020 14:17:37 +0800 Message-Id: <9fa979f1099f7c02fd746f25002d8a652253d70f.1590982881.git.Sandor.yu@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200531_232327_756932_B34D4533 X-CRM114-Status: GOOD ( 11.20 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [92.121.34.21 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-imx@nxp.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Sandor Yu Document the bindings used for the Cadence MHDP HDMI/DP bridge. Signed-off-by: Sandor Yu --- .../bindings/display/bridge/cdns,mhdp.yaml | 46 +++++++++++++++ .../devicetree/bindings/display/imx/mhdp.yaml | 59 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/mhdp.yaml diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml new file mode 100644 index 000000000000..aa23feba744a --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/cdns,mhdp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MHDP TX Encoder + +maintainers: + - Sandor Yu + +description: | + Cadence MHDP Controller supports one or more of the protocols, + such as HDMI and DisplayPort. + Each protocol requires a different FW binaries. + + This document defines device tree properties for the Cadence MHDP Encoder + (CDNS MHDP TX). It doesn't constitue a device tree binding + specification by itself but is meant to be referenced by platform-specific + device tree bindings. + + When referenced from platform device tree bindings the properties defined in + this document are defined as follows. The platform device tree bindings are + responsible for defining whether each property is required or optional. + +properties: + reg: + maxItems: 1 + description: Memory mapped base address and length of the MHDP TX registers. + + interrupts: + maxItems: 2 + + interrupt-names: + - const: plug_in + description: Hotplug detect interrupter for cable plugin event. + - const: plug_out + description: Hotplug detect interrupter for cable plugout event. + + port: + type: object + description: | + The connectivity of the MHDP TX with the rest of the system is + expressed in using ports as specified in the device graph bindings defined + in Documentation/devicetree/bindings/graph.txt. The numbering of the ports + is platform-specific. diff --git a/Documentation/devicetree/bindings/display/imx/mhdp.yaml b/Documentation/devicetree/bindings/display/imx/mhdp.yaml new file mode 100644 index 000000000000..17850cfd1cb1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/mhdp.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/mhdp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MHDP Encoder + +maintainers: + - Sandor Yu + +description: | + The MHDP transmitter is a Cadence HD Display TX controller IP + with a companion PHY IP. + The MHDP supports one or more of the protocols, + such as HDMI(1.4 & 2.0), DisplayPort(1.2). + switching between the two modes (HDMI and DisplayPort) + requires reloading the appropriate FW + + These DT bindings follow the Cadence MHDP TX bindings defined in + Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml with the + following device-specific properties. + +Properties: + compatible: + enum: + - nxp,imx8mq-cdns-hdmi + - nxp,imx8mq-cdns-dp + + reg: See cdns,mhdp.yaml. + + interrupts: See cdns,mhdp.yaml. + + interrupt-names: See cdns,mhdp.yaml. + + ports: See cdns,mhdp.yaml. + +Required: + - compatible + - reg + - interrupts + - interrupt-names + - ports + +Example: + - | + mhdp: mhdp@32c00000 { + compatible = "nxp,imx8mq-cdns-hdmi"; + reg = <0x32c00000 0x100000>; + interrupts = , + ; + interrupt-names = "plug_in", "plug_out"; + + ports { + mhdp_in: endpoint { + remote-endpoint = <&dcss_out>; + }; + }; + };