Received: by 2002:a05:6a10:8a4d:0:0:0:0 with SMTP id dn13csp861371pxb; Fri, 13 Aug 2021 07:58:21 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzGmjmQOwfBcPzHbXKGvLywDJ1wRajquVRUpg3aa61N8S5WMnVBoXtfd/98vXXWufbsd7fW X-Received: by 2002:aa7:c303:: with SMTP id l3mr3637466edq.245.1628866701302; Fri, 13 Aug 2021 07:58:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1628866701; cv=none; d=google.com; s=arc-20160816; b=YF5LgoCsEG1+53vxlU3q2Fyee2/J0PX9hUAur/UUFvVSLM2bsxqZkqpB1jX+/G9dAE J3+v+UkZOgAiDNZ+lDilujr++/VvTIYWtEsosSqgJgyH2ei2wD0BkTmQ0j2OkBp2ZVuR 1uuZujA+g4zd0DxKY+R8vrXYCylNPrPVbzDvhDM8RcuOr4H0AKLuryce2bL9xPFmXyi9 wZBvDDU1344Mvv4sGeNJTdAB5aie4dvf4vTQVwDafQWaQ417Gof6hv08CXTVInY1z2pV tqOMRPAgBYWeClReiFu5OHzjvnzsvrx4s9D2k1M3e0SaICH/X8+Hv8jtJOMZjDeN2Afc HzEw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=e4wyyk7zlVEaIYk8g//Asy/1QUljv82yH+8rAYTNm6g=; b=H+tIEL2/fjJ9kphx6gbpo7hzZt5M2PM4dnIDbwr3hGRRa0z4YqP/EDjTc3t7DYCFKw k9FDLp+WvG3EItfUD7nZBcEETuYv8ImHTO6Men1RkR3KgXFTlxqwDd3uMnBU+v3ZldEZ XliWHvJPwODSvXsJPMFv7Ki/Gsh8bRPFIsd246j+lEaR5M4mrffqPAuM9c6xNWrYs3Qy oaFA9b08FUrVMGW322o11qj9JKwBkqVt17kVvgTwGXwmGIJcGV3nX+1RyHCZGauvw1pK 48o4d4N9PXCPoBaCD1MuwAyV0x+T+T4XLfrb8MolJGMOxJ/Vj14NtJS7e2QJWnX/bu8u oBMg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ZutbHs0p; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id yd7si1987191ejb.585.2021.08.13.07.57.46; Fri, 13 Aug 2021 07:58:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ZutbHs0p; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240965AbhHMOyF (ORCPT + 99 others); Fri, 13 Aug 2021 10:54:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42018 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240969AbhHMOx7 (ORCPT ); Fri, 13 Aug 2021 10:53:59 -0400 Received: from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com [IPv6:2607:f8b0:4864:20::1036]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8AFE7C0617AF; Fri, 13 Aug 2021 07:53:32 -0700 (PDT) Received: by mail-pj1-x1036.google.com with SMTP id fa24-20020a17090af0d8b0290178bfa69d97so16427239pjb.0; Fri, 13 Aug 2021 07:53:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=e4wyyk7zlVEaIYk8g//Asy/1QUljv82yH+8rAYTNm6g=; b=ZutbHs0pLX1QWu3JnpH4MQxzNYjLLgsN3LEtdKTUyK7vinszs7Fpxkj17xUNk3YCwb TwP3A8X91o79GqIrbxgKUv2c70Af7IQGDnN82x5+R9UzCR0Wl1L2yvXJY97+9NdICtbW 2jrxj1apsbcnPF8SQ+Y8CfhOEf6lRzBNjehefcnP/dFT6JV4xLqrILcuTmRDLw7TJU9I TrKe5TaJFR9UDgULWzszsHkFEW/c43wrbGPyE/oDrIBJbb1dT7Kb8D1JcYD2fsX7IDeh 8qP+u2bgUyeIrW89Pku5bNg+tygNMy5ehYdglCoTjv7DaK6C5Qcnu8dXgNv+A4JNdml/ 9lmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=e4wyyk7zlVEaIYk8g//Asy/1QUljv82yH+8rAYTNm6g=; b=Ho/GM3ODGzaOWHXxmG/znOtHPOik15syFAjacm+lyckpx663j45FKwQxi1PY3PDh4Q 4AlX3KaE5M+nFG2+4U7CkbtdZs9x9tzxWT+Evt/hl9a6LUGCv1OlqB/5NkKC+HjiKH7Y RShX3JBldrPWRDgVHmA64fEkghWLTGX6ZWqQDDV3EZR1aO02gipWGGVLhm+wQ7200D17 FAyN070qhio3yRpBO31g936cFqt4PvF29OBgXPhaXWVVyZ01sbcnJSI6wNyYIVsGhZKm quXjt39aZn/id6n0y3wv0OhEBMtT+5ZoIvwxzqysLDYdHgwJQpo+hwR5SepACHqnt+bz VH5A== X-Gm-Message-State: AOAM533R2hDRaNw1FxuOrrYhzp1RWx/25smFfQXXaNPrbvuyajA9+X5J EV4v0UDUgb9CB4TyeKX2UkU= X-Received: by 2002:a63:788e:: with SMTP id t136mr2632054pgc.374.1628866411814; Fri, 13 Aug 2021 07:53:31 -0700 (PDT) Received: from nj08008nbu.spreadtrum.com ([117.18.48.102]) by smtp.gmail.com with ESMTPSA id e12sm2524787pfc.214.2021.08.13.07.53.28 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 13 Aug 2021 07:53:31 -0700 (PDT) From: Kevin Tang To: maarten.lankhorst@linux.intel.com, mripard@kernel.org, sean@poorly.run, airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com, pony1.wu@gmail.com Cc: orsonzhai@gmail.com, zhang.lyra@gmail.com, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org Subject: [PATCH v6 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver Date: Fri, 13 Aug 2021 22:53:02 +0800 Message-Id: <20210813145302.3933-7-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.29.0 In-Reply-To: <20210813145302.3933-1-kevin3.tang@gmail.com> References: <20210813145302.3933-1-kevin3.tang@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Adds dsi host controller support for the Unisoc's display subsystem. Adds dsi phy support for the Unisoc's display subsystem. Only MIPI DSI Displays supported, DP/TV/HMDI will be support in the feature. v1: - Remove dphy and dsi graph binding, merge the dphy driver into the dsi. v2: - Use drm_xxx to replace all DRM_XXX. - Use kzalloc to replace devm_kzalloc for sprd_dsi structure init. v4: - Use drmm_helpers to allocate encoder. - Move allocate encoder and connector to bind function. v5: - Drop the dsi ip file prefix. - Fix the checkpatch warnings. - Add Signed-off-by for dsi&dphy patch. - Use the mode_flags of mipi_dsi_device to setup crtc DPI and EDPI mode. v6: - Redesign the way to access the dsi register. - Reduce the dsi_context member variables. --- drivers/gpu/drm/sprd/Kconfig | 1 + drivers/gpu/drm/sprd/Makefile | 4 +- drivers/gpu/drm/sprd/megacores_pll.c | 317 +++++++ drivers/gpu/drm/sprd/megacores_pll.h | 146 +++ drivers/gpu/drm/sprd/sprd_dpu.c | 17 + drivers/gpu/drm/sprd/sprd_drm.c | 1 + drivers/gpu/drm/sprd/sprd_drm.h | 1 + drivers/gpu/drm/sprd/sprd_dsi.c | 1260 ++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_dsi.h | 94 ++ 9 files changed, 1840 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/sprd/megacores_pll.c create mode 100644 drivers/gpu/drm/sprd/megacores_pll.h create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig index 37762c333..3edeaeca0 100644 --- a/drivers/gpu/drm/sprd/Kconfig +++ b/drivers/gpu/drm/sprd/Kconfig @@ -5,6 +5,7 @@ config DRM_SPRD select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER select DRM_KMS_HELPER + select DRM_MIPI_DSI select VIDEOMODE_HELPERS help Choose this option if you have a Unisoc chipset. diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile index ab12b95e6..73f96c459 100644 --- a/drivers/gpu/drm/sprd/Makefile +++ b/drivers/gpu/drm/sprd/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-y := sprd_drm.o \ - sprd_dpu.o + sprd_dpu.o \ + sprd_dsi.o \ + megacores_pll.o diff --git a/drivers/gpu/drm/sprd/megacores_pll.c b/drivers/gpu/drm/sprd/megacores_pll.c new file mode 100644 index 000000000..0dfd3c372 --- /dev/null +++ b/drivers/gpu/drm/sprd/megacores_pll.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "megacores_pll.h" + +#define L 0 +#define H 1 +#define CLK 0 +#define DATA 1 +#define INFINITY 0xffffffff +#define MIN_OUTPUT_FREQ (100) + +#define AVERAGE(a, b) (min(a, b) + abs((b) - (a)) / 2) + +/* sharkle */ +#define VCO_BAND_LOW 750 +#define VCO_BAND_MID 1100 +#define VCO_BAND_HIGH 1500 +#define PHY_REF_CLK 26000 + +static int dphy_calc_pll_param(struct dphy_pll *pll) +{ + const u32 khz = 1000; + const u32 mhz = 1000000; + const unsigned long long factor = 100; + unsigned long long tmp; + int i; + + pll->potential_fvco = pll->freq / khz; + pll->ref_clk = PHY_REF_CLK / khz; + + for (i = 0; i < 4; ++i) { + if (pll->potential_fvco >= VCO_BAND_LOW && + pll->potential_fvco <= VCO_BAND_HIGH) { + pll->fvco = pll->potential_fvco; + pll->out_sel = BIT(i); + break; + } + pll->potential_fvco <<= 1; + } + if (pll->fvco == 0) + return -EINVAL; + + if (pll->fvco >= VCO_BAND_LOW && pll->fvco <= VCO_BAND_MID) { + /* vco band control */ + pll->vco_band = 0x0; + /* low pass filter control */ + pll->lpf_sel = 1; + } else if (pll->fvco > VCO_BAND_MID && pll->fvco <= VCO_BAND_HIGH) { + pll->vco_band = 0x1; + pll->lpf_sel = 0; + } else + return -EINVAL; + + pll->nint = pll->fvco / pll->ref_clk; + tmp = pll->fvco * factor * mhz; + do_div(tmp, pll->ref_clk); + tmp = tmp - pll->nint * factor * mhz; + tmp *= BIT(20); + do_div(tmp, 100000000); + pll->kint = (u32)tmp; + pll->refin = 3; /* pre-divider bypass */ + pll->sdm_en = true; /* use fraction N PLL */ + pll->fdk_s = 0x1; /* fraction */ + pll->cp_s = 0x0; + pll->det_delay = 0x1; + + return 0; +} + +static void dphy_set_pll_reg(struct dphy_pll *pll, struct regmap *regmap) +{ + struct pll_reg *reg = &pll->reg; + u8 *val; + int i; + + u8 reg_addr[] = { + 0x03, 0x04, 0x06, 0x08, 0x09, + 0x0a, 0x0b, 0x0e, 0x0f + }; + + reg->_03.bits.prbs_bist = 1; + reg->_03.bits.en_lp_treot = true; + reg->_03.bits.lpf_sel = pll->lpf_sel; + reg->_03.bits.txfifo_bypass = 0; + reg->_04.bits.div = pll->div; + reg->_04.bits.masterof8lane = 1; + reg->_04.bits.cp_s = pll->cp_s; + reg->_04.bits.fdk_s = pll->fdk_s; + reg->_06.bits.nint = pll->nint; + reg->_08.bits.vco_band = pll->vco_band; + reg->_08.bits.sdm_en = pll->sdm_en; + reg->_08.bits.refin = pll->refin; + reg->_09.bits.kint_h = pll->kint >> 12; + reg->_0a.bits.kint_m = (pll->kint >> 4) & 0xff; + reg->_0b.bits.out_sel = pll->out_sel; + reg->_0b.bits.kint_l = pll->kint & 0xf; + reg->_0e.bits.pll_pu_byp = 0; + reg->_0e.bits.pll_pu = 0; + reg->_0e.bits.stopstate_sel = 1; + reg->_0f.bits.det_delay = pll->det_delay; + + val = (u8 *)® + + for (i = 0; i < sizeof(reg_addr); ++i) { + regmap_write(regmap, reg_addr[i], val[i]); + DRM_DEBUG("%02x: %02x\n", reg_addr[i], val[i]); + } +} + +int dphy_pll_config(struct dsi_context *ctx) +{ + struct sprd_dsi *dsi = container_of(ctx, struct sprd_dsi, ctx); + struct regmap *regmap = ctx->regmap; + struct dphy_pll *pll = ctx->pll; + int ret; + + pll->freq = dsi->slave->hs_rate; + + /* FREQ = 26M * (NINT + KINT / 2^20) / out_sel */ + ret = dphy_calc_pll_param(pll); + if (ret) { + drm_err(dsi->drm, "failed to calculate dphy pll parameters\n"); + return ret; + } + dphy_set_pll_reg(pll, regmap); + + return 0; +} + +static void dphy_set_timing_reg(struct regmap *regmap, int type, u8 val[]) +{ + switch (type) { + case REQUEST_TIME: + regmap_write(regmap, 0x31, val[CLK]); + regmap_write(regmap, 0x41, val[DATA]); + regmap_write(regmap, 0x51, val[DATA]); + regmap_write(regmap, 0x61, val[DATA]); + regmap_write(regmap, 0x71, val[DATA]); + + regmap_write(regmap, 0x90, val[CLK]); + regmap_write(regmap, 0xa0, val[DATA]); + regmap_write(regmap, 0xb0, val[DATA]); + regmap_write(regmap, 0xc0, val[DATA]); + regmap_write(regmap, 0xd0, val[DATA]); + break; + case PREPARE_TIME: + regmap_write(regmap, 0x32, val[CLK]); + regmap_write(regmap, 0x42, val[DATA]); + regmap_write(regmap, 0x52, val[DATA]); + regmap_write(regmap, 0x62, val[DATA]); + regmap_write(regmap, 0x72, val[DATA]); + + regmap_write(regmap, 0x91, val[CLK]); + regmap_write(regmap, 0xa1, val[DATA]); + regmap_write(regmap, 0xb1, val[DATA]); + regmap_write(regmap, 0xc1, val[DATA]); + regmap_write(regmap, 0xd1, val[DATA]); + break; + case ZERO_TIME: + regmap_write(regmap, 0x33, val[CLK]); + regmap_write(regmap, 0x43, val[DATA]); + regmap_write(regmap, 0x53, val[DATA]); + regmap_write(regmap, 0x63, val[DATA]); + regmap_write(regmap, 0x73, val[DATA]); + + regmap_write(regmap, 0x92, val[CLK]); + regmap_write(regmap, 0xa2, val[DATA]); + regmap_write(regmap, 0xb2, val[DATA]); + regmap_write(regmap, 0xc2, val[DATA]); + regmap_write(regmap, 0xd2, val[DATA]); + break; + case TRAIL_TIME: + regmap_write(regmap, 0x34, val[CLK]); + regmap_write(regmap, 0x44, val[DATA]); + regmap_write(regmap, 0x54, val[DATA]); + regmap_write(regmap, 0x64, val[DATA]); + regmap_write(regmap, 0x74, val[DATA]); + + regmap_write(regmap, 0x93, val[CLK]); + regmap_write(regmap, 0xa3, val[DATA]); + regmap_write(regmap, 0xb3, val[DATA]); + regmap_write(regmap, 0xc3, val[DATA]); + regmap_write(regmap, 0xd3, val[DATA]); + break; + case EXIT_TIME: + regmap_write(regmap, 0x36, val[CLK]); + regmap_write(regmap, 0x46, val[DATA]); + regmap_write(regmap, 0x56, val[DATA]); + regmap_write(regmap, 0x66, val[DATA]); + regmap_write(regmap, 0x76, val[DATA]); + + regmap_write(regmap, 0x95, val[CLK]); + regmap_write(regmap, 0xA5, val[DATA]); + regmap_write(regmap, 0xB5, val[DATA]); + regmap_write(regmap, 0xc5, val[DATA]); + regmap_write(regmap, 0xd5, val[DATA]); + break; + case CLKPOST_TIME: + regmap_write(regmap, 0x35, val[CLK]); + regmap_write(regmap, 0x94, val[CLK]); + break; + + /* the following just use default value */ + case SETTLE_TIME: + case TA_GET: + case TA_GO: + case TA_SURE: + break; + default: + break; + } +} + +void dphy_timing_config(struct dsi_context *ctx) +{ + struct regmap *regmap = ctx->regmap; + struct dphy_pll *pll = ctx->pll; + const u32 factor = 2; + const u32 scale = 100; + u32 t_ui, t_byteck, t_half_byteck; + u32 range[2], constant; + u8 val[2]; + u32 tmp = 0; + + /* t_ui: 1 ui, byteck: 8 ui, half byteck: 4 ui */ + t_ui = 1000 * scale / (pll->freq / 1000); + t_byteck = t_ui << 3; + t_half_byteck = t_ui << 2; + constant = t_ui << 1; + + /* REQUEST_TIME: HS T-LPX: LP-01 + * For T-LPX, mipi spec defined min value is 50ns, + * but maybe it shouldn't be too small, because BTA, + * LP-10, LP-00, LP-01, all of this is related to T-LPX. + */ + range[L] = 50 * scale; + range[H] = INFINITY; + val[CLK] = DIV_ROUND_UP(range[L] * (factor << 1), t_byteck) - 2; + val[DATA] = val[CLK]; + dphy_set_timing_reg(regmap, REQUEST_TIME, val); + + /* PREPARE_TIME: HS sequence: LP-00 */ + range[L] = 38 * scale; + range[H] = 95 * scale; + tmp = AVERAGE(range[L], range[H]); + val[CLK] = DIV_ROUND_UP(AVERAGE(range[L], range[H]), + t_half_byteck) - 1; + range[L] = 40 * scale + 4 * t_ui; + range[H] = 85 * scale + 6 * t_ui; + tmp |= AVERAGE(range[L], range[H]) << 16; + val[DATA] = DIV_ROUND_UP(AVERAGE(range[L], range[H]), + t_half_byteck) - 1; + dphy_set_timing_reg(regmap, PREPARE_TIME, val); + + /* ZERO_TIME: HS-ZERO */ + range[L] = 300 * scale; + range[H] = INFINITY; + val[CLK] = DIV_ROUND_UP(range[L] * factor + (tmp & 0xffff) + - 525 * t_byteck / 100, t_byteck) - 2; + range[L] = 145 * scale + 10 * t_ui; + val[DATA] = DIV_ROUND_UP(range[L] * factor + + ((tmp >> 16) & 0xffff) - 525 * t_byteck / 100, + t_byteck) - 2; + dphy_set_timing_reg(regmap, ZERO_TIME, val); + + /* TRAIL_TIME: HS-TRAIL */ + range[L] = 60 * scale; + range[H] = INFINITY; + val[CLK] = DIV_ROUND_UP(range[L] * factor - constant, t_half_byteck); + range[L] = max(8 * t_ui, 60 * scale + 4 * t_ui); + val[DATA] = DIV_ROUND_UP(range[L] * 3 / 2 - constant, t_half_byteck) - 2; + dphy_set_timing_reg(regmap, TRAIL_TIME, val); + + /* EXIT_TIME: */ + range[L] = 100 * scale; + range[H] = INFINITY; + val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2; + val[DATA] = val[CLK]; + dphy_set_timing_reg(regmap, EXIT_TIME, val); + + /* CLKPOST_TIME: */ + range[L] = 60 * scale + 52 * t_ui; + range[H] = INFINITY; + val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2; + val[DATA] = val[CLK]; + dphy_set_timing_reg(regmap, CLKPOST_TIME, val); + + /* SETTLE_TIME: + * This time is used for receiver. So for transmitter, + * it can be ignored. + */ + + /* TA_GO: + * transmitter drives bridge state(LP-00) before releasing control, + * reg 0x1f default value: 0x04, which is good. + */ + + /* TA_SURE: + * After LP-10 state and before bridge state(LP-00), + * reg 0x20 default value: 0x01, which is good. + */ + + /* TA_GET: + * receiver drives Bridge state(LP-00) before releasing control + * reg 0x21 default value: 0x03, which is good. + */ +} diff --git a/drivers/gpu/drm/sprd/megacores_pll.h b/drivers/gpu/drm/sprd/megacores_pll.h new file mode 100644 index 000000000..bf20aae65 --- /dev/null +++ b/drivers/gpu/drm/sprd/megacores_pll.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#ifndef _MEGACORES_PLL_H_ +#define _MEGACORES_PLL_H_ + +#include "sprd_dsi.h" + +enum PLL_TIMING { + NONE, + REQUEST_TIME, + PREPARE_TIME, + SETTLE_TIME, + ZERO_TIME, + TRAIL_TIME, + EXIT_TIME, + CLKPOST_TIME, + TA_GET, + TA_GO, + TA_SURE, + TA_WAIT, +}; + +struct pll_reg { + union __reg_03__ { + struct __03 { + u8 prbs_bist: 1; + u8 en_lp_treot: 1; + u8 lpf_sel: 4; + u8 txfifo_bypass: 1; + u8 freq_hopping: 1; + } bits; + u8 val; + } _03; + union __reg_04__ { + struct __04 { + u8 div: 3; + u8 masterof8lane: 1; + u8 hop_trig: 1; + u8 cp_s: 2; + u8 fdk_s: 1; + } bits; + u8 val; + } _04; + union __reg_06__ { + struct __06 { + u8 nint: 7; + u8 mod_en: 1; + } bits; + u8 val; + } _06; + union __reg_07__ { + struct __07 { + u8 kdelta_h: 8; + } bits; + u8 val; + } _07; + union __reg_08__ { + struct __08 { + u8 vco_band: 1; + u8 sdm_en: 1; + u8 refin: 2; + u8 kdelta_l: 4; + } bits; + u8 val; + } _08; + union __reg_09__ { + struct __09 { + u8 kint_h: 8; + } bits; + u8 val; + } _09; + union __reg_0a__ { + struct __0a { + u8 kint_m: 8; + } bits; + u8 val; + } _0a; + union __reg_0b__ { + struct __0b { + u8 out_sel: 4; + u8 kint_l: 4; + } bits; + u8 val; + } _0b; + union __reg_0c__ { + struct __0c { + u8 kstep_h: 8; + } bits; + u8 val; + } _0c; + union __reg_0d__ { + struct __0d { + u8 kstep_m: 8; + } bits; + u8 val; + } _0d; + union __reg_0e__ { + struct __0e { + u8 pll_pu_byp: 1; + u8 pll_pu: 1; + u8 hsbist_len: 2; + u8 stopstate_sel: 1; + u8 kstep_l: 3; + } bits; + u8 val; + } _0e; + union __reg_0f__ { + struct __0f { + u8 det_delay:2; + u8 kdelta: 4; + u8 ldo0p4:2; + } bits; + u8 val; + } _0f; +}; + +struct dphy_pll { + u8 refin; /* Pre-divider control signal */ + u8 cp_s; /* 00: SDM_EN=1, 10: SDM_EN=0 */ + u8 fdk_s; /* PLL mode control: integer or fraction */ + u8 sdm_en; + u8 div; + u8 int_n; /* integer N PLL */ + u32 ref_clk; /* dphy reference clock, unit: MHz */ + u32 freq; /* panel config, unit: KHz */ + u32 fvco; + u32 potential_fvco; + u32 nint; /* sigma delta modulator NINT control */ + u32 kint; /* sigma delta modulator KINT control */ + u8 lpf_sel; /* low pass filter control */ + u8 out_sel; /* post divider control */ + u8 vco_band; /* vco range */ + u8 det_delay; + + struct pll_reg reg; +}; + +struct dsi_context; + +int dphy_pll_config(struct dsi_context *ctx); +void dphy_timing_config(struct dsi_context *ctx); + +#endif /* _MEGACORES_PLL_H_ */ diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c index 448dd4fb6..4e3b30abe 100644 --- a/drivers/gpu/drm/sprd/sprd_dpu.c +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -25,6 +25,7 @@ #include "sprd_drm.h" #include "sprd_dpu.h" +#include "sprd_dsi.h" /* Global control registers */ #define REG_DPU_CTRL 0x04 @@ -686,9 +687,25 @@ static void sprd_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct sprd_dpu *dpu = to_sprd_crtc(crtc); struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct drm_encoder *encoder; + struct mipi_dsi_device *slave; + struct sprd_dsi *dsi; drm_display_mode_to_videomode(mode, &dpu->ctx.vm); + drm_for_each_encoder(encoder, crtc->dev) { + if (encoder->crtc != crtc) + continue; + + dsi = encoder_to_dsi(encoder); + slave = dsi->slave; + + if (slave->mode_flags & MIPI_DSI_MODE_VIDEO) + dpu->ctx.if_type = SPRD_DPU_IF_DPI; + else + dpu->ctx.if_type = SPRD_DPU_IF_EDPI; + } + sprd_dpi_init(dpu); } diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c index 549b71278..68cf0933c 100644 --- a/drivers/gpu/drm/sprd/sprd_drm.c +++ b/drivers/gpu/drm/sprd/sprd_drm.c @@ -183,6 +183,7 @@ static struct platform_driver sprd_drm_driver = { static struct platform_driver *sprd_drm_drivers[] = { &sprd_drm_driver, &sprd_dpu_driver, + &sprd_dsi_driver, }; static int __init sprd_drm_init(void) diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h index 85d4a8b9f..95d1b972f 100644 --- a/drivers/gpu/drm/sprd/sprd_drm.h +++ b/drivers/gpu/drm/sprd/sprd_drm.h @@ -14,5 +14,6 @@ struct sprd_drm { }; extern struct platform_driver sprd_dpu_driver; +extern struct platform_driver sprd_dsi_driver; #endif /* _SPRD_DRM_H_ */ diff --git a/drivers/gpu/drm/sprd/sprd_dsi.c b/drivers/gpu/drm/sprd/sprd_dsi.c new file mode 100644 index 000000000..5ce98fe90 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dsi.c @@ -0,0 +1,1260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#include +#include +#include +#include +#include +#include +#include