| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: John Stultz <john.stultz@linaro.org> |
| Date: Thu, 10 Jan 2019 22:59:16 +0800 |
| Subject: ANDROID: drm: kirin: Introduce kirin960 |
| |
| Add initial kirin960 support files. |
| |
| Bug: 146450171 |
| Signed-off-by: Xu YiPing <xuyiping@hisilicon.com> |
| [jstultz: fold in some other changes, this patch really needs to be |
| split up into some cleaner steps] |
| Signed-off-by: John Stultz <john.stultz@linaro.org> |
| Change-Id: I934004f4a15e1bba341807f6c63c13bf6c661698 |
| --- |
| drivers/gpu/drm/hisilicon/Makefile | 0 |
| drivers/gpu/drm/hisilicon/kirin/Kconfig | 32 +- |
| drivers/gpu/drm/hisilicon/kirin/Makefile | 15 +- |
| drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 46 +- |
| .../hisilicon/kirin/{ => kirin}/dw_drm_dsi.c | 268 +--- |
| .../drm/hisilicon/kirin/kirin960/dw_drm_dsi.c | 1221 +++++++++++++++++ |
| .../gpu/drm/hisilicon/kirin/kirin_ade_reg.h | 4 +- |
| .../gpu/drm/hisilicon/kirin/kirin_dpe_reg.h | 393 ++++++ |
| .../gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 0 |
| .../gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 2 + |
| .../gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 2 + |
| .../gpu/drm/hisilicon/kirin/kirin_drm_dsi.c | 444 ++++++ |
| .../gpu/drm/hisilicon/kirin/kirin_drm_dsi.h | 259 ++++ |
| 13 files changed, 2421 insertions(+), 265 deletions(-) |
| mode change 100644 => 100755 drivers/gpu/drm/hisilicon/Makefile |
| mode change 100644 => 100755 drivers/gpu/drm/hisilicon/kirin/Makefile |
| rename drivers/gpu/drm/hisilicon/kirin/{ => kirin}/dw_drm_dsi.c (74%) |
| create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin960/dw_drm_dsi.c |
| mode change 100644 => 100755 drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h |
| create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_dpe_reg.h |
| mode change 100644 => 100755 drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c |
| mode change 100644 => 100755 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h |
| create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.c |
| create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.h |
| |
| diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile |
| old mode 100644 |
| new mode 100755 |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig |
| index 290553e2f6b4..0f2a29e84296 100644 |
| --- a/drivers/gpu/drm/hisilicon/kirin/Kconfig |
| +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig |
| @@ -6,7 +6,37 @@ config DRM_HISI_KIRIN |
| select DRM_GEM_CMA_HELPER |
| select DRM_KMS_CMA_HELPER |
| select DRM_MIPI_DSI |
| + select DRM_PANEL |
| help |
| - Choose this option if you have a hisilicon Kirin chipsets(hi6220). |
| + Choose this option if you have a hisilicon Kirin chipsets. |
| If M is selected the module will be called kirin-drm. |
| |
| +if DRM_HISI_KIRIN |
| +config DRM_HISI_KIRIN620 |
| + bool "DRM Support for Hisilicon Kirin620 Platform" |
| + default n |
| + depends on DRM_HISI_KIRIN |
| + select DRM_KMS_HELPER |
| + select DRM_GEM_CMA_HELPER |
| + select DRM_KMS_CMA_HELPER |
| + select DRM_MIPI_DSI |
| + select DRM_PANEL |
| + help |
| + Choose this option if you have hisilicon Kirin Chipset(kirin620). |
| + It includes kirin620 ade and dsi drivers. |
| + If y is to build kirin620 drm into kirin drm drivers. |
| + |
| +config DRM_HISI_KIRIN960 |
| + bool "DRM Support for Hisilicon Kirin960 Platform" |
| + default n |
| + depends on DRM_HISI_KIRIN |
| + select DRM_KMS_HELPER |
| + select DRM_GEM_CMA_HELPER |
| + select DRM_KMS_CMA_HELPER |
| + select DRM_MIPI_DSI |
| + select DRM_PANEL |
| + help |
| + Choose this option if you have hisilicon Kirin Chipset(kirin960), such |
| + as hikey board. It includes kirin620 dpe and dsi drivers. |
| + If y is to build kirin960 drm into kirin drm drivers. |
| +endif |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile |
| old mode 100644 |
| new mode 100755 |
| index d9323f66a7d4..009f5e05f97a |
| --- a/drivers/gpu/drm/hisilicon/kirin/Makefile |
| +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile |
| @@ -1,6 +1,15 @@ |
| # SPDX-License-Identifier: GPL-2.0-only |
| -kirin-drm-y := kirin_drm_drv.o \ |
| - kirin_drm_ade.o |
| +EXTRA_CFLAGS += \ |
| + -Iinclude/drm |
| |
| -obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o dw_drm_dsi.o |
| +kirin-drm-y := kirin_drm_drv.o \ |
| + kirin_drm_dsi.o \ |
| |
| +kirin-drm-$(CONFIG_DRM_HISI_KIRIN620) += \ |
| + kirin_drm_ade.o \ |
| + kirin/dw_drm_dsi.o |
| + |
| +kirin-drm-$(CONFIG_DRM_HISI_KIRIN960) += \ |
| + kirin960/dw_drm_dsi.o \ |
| + |
| +obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h |
| index 19e81ff64fac..6c31ee0b487f 100644 |
| --- a/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h |
| +++ b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h |
| @@ -8,7 +8,7 @@ |
| #define __DW_DSI_REG_H__ |
| |
| #define MASK(x) (BIT(x) - 1) |
| - |
| +#define DEFAULT_MAX_TX_ESC_CLK (10 * 1000000UL) //for hikey960 |
| /* |
| * regs |
| */ |
| @@ -52,6 +52,50 @@ |
| #define VID_VACTIVE_LINES 0x60 /* Vertical resolution */ |
| #define VID_PKT_SIZE 0x3C /* Video packet size */ |
| #define VID_MODE_CFG 0x38 /* Video mode configuration */ |
| +/***************************for hikey960***********************************/ |
| +#define GEN_HDR 0x6c |
| +#define GEN_HDATA(data) (((data) & 0xffff) << 8) |
| +#define GEN_HDATA_MASK (0xffff << 8) |
| +#define GEN_HTYPE(type) (((type) & 0xff) << 0) |
| +#define GEN_HTYPE_MASK 0xff |
| +#define GEN_PLD_DATA 0x70 |
| +#define CMD_PKT_STATUS 0x74 |
| +#define GEN_CMD_EMPTY BIT(0) |
| +#define GEN_CMD_FULL BIT(1) |
| +#define GEN_PLD_W_EMPTY BIT(2) |
| +#define GEN_PLD_W_FULL BIT(3) |
| +#define GEN_PLD_R_EMPTY BIT(4) |
| +#define GEN_PLD_R_FULL BIT(5) |
| +#define GEN_RD_CMD_BUSY BIT(6) |
| +#define CMD_MODE_CFG 0x68 |
| +#define MAX_RD_PKT_SIZE_LP BIT(24) |
| +#define DCS_LW_TX_LP BIT(19) |
| +#define DCS_SR_0P_TX_LP BIT(18) |
| +#define DCS_SW_1P_TX_LP BIT(17) |
| +#define DCS_SW_0P_TX_LP BIT(16) |
| +#define GEN_LW_TX_LP BIT(14) |
| +#define GEN_SR_2P_TX_LP BIT(13) |
| +#define GEN_SR_1P_TX_LP BIT(12) |
| +#define GEN_SR_0P_TX_LP BIT(11) |
| +#define GEN_SW_2P_TX_LP BIT(10) |
| +#define GEN_SW_1P_TX_LP BIT(9) |
| +#define GEN_SW_0P_TX_LP BIT(8) |
| +#define EN_ACK_RQST BIT(1) |
| +#define EN_TEAR_FX BIT(0) |
| +#define CMD_PKT_STATUS_TIMEOUT_US 20000 |
| +#define CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \ |
| + DCS_LW_TX_LP | \ |
| + DCS_SR_0P_TX_LP | \ |
| + DCS_SW_1P_TX_LP | \ |
| + DCS_SW_0P_TX_LP | \ |
| + GEN_LW_TX_LP | \ |
| + GEN_SR_2P_TX_LP | \ |
| + GEN_SR_1P_TX_LP | \ |
| + GEN_SR_0P_TX_LP | \ |
| + GEN_SW_2P_TX_LP | \ |
| + GEN_SW_1P_TX_LP | \ |
| + GEN_SW_0P_TX_LP) |
| +/***************************for hikey960***********************************/ |
| #define PHY_TMR_CFG 0x9C /* Data lanes timing configuration */ |
| #define BTA_TO_CNT 0x8C /* Response timeout definition */ |
| #define PHY_TMR_LPCLK_CFG 0x98 /* clock lane timing configuration */ |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/kirin/dw_drm_dsi.c |
| similarity index 74% |
| rename from drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c |
| rename to drivers/gpu/drm/hisilicon/kirin/kirin/dw_drm_dsi.c |
| index 5bf8138941de..100f502f1b17 100644 |
| --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin/dw_drm_dsi.c |
| @@ -9,6 +9,7 @@ |
| * Xinliang Liu <z.liuxinliang@hisilicon.com> |
| * Xinliang Liu <xinliang.liu@linaro.org> |
| * Xinwei Kong <kong.kongxinwei@hisilicon.com> |
| + * Da Lv <lvda3@hisilicon.com> |
| */ |
| |
| #include <linux/clk.h> |
| @@ -25,97 +26,10 @@ |
| #include <drm/drm_print.h> |
| #include <drm/drm_probe_helper.h> |
| |
| -#include "dw_dsi_reg.h" |
| +#include "../kirin_drm_dsi.h" |
| +#include "../dw_dsi_reg.h" |
| |
| #define MAX_TX_ESC_CLK 10 |
| -#define ROUND(x, y) ((x) / (y) + \ |
| - ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) |
| -#define PHY_REF_CLK_RATE 19200000 |
| -#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000)) |
| - |
| -#define encoder_to_dsi(encoder) \ |
| - container_of(encoder, struct dw_dsi, encoder) |
| -#define host_to_dsi(host) \ |
| - container_of(host, struct dw_dsi, host) |
| - |
| -struct mipi_phy_params { |
| - u32 clk_t_lpx; |
| - u32 clk_t_hs_prepare; |
| - u32 clk_t_hs_zero; |
| - u32 clk_t_hs_trial; |
| - u32 clk_t_wakeup; |
| - u32 data_t_lpx; |
| - u32 data_t_hs_prepare; |
| - u32 data_t_hs_zero; |
| - u32 data_t_hs_trial; |
| - u32 data_t_ta_go; |
| - u32 data_t_ta_get; |
| - u32 data_t_wakeup; |
| - u32 hstx_ckg_sel; |
| - u32 pll_fbd_div5f; |
| - u32 pll_fbd_div1f; |
| - u32 pll_fbd_2p; |
| - u32 pll_enbwt; |
| - u32 pll_fbd_p; |
| - u32 pll_fbd_s; |
| - u32 pll_pre_div1p; |
| - u32 pll_pre_p; |
| - u32 pll_vco_750M; |
| - u32 pll_lpf_rs; |
| - u32 pll_lpf_cs; |
| - u32 clklp2hs_time; |
| - u32 clkhs2lp_time; |
| - u32 lp2hs_time; |
| - u32 hs2lp_time; |
| - u32 clk_to_data_delay; |
| - u32 data_to_clk_delay; |
| - u32 lane_byte_clk_kHz; |
| - u32 clk_division; |
| -}; |
| - |
| -struct dsi_hw_ctx { |
| - void __iomem *base; |
| - struct clk *pclk; |
| -}; |
| - |
| -struct dw_dsi { |
| - struct drm_encoder encoder; |
| - struct drm_bridge *bridge; |
| - struct mipi_dsi_host host; |
| - struct drm_display_mode cur_mode; |
| - struct dsi_hw_ctx *ctx; |
| - struct mipi_phy_params phy; |
| - |
| - u32 lanes; |
| - enum mipi_dsi_pixel_format format; |
| - unsigned long mode_flags; |
| - bool enable; |
| -}; |
| - |
| -struct dsi_data { |
| - struct dw_dsi dsi; |
| - struct dsi_hw_ctx ctx; |
| -}; |
| - |
| -struct dsi_phy_range { |
| - u32 min_range_kHz; |
| - u32 max_range_kHz; |
| - u32 pll_vco_750M; |
| - u32 hstx_ckg_sel; |
| -}; |
| - |
| -static const struct dsi_phy_range dphy_range_info[] = { |
| - { 46875, 62500, 1, 7 }, |
| - { 62500, 93750, 0, 7 }, |
| - { 93750, 125000, 1, 6 }, |
| - { 125000, 187500, 0, 6 }, |
| - { 187500, 250000, 1, 5 }, |
| - { 250000, 375000, 0, 5 }, |
| - { 375000, 500000, 1, 4 }, |
| - { 500000, 750000, 0, 4 }, |
| - { 750000, 1000000, 1, 0 }, |
| - { 1000000, 1500000, 0, 0 } |
| -}; |
| |
| static u32 dsi_calc_phy_rate(u32 req_kHz, struct mipi_phy_params *phy) |
| { |
| @@ -567,24 +481,7 @@ static void dsi_mipi_init(struct dw_dsi *dsi) |
| dsi->lanes, mode->clock, phy->lane_byte_clk_kHz); |
| } |
| |
| -static void dsi_encoder_disable(struct drm_encoder *encoder) |
| -{ |
| - struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| - struct dsi_hw_ctx *ctx = dsi->ctx; |
| - void __iomem *base = ctx->base; |
| - |
| - if (!dsi->enable) |
| - return; |
| - |
| - writel(0, base + PWR_UP); |
| - writel(0, base + LPCLK_CTRL); |
| - writel(0, base + PHY_RSTZ); |
| - clk_disable_unprepare(ctx->pclk); |
| - |
| - dsi->enable = false; |
| -} |
| - |
| -static void dsi_encoder_enable(struct drm_encoder *encoder) |
| +static void dsi_encoder_enable_sub(struct drm_encoder *encoder) |
| { |
| struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| struct dsi_hw_ctx *ctx = dsi->ctx; |
| @@ -600,8 +497,6 @@ static void dsi_encoder_enable(struct drm_encoder *encoder) |
| } |
| |
| dsi_mipi_init(dsi); |
| - |
| - dsi->enable = true; |
| } |
| |
| static enum drm_mode_status dsi_encoder_phy_mode_valid( |
| @@ -670,60 +565,6 @@ static enum drm_mode_status dsi_encoder_mode_valid(struct drm_encoder *encoder, |
| return MODE_OK; |
| } |
| |
| -static void dsi_encoder_mode_set(struct drm_encoder *encoder, |
| - struct drm_display_mode *mode, |
| - struct drm_display_mode *adj_mode) |
| -{ |
| - struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| - |
| - drm_mode_copy(&dsi->cur_mode, adj_mode); |
| -} |
| - |
| -static int dsi_encoder_atomic_check(struct drm_encoder *encoder, |
| - struct drm_crtc_state *crtc_state, |
| - struct drm_connector_state *conn_state) |
| -{ |
| - /* do nothing */ |
| - return 0; |
| -} |
| - |
| -static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = { |
| - .atomic_check = dsi_encoder_atomic_check, |
| - .mode_valid = dsi_encoder_mode_valid, |
| - .mode_set = dsi_encoder_mode_set, |
| - .enable = dsi_encoder_enable, |
| - .disable = dsi_encoder_disable |
| -}; |
| - |
| -static const struct drm_encoder_funcs dw_encoder_funcs = { |
| - .destroy = drm_encoder_cleanup, |
| -}; |
| - |
| -static int dw_drm_encoder_init(struct device *dev, |
| - struct drm_device *drm_dev, |
| - struct drm_encoder *encoder) |
| -{ |
| - int ret; |
| - u32 crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node); |
| - |
| - if (!crtc_mask) { |
| - DRM_ERROR("failed to find crtc mask\n"); |
| - return -EINVAL; |
| - } |
| - |
| - encoder->possible_crtcs = crtc_mask; |
| - ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs, |
| - DRM_MODE_ENCODER_DSI, NULL); |
| - if (ret) { |
| - DRM_ERROR("failed to init dsi encoder\n"); |
| - return ret; |
| - } |
| - |
| - drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs); |
| - |
| - return 0; |
| -} |
| - |
| static int dsi_host_attach(struct mipi_dsi_host *host, |
| struct mipi_dsi_device *mdsi) |
| { |
| @@ -769,54 +610,6 @@ static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) |
| return 0; |
| } |
| |
| -static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) |
| -{ |
| - struct drm_encoder *encoder = &dsi->encoder; |
| - struct drm_bridge *bridge = dsi->bridge; |
| - int ret; |
| - |
| - /* associate the bridge to dsi encoder */ |
| - ret = drm_bridge_attach(encoder, bridge, NULL); |
| - if (ret) { |
| - DRM_ERROR("failed to attach external bridge\n"); |
| - return ret; |
| - } |
| - |
| - return 0; |
| -} |
| - |
| -static int dsi_bind(struct device *dev, struct device *master, void *data) |
| -{ |
| - struct dsi_data *ddata = dev_get_drvdata(dev); |
| - struct dw_dsi *dsi = &ddata->dsi; |
| - struct drm_device *drm_dev = data; |
| - int ret; |
| - |
| - ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder); |
| - if (ret) |
| - return ret; |
| - |
| - ret = dsi_host_init(dev, dsi); |
| - if (ret) |
| - return ret; |
| - |
| - ret = dsi_bridge_init(drm_dev, dsi); |
| - if (ret) |
| - return ret; |
| - |
| - return 0; |
| -} |
| - |
| -static void dsi_unbind(struct device *dev, struct device *master, void *data) |
| -{ |
| - /* do nothing */ |
| -} |
| - |
| -static const struct component_ops dsi_ops = { |
| - .bind = dsi_bind, |
| - .unbind = dsi_unbind, |
| -}; |
| - |
| static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) |
| { |
| struct dsi_hw_ctx *ctx = dsi->ctx; |
| @@ -848,55 +641,14 @@ static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) |
| return 0; |
| } |
| |
| -static int dsi_probe(struct platform_device *pdev) |
| -{ |
| - struct dsi_data *data; |
| - struct dw_dsi *dsi; |
| - struct dsi_hw_ctx *ctx; |
| - int ret; |
| - |
| - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
| - if (!data) { |
| - DRM_ERROR("failed to allocate dsi data.\n"); |
| - return -ENOMEM; |
| - } |
| - dsi = &data->dsi; |
| - ctx = &data->ctx; |
| - dsi->ctx = ctx; |
| - |
| - ret = dsi_parse_dt(pdev, dsi); |
| - if (ret) |
| - return ret; |
| - |
| - platform_set_drvdata(pdev, data); |
| - |
| - return component_add(&pdev->dev, &dsi_ops); |
| -} |
| - |
| -static int dsi_remove(struct platform_device *pdev) |
| -{ |
| - component_del(&pdev->dev, &dsi_ops); |
| - |
| - return 0; |
| -} |
| - |
| -static const struct of_device_id dsi_of_match[] = { |
| - {.compatible = "hisilicon,hi6220-dsi"}, |
| - { } |
| -}; |
| -MODULE_DEVICE_TABLE(of, dsi_of_match); |
| - |
| -static struct platform_driver dsi_driver = { |
| - .probe = dsi_probe, |
| - .remove = dsi_remove, |
| - .driver = { |
| - .name = "dw-dsi", |
| - .of_match_table = dsi_of_match, |
| - }, |
| +const struct kirin_dsi_ops kirin_dsi_620 = { |
| + .version = KIRIN620_DSI, |
| + .parse_dt = dsi_parse_dt, |
| + .host_init = dsi_host_init, |
| + .encoder_enable = dsi_encoder_enable_sub, |
| + .encoder_valid = dsi_encoder_mode_valid |
| }; |
| |
| -module_platform_driver(dsi_driver); |
| - |
| MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>"); |
| MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>"); |
| MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>"); |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin960/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/kirin960/dw_drm_dsi.c |
| new file mode 100644 |
| index 000000000000..fa7739234b33 |
| --- /dev/null |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin960/dw_drm_dsi.c |
| @@ -0,0 +1,1221 @@ |
| +/* |
| + * DesignWare MIPI DSI Host Controller v1.02 driver |
| + * |
| + * Copyright (c) 2016 Linaro Limited. |
| + * Copyright (c) 2014-2016 Hisilicon Limited. |
| + * |
| + * Author: |
| + * <shizongxuan@huawei.com> |
| + * <zhangxiubin@huawei.com> |
| + * <lvda3@hisilicon.com> |
| + * |
| + * This program is free software; you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 2 as |
| + * published by the Free Software Foundation. |
| + * |
| + */ |
| + |
| +#include <linux/clk.h> |
| +#include <linux/module.h> |
| +#include <linux/component.h> |
| +#include <linux/of_graph.h> |
| +#include <linux/iopoll.h> |
| +#include <video/mipi_display.h> |
| +#include <linux/gpio/consumer.h> |
| +#include <linux/of_address.h> |
| + |
| +#include <drm/drm_of.h> |
| +#include <drm/drm_crtc_helper.h> |
| +#include <drm/drm_mipi_dsi.h> |
| +#include <drm/drm_encoder_slave.h> |
| +#include <drm/drm_atomic_helper.h> |
| +#include <drm/drm_panel.h> |
| +#include <drm/drm_device.h> |
| +#include <drm/drm_sysfs.h> |
| +#include <drm/drm_print.h> |
| +#include <drm/drm_probe_helper.h> |
| + |
| +#include "../kirin_drm_dsi.h" |
| +#include "../dw_dsi_reg.h" |
| +#include "../kirin_dpe_reg.h" |
| + |
| +#define DTS_COMP_DSI_NAME "hisilicon,hi3660-dsi" |
| +#define ROUND1(x, y) ((x) / (y) + ((x) % (y) ? 1 : 0)) |
| +#define DSS_REDUCE(x) ((x) > 0 ? ((x) - 1) : (x)) |
| + |
| +#define DEFAULT_MIPI_CLK_RATE (192 * 100000L) |
| +#define DEFAULT_PCLK_DSI_RATE (120 * 1000000L) |
| + |
| +#define outp32(addr, val) writel(val, addr) |
| +#define inp32(addr) readl(addr) |
| + |
| +struct dss_rect { |
| + s32 x; |
| + s32 y; |
| + s32 w; |
| + s32 h; |
| +}; |
| + |
| +enum { |
| + DSI_1_LANES = 0, |
| + DSI_2_LANES, |
| + DSI_3_LANES, |
| + DSI_4_LANES, |
| +}; |
| + |
| +static void set_reg(char __iomem *addr, uint32_t val, uint8_t bw, uint8_t bs) |
| +{ |
| + u32 mask = (1UL << bw) - 1UL; |
| + u32 tmp = 0; |
| + |
| + tmp = inp32(addr); |
| + tmp &= ~(mask << bs); |
| + |
| + outp32(addr, tmp | ((val & mask) << bs)); |
| +} |
| + |
| +static enum drm_mode_status dsi_encoder_phy_mode_valid( |
| + struct drm_encoder *encoder, |
| + const struct drm_display_mode *mode) |
| +{ |
| + /* XXX HACK whitelist for now, to move it out of |
| + * common adv7511 code. This should be replaced by |
| + * something closer to dsi_encoder_phy_mode_valid() |
| + * found in in: |
| + * drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c |
| + */ |
| + DRM_DEBUG_DRIVER("Checking mode %ix%i@%i clock: %i...", |
| + mode->hdisplay, mode->vdisplay, |
| + drm_mode_vrefresh(mode), mode->clock); |
| + if ((mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 148500) || |
| + (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 80192) || |
| + (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 74250) || |
| + (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 61855) || |
| + (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 147116) || |
| + (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 146250) || |
| + (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 144589) || |
| + (mode->hdisplay == 1600 && mode->vdisplay == 1200 && mode->clock == 160961) || |
| + (mode->hdisplay == 1600 && mode->vdisplay == 900 && mode->clock == 118963) || |
| + (mode->hdisplay == 1440 && mode->vdisplay == 900 && mode->clock == 126991) || |
| + (mode->hdisplay == 1280 && mode->vdisplay == 1024 && mode->clock == 128946) || |
| + (mode->hdisplay == 1280 && mode->vdisplay == 1024 && mode->clock == 98619) || |
| + (mode->hdisplay == 1280 && mode->vdisplay == 960 && mode->clock == 102081) || |
| + (mode->hdisplay == 1280 && mode->vdisplay == 800 && mode->clock == 83496) || |
| + (mode->hdisplay == 1280 && mode->vdisplay == 720 && mode->clock == 74440) || |
| + (mode->hdisplay == 1280 && mode->vdisplay == 720 && mode->clock == 74250) || |
| + (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 78800) || |
| + (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 75000) || |
| + (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 81833) || |
| + (mode->hdisplay == 1024 && mode->vdisplay == 600 && mode->clock == 50250) || |
| + (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 48907) || |
| + (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) || |
| + (mode->hdisplay == 800 && mode->vdisplay == 480 && mode->clock == 32000)) { |
| + DRM_DEBUG("OK\n"); |
| + return MODE_OK; |
| + } |
| + DRM_DEBUG("BAD\n"); |
| + return MODE_BAD; |
| +} |
| + |
| +static enum drm_mode_status dsi_encoder_mode_valid(struct drm_encoder *encoder, |
| + const struct drm_display_mode *mode) |
| + |
| +{ |
| + struct drm_crtc *crtc = NULL; |
| + struct drm_display_mode adj_mode; |
| + enum drm_mode_status ret; |
| + |
| + /* |
| + * The crtc might adjust the mode, so go through the |
| + * possible crtcs (technically just one) and call |
| + * mode_fixup to figure out the adjusted mode before we |
| + * validate it. |
| + */ |
| + drm_for_each_crtc(crtc, encoder->dev) { |
| + /* |
| + * reset adj_mode to the mode value each time, |
| + * so we don't adjust the mode twice |
| + */ |
| + drm_mode_copy(&adj_mode, mode); |
| + |
| + /* XXX - skip this as we're just using a whitelist |
| + crtc_funcs = crtc->helper_private; |
| + if (crtc_funcs && crtc_funcs->mode_fixup) |
| + if (!crtc_funcs->mode_fixup(crtc, mode, &adj_mode)) |
| + return MODE_BAD; |
| + */ |
| + ret = dsi_encoder_phy_mode_valid(encoder, &adj_mode); |
| + if (ret != MODE_OK) |
| + return ret; |
| + } |
| + return MODE_OK; |
| +} |
| + |
| +static void get_dsi_phy_ctrl(struct dw_dsi *dsi, |
| + struct mipi_phy_params *phy_ctrl) |
| +{ |
| + struct mipi_panel_info *mipi = NULL; |
| + struct drm_display_mode *mode = NULL; |
| + u32 dphy_req_kHz; |
| + int bpp; |
| + u32 id = 0; |
| + u32 ui = 0; |
| + u32 m_pll = 0; |
| + u32 n_pll = 0; |
| + u32 m_n_fract = 0; |
| + u32 m_n_int = 0; |
| + u64 lane_clock = 0; |
| + u64 vco_div = 1; |
| + |
| + u32 accuracy = 0; |
| + u32 unit_tx_byte_clk_hs = 0; |
| + u32 clk_post = 0; |
| + u32 clk_pre = 0; |
| + u32 clk_t_hs_exit = 0; |
| + u32 clk_pre_delay = 0; |
| + u32 clk_t_hs_prepare = 0; |
| + u32 clk_t_lpx = 0; |
| + u32 clk_t_hs_zero = 0; |
| + u32 clk_t_hs_trial = 0; |
| + u32 data_post_delay = 0; |
| + u32 data_t_hs_prepare = 0; |
| + u32 data_t_hs_zero = 0; |
| + u32 data_t_hs_trial = 0; |
| + u32 data_t_lpx = 0; |
| + u32 clk_pre_delay_reality = 0; |
| + u32 clk_t_hs_zero_reality = 0; |
| + u32 clk_post_delay_reality = 0; |
| + u32 data_t_hs_zero_reality = 0; |
| + u32 data_post_delay_reality = 0; |
| + u32 data_pre_delay_reality = 0; |
| + |
| + WARN_ON(!phy_ctrl); |
| + WARN_ON(!dsi); |
| + |
| + id = dsi->cur_client; |
| + mode = &dsi->cur_mode; |
| + mipi = &dsi->mipi; |
| + |
| + /* |
| + * count phy params |
| + */ |
| + bpp = mipi_dsi_pixel_format_to_bpp(dsi->client[id].format); |
| + if (bpp < 0) |
| + return; |
| + if (mode->clock > 80000) |
| + dsi->client[id].lanes = 4; |
| + else |
| + dsi->client[id].lanes = 3; |
| + if (dsi->client[id].phy_clock) |
| + dphy_req_kHz = dsi->client[id].phy_clock; |
| + else |
| + dphy_req_kHz = mode->clock * bpp / dsi->client[id].lanes; |
| + |
| + lane_clock = dphy_req_kHz / 1000; |
| + DRM_INFO("Expected : lane_clock = %llu M\n", lane_clock); |
| + |
| + /************************ PLL parameters config *********************/ |
| + /*chip spec : |
| + If the output data rate is below 320 Mbps, |
| + RG_BNAD_SEL should be set to 1. |
| + At this mode a post divider of 1/4 will be applied to VCO. |
| + */ |
| + if ((320 <= lane_clock) && (lane_clock <= 2500)) { |
| + phy_ctrl->rg_band_sel = 0; /*0x1E[2]*/ |
| + vco_div = 1; |
| + } else if ((80 <= lane_clock) && (lane_clock < 320)) { |
| + phy_ctrl->rg_band_sel = 1; |
| + vco_div = 4; |
| + } else { |
| + DRM_ERROR("80M <= lane_clock< = 2500M, not support lane_clock = %llu M\n", |
| + lane_clock); |
| + } |
| + |
| + m_n_int = lane_clock * vco_div * 1000000UL / DEFAULT_MIPI_CLK_RATE; |
| + m_n_fract = ((lane_clock * vco_div * 1000000UL * 1000UL / DEFAULT_MIPI_CLK_RATE) % 1000) * 10 / 1000; |
| + |
| + if (m_n_int % 2 == 0) { |
| + if (m_n_fract * 6 >= 50) { |
| + n_pll = 2; |
| + m_pll = (m_n_int + 1) * n_pll; |
| + } else if (m_n_fract * 6 >= 30) { |
| + n_pll = 3; |
| + m_pll = m_n_int * n_pll + 2; |
| + } else { |
| + n_pll = 1; |
| + m_pll = m_n_int * n_pll; |
| + } |
| + } else { |
| + if (m_n_fract * 6 >= 50) { |
| + n_pll = 1; |
| + m_pll = (m_n_int + 1) * n_pll; |
| + } else if (m_n_fract * 6 >= 30) { |
| + n_pll = 1; |
| + m_pll = (m_n_int + 1) * n_pll; |
| + } else if (m_n_fract * 6 >= 10) { |
| + n_pll = 3; |
| + m_pll = m_n_int * n_pll + 1; |
| + } else { |
| + n_pll = 2; |
| + m_pll = m_n_int * n_pll; |
| + } |
| + } |
| + |
| + /*if set rg_pll_enswc=1, pll_fbd_s can't be 0*/ |
| + if (m_pll <= 8) { |
| + phy_ctrl->pll_fbd_s = 1; |
| + phy_ctrl->rg_pll_enswc = 0; |
| + |
| + if (m_pll % 2 == 0) { |
| + phy_ctrl->pll_fbd_p = m_pll / 2; |
| + } else { |
| + if (n_pll == 1) { |
| + n_pll *= 2; |
| + phy_ctrl->pll_fbd_p = (m_pll * 2) / 2; |
| + } else { |
| + DRM_ERROR("phy m_pll not support!m_pll = %d\n", m_pll); |
| + return; |
| + } |
| + } |
| + } else if (m_pll <= 300) { |
| + if (m_pll % 2 == 0) |
| + phy_ctrl->rg_pll_enswc = 0; |
| + else |
| + phy_ctrl->rg_pll_enswc = 1; |
| + |
| + phy_ctrl->pll_fbd_s = 1; |
| + phy_ctrl->pll_fbd_p = m_pll / 2; |
| + } else if (m_pll <= 315) { |
| + phy_ctrl->pll_fbd_p = 150; |
| + phy_ctrl->pll_fbd_s = m_pll - 2 * phy_ctrl->pll_fbd_p; |
| + phy_ctrl->rg_pll_enswc = 1; |
| + } else { |
| + DRM_ERROR("phy m_pll not support!m_pll = %d\n", m_pll); |
| + return; |
| + } |
| + |
| + phy_ctrl->pll_pre_p = n_pll; |
| + |
| + lane_clock = m_pll * (DEFAULT_MIPI_CLK_RATE / n_pll) / vco_div; |
| + DRM_INFO("Config : lane_clock = %llu\n", lane_clock); |
| + |
| + /*FIXME :*/ |
| + phy_ctrl->rg_pll_cp = 1; /*0x16[7:5]*/ |
| + phy_ctrl->rg_pll_cp_p = 3; /*0x1E[7:5]*/ |
| + |
| + /*test_code_0x14 other parameters config*/ |
| + phy_ctrl->pll_enbwt = 0; /*0x14[2]*/ |
| + phy_ctrl->rg_pll_chp = 0; /*0x14[1:0]*/ |
| + |
| + /*test_code_0x16 other parameters config, 0x16[3:2] reserved*/ |
| + phy_ctrl->pll_lpf_cs = 0; /*0x16[4]*/ |
| + phy_ctrl->rg_pll_refsel = 1; /*0x16[1:0]*/ |
| + |
| + /*test_code_0x1E other parameters config*/ |
| + phy_ctrl->reload_sel = 1; /*0x1E[4]*/ |
| + phy_ctrl->rg_phase_gen_en = 1; /*0x1E[3]*/ |
| + phy_ctrl->pll_power_down = 0; /*0x1E[1]*/ |
| + phy_ctrl->pll_register_override = 1; /*0x1E[0]*/ |
| + |
| + /*HSTX select VCM VREF*/ |
| + phy_ctrl->rg_vrefsel_vcm = 0x55; |
| + if (mipi->rg_vrefsel_vcm_clk_adjust != 0) |
| + phy_ctrl->rg_vrefsel_vcm = (phy_ctrl->rg_vrefsel_vcm & 0x0F) | |
| + ((mipi->rg_vrefsel_vcm_clk_adjust & 0x0F) << 4); |
| + |
| + if (mipi->rg_vrefsel_vcm_data_adjust != 0) |
| + phy_ctrl->rg_vrefsel_vcm = (phy_ctrl->rg_vrefsel_vcm & 0xF0) | |
| + (mipi->rg_vrefsel_vcm_data_adjust & 0x0F); |
| + |
| + /*if reload_sel = 1, need to set load_command*/ |
| + phy_ctrl->load_command = 0x5A; |
| + |
| + /******************** clock/data lane parameters config ******************/ |
| + accuracy = 10; |
| + ui = 10 * 1000000000UL * accuracy / lane_clock; |
| + /*unit of measurement*/ |
| + unit_tx_byte_clk_hs = 8 * ui; |
| + |
| + /* D-PHY Specification : 60ns + 52*UI <= clk_post*/ |
| + clk_post = 600 * accuracy + 52 * ui + mipi->clk_post_adjust * ui; |
| + |
| + /* D-PHY Specification : clk_pre >= 8*UI*/ |
| + clk_pre = 8 * ui + mipi->clk_pre_adjust * ui; |
| + |
| + /* D-PHY Specification : clk_t_hs_exit >= 100ns*/ |
| + clk_t_hs_exit = 1000 * accuracy + mipi->clk_t_hs_exit_adjust * ui; |
| + |
| + /* clocked by TXBYTECLKHS*/ |
| + clk_pre_delay = 0 + mipi->clk_pre_delay_adjust * ui; |
| + |
| + /* D-PHY Specification : clk_t_hs_trial >= 60ns*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + clk_t_hs_trial = 600 * accuracy + 3 * unit_tx_byte_clk_hs + mipi->clk_t_hs_trial_adjust * ui; |
| + |
| + /* D-PHY Specification : 38ns <= clk_t_hs_prepare <= 95ns*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + if (mipi->clk_t_hs_prepare_adjust == 0) |
| + mipi->clk_t_hs_prepare_adjust = 43; |
| + |
| + clk_t_hs_prepare = ((380 * accuracy + mipi->clk_t_hs_prepare_adjust * ui) <= (950 * accuracy - 8 * ui)) ? |
| + (380 * accuracy + mipi->clk_t_hs_prepare_adjust * ui) : (950 * accuracy - 8 * ui); |
| + |
| + /* clocked by TXBYTECLKHS*/ |
| + data_post_delay = 0 + mipi->data_post_delay_adjust * ui; |
| + |
| + /* D-PHY Specification : data_t_hs_trial >= max( n*8*UI, 60ns + n*4*UI ), n = 1*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + data_t_hs_trial = ((600 * accuracy + 4 * ui) >= (8 * ui) ? (600 * accuracy + 4 * ui) : (8 * ui)) + 8 * ui + |
| + 3 * unit_tx_byte_clk_hs + mipi->data_t_hs_trial_adjust * ui; |
| + |
| + /* D-PHY Specification : 40ns + 4*UI <= data_t_hs_prepare <= 85ns + 6*UI*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + if (mipi->data_t_hs_prepare_adjust == 0) |
| + mipi->data_t_hs_prepare_adjust = 35; |
| + |
| + data_t_hs_prepare = ((400 * accuracy + 4 * ui + mipi->data_t_hs_prepare_adjust * ui) <= (850 * accuracy + 6 * ui - 8 * ui)) ? |
| + (400 * accuracy + 4 * ui + mipi->data_t_hs_prepare_adjust * ui) : (850 * accuracy + 6 * ui - 8 * ui); |
| + |
| + /* D-PHY chip spec : clk_t_lpx + clk_t_hs_prepare > 200ns*/ |
| + /* D-PHY Specification : clk_t_lpx >= 50ns*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + clk_t_lpx = (((2000 * accuracy - clk_t_hs_prepare) >= 500 * accuracy) ? |
| + ((2000 * accuracy - clk_t_hs_prepare)) : (500 * accuracy)) + |
| + mipi->clk_t_lpx_adjust * ui; |
| + |
| + /* D-PHY Specification : clk_t_hs_zero + clk_t_hs_prepare >= 300 ns*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + clk_t_hs_zero = 3000 * accuracy - clk_t_hs_prepare + 3 * unit_tx_byte_clk_hs + mipi->clk_t_hs_zero_adjust * ui; |
| + |
| + /* D-PHY chip spec : data_t_lpx + data_t_hs_prepare > 200ns*/ |
| + /* D-PHY Specification : data_t_lpx >= 50ns*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + data_t_lpx = clk_t_lpx + mipi->data_t_lpx_adjust * ui; /*2000 * accuracy - data_t_hs_prepare;*/ |
| + |
| + /* D-PHY Specification : data_t_hs_zero + data_t_hs_prepare >= 145ns + 10*UI*/ |
| + /* clocked by TXBYTECLKHS*/ |
| + data_t_hs_zero = 1450 * accuracy + 10 * ui - data_t_hs_prepare + |
| + 3 * unit_tx_byte_clk_hs + mipi->data_t_hs_zero_adjust * ui; |
| + |
| + phy_ctrl->clk_pre_delay = ROUND1(clk_pre_delay, unit_tx_byte_clk_hs); |
| + phy_ctrl->clk_t_hs_prepare = ROUND1(clk_t_hs_prepare, unit_tx_byte_clk_hs); |
| + phy_ctrl->clk_t_lpx = ROUND1(clk_t_lpx, unit_tx_byte_clk_hs); |
| + phy_ctrl->clk_t_hs_zero = ROUND1(clk_t_hs_zero, unit_tx_byte_clk_hs); |
| + phy_ctrl->clk_t_hs_trial = ROUND1(clk_t_hs_trial, unit_tx_byte_clk_hs); |
| + |
| + phy_ctrl->data_post_delay = ROUND1(data_post_delay, unit_tx_byte_clk_hs); |
| + phy_ctrl->data_t_hs_prepare = ROUND1(data_t_hs_prepare, unit_tx_byte_clk_hs); |
| + phy_ctrl->data_t_lpx = ROUND1(data_t_lpx, unit_tx_byte_clk_hs); |
| + phy_ctrl->data_t_hs_zero = ROUND1(data_t_hs_zero, unit_tx_byte_clk_hs); |
| + phy_ctrl->data_t_hs_trial = ROUND1(data_t_hs_trial, unit_tx_byte_clk_hs); |
| + phy_ctrl->data_t_ta_go = 4; |
| + phy_ctrl->data_t_ta_get = 5; |
| + |
| + clk_pre_delay_reality = phy_ctrl->clk_pre_delay + 2; |
| + clk_t_hs_zero_reality = phy_ctrl->clk_t_hs_zero + 8; |
| + data_t_hs_zero_reality = phy_ctrl->data_t_hs_zero + 4; |
| + data_post_delay_reality = phy_ctrl->data_post_delay + 4; |
| + |
| + phy_ctrl->clk_post_delay = phy_ctrl->data_t_hs_trial + ROUND1(clk_post, unit_tx_byte_clk_hs); |
| + phy_ctrl->data_pre_delay = clk_pre_delay_reality + phy_ctrl->clk_t_lpx + |
| + phy_ctrl->clk_t_hs_prepare + clk_t_hs_zero_reality + ROUND1(clk_pre, unit_tx_byte_clk_hs) ; |
| + |
| + clk_post_delay_reality = phy_ctrl->clk_post_delay + 4; |
| + data_pre_delay_reality = phy_ctrl->data_pre_delay + 2; |
| + |
| + phy_ctrl->clk_lane_lp2hs_time = clk_pre_delay_reality + phy_ctrl->clk_t_lpx + |
| + phy_ctrl->clk_t_hs_prepare + clk_t_hs_zero_reality + 3; |
| + phy_ctrl->clk_lane_hs2lp_time = clk_post_delay_reality + phy_ctrl->clk_t_hs_trial + 3; |
| + phy_ctrl->data_lane_lp2hs_time = data_pre_delay_reality + phy_ctrl->data_t_lpx + |
| + phy_ctrl->data_t_hs_prepare + data_t_hs_zero_reality + 3; |
| + phy_ctrl->data_lane_hs2lp_time = data_post_delay_reality + phy_ctrl->data_t_hs_trial + 3; |
| + phy_ctrl->phy_stop_wait_time = clk_post_delay_reality + |
| + phy_ctrl->clk_t_hs_trial + ROUND1(clk_t_hs_exit, unit_tx_byte_clk_hs) - |
| + (data_post_delay_reality + phy_ctrl->data_t_hs_trial) + 3; |
| + |
| + phy_ctrl->lane_byte_clk = lane_clock / 8; |
| + phy_ctrl->clk_division = (((phy_ctrl->lane_byte_clk / 2) % mipi->max_tx_esc_clk) > 0) ? |
| + (phy_ctrl->lane_byte_clk / 2 / mipi->max_tx_esc_clk + 1) : |
| + (phy_ctrl->lane_byte_clk / 2 / mipi->max_tx_esc_clk); |
| + |
| + DRM_INFO("PHY clock_lane and data_lane config : \n" |
| + "rg_vrefsel_vcm=%u\n" |
| + "clk_pre_delay=%u\n" |
| + "clk_post_delay=%u\n" |
| + "clk_t_hs_prepare=%u\n" |
| + "clk_t_lpx=%u\n" |
| + "clk_t_hs_zero=%u\n" |
| + "clk_t_hs_trial=%u\n" |
| + "data_pre_delay=%u\n" |
| + "data_post_delay=%u\n" |
| + "data_t_hs_prepare=%u\n" |
| + "data_t_lpx=%u\n" |
| + "data_t_hs_zero=%u\n" |
| + "data_t_hs_trial=%u\n" |
| + "data_t_ta_go=%u\n" |
| + "data_t_ta_get=%u\n", |
| + phy_ctrl->rg_vrefsel_vcm, |
| + phy_ctrl->clk_pre_delay, |
| + phy_ctrl->clk_post_delay, |
| + phy_ctrl->clk_t_hs_prepare, |
| + phy_ctrl->clk_t_lpx, |
| + phy_ctrl->clk_t_hs_zero, |
| + phy_ctrl->clk_t_hs_trial, |
| + phy_ctrl->data_pre_delay, |
| + phy_ctrl->data_post_delay, |
| + phy_ctrl->data_t_hs_prepare, |
| + phy_ctrl->data_t_lpx, |
| + phy_ctrl->data_t_hs_zero, |
| + phy_ctrl->data_t_hs_trial, |
| + phy_ctrl->data_t_ta_go, |
| + phy_ctrl->data_t_ta_get); |
| + DRM_INFO("clk_lane_lp2hs_time=%u\n" |
| + "clk_lane_hs2lp_time=%u\n" |
| + "data_lane_lp2hs_time=%u\n" |
| + "data_lane_hs2lp_time=%u\n" |
| + "phy_stop_wait_time=%u\n", |
| + phy_ctrl->clk_lane_lp2hs_time, |
| + phy_ctrl->clk_lane_hs2lp_time, |
| + phy_ctrl->data_lane_lp2hs_time, |
| + phy_ctrl->data_lane_hs2lp_time, |
| + phy_ctrl->phy_stop_wait_time); |
| +} |
| + |
| +static void dsi_set_burst_mode(void __iomem *base, unsigned long flags) |
| +{ |
| + u32 val; |
| + u32 mode_mask = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | |
| + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; |
| + u32 non_burst_sync_pulse = MIPI_DSI_MODE_VIDEO | |
| + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; |
| + u32 non_burst_sync_event = MIPI_DSI_MODE_VIDEO; |
| + |
| + /* |
| + * choose video mode type |
| + */ |
| + if ((flags & mode_mask) == non_burst_sync_pulse) |
| + val = DSI_NON_BURST_SYNC_PULSES; |
| + else if ((flags & mode_mask) == non_burst_sync_event) |
| + val = DSI_NON_BURST_SYNC_EVENTS; |
| + else |
| + val = DSI_BURST_SYNC_PULSES_1; |
| + |
| + set_reg(base + MIPIDSI_VID_MODE_CFG_OFFSET, val, 2, 0); |
| +} |
| + |
| +/* |
| + * dsi phy reg write function |
| + */ |
| +static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) |
| +{ |
| + u32 reg_write = 0x10000 + reg; |
| + |
| + /* |
| + * latch reg first |
| + */ |
| + writel(reg_write, base + MIPIDSI_PHY_TST_CTRL1_OFFSET); |
| + writel(0x02, base + MIPIDSI_PHY_TST_CTRL0_OFFSET); |
| + writel(0x00, base + MIPIDSI_PHY_TST_CTRL0_OFFSET); |
| + |
| + /* |
| + * then latch value |
| + */ |
| + writel(val, base + MIPIDSI_PHY_TST_CTRL1_OFFSET); |
| + writel(0x02, base + MIPIDSI_PHY_TST_CTRL0_OFFSET); |
| + writel(0x00, base + MIPIDSI_PHY_TST_CTRL0_OFFSET); |
| +} |
| + |
| +static void dsi_mipi_init(struct dw_dsi *dsi, char __iomem *mipi_dsi_base) |
| +{ |
| + u32 hline_time = 0; |
| + u32 hsa_time = 0; |
| + u32 hbp_time = 0; |
| + u64 pixel_clk = 0; |
| + u32 i = 0; |
| + u32 id = 0; |
| + unsigned long dw_jiffies = 0; |
| + u32 tmp = 0; |
| + bool is_ready = false; |
| + struct mipi_panel_info *mipi = NULL; |
| + struct dss_rect rect; |
| + u32 cmp_stopstate_val = 0; |
| + u32 lanes; |
| + |
| + WARN_ON(!dsi); |
| + WARN_ON(!mipi_dsi_base); |
| + |
| + id = dsi->cur_client; |
| + mipi = &dsi->mipi; |
| + |
| + if (mipi->max_tx_esc_clk == 0) { |
| + DRM_INFO("max_tx_esc_clk is invalid!"); |
| + mipi->max_tx_esc_clk = DEFAULT_MAX_TX_ESC_CLK; |
| + } |
| + |
| + memset(&dsi->phy, 0, sizeof(struct mipi_phy_params)); |
| + get_dsi_phy_ctrl(dsi, &dsi->phy); |
| + |
| + rect.x = 0; |
| + rect.y = 0; |
| + rect.w = dsi->cur_mode.hdisplay; |
| + rect.h = dsi->cur_mode.vdisplay; |
| + lanes = dsi->client[id].lanes - 1; |
| + /***************Configure the DPHY start**************/ |
| + |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_IF_CFG_OFFSET, lanes, 2, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, dsi->phy.clk_division, 8, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, dsi->phy.clk_division, 8, 8); |
| + |
| + outp32(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x00000000); |
| + |
| + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); |
| + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000001); |
| + outp32(mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET, 0x00000000); |
| + |
| + /* physical configuration PLL I*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x14, |
| + (dsi->phy.pll_fbd_s << 4) + (dsi->phy.rg_pll_enswc << 3) + |
| + (dsi->phy.pll_enbwt << 2) + dsi->phy.rg_pll_chp); |
| + |
| + /* physical configuration PLL II, M*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x15, dsi->phy.pll_fbd_p); |
| + |
| + /* physical configuration PLL III*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x16, |
| + (dsi->phy.rg_pll_cp << 5) + (dsi->phy.pll_lpf_cs << 4) + |
| + dsi->phy.rg_pll_refsel); |
| + |
| + /* physical configuration PLL IV, N*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x17, dsi->phy.pll_pre_p); |
| + |
| + /* sets the analog characteristic of V reference in D-PHY TX*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x1D, dsi->phy.rg_vrefsel_vcm); |
| + |
| + /* MISC AFE Configuration*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x1E, |
| + (dsi->phy.rg_pll_cp_p << 5) + (dsi->phy.reload_sel << 4) + |
| + (dsi->phy.rg_phase_gen_en << 3) + (dsi->phy.rg_band_sel << 2) + |
| + (dsi->phy.pll_power_down << 1) + dsi->phy.pll_register_override); |
| + |
| + /*reload_command*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x1F, dsi->phy.load_command); |
| + |
| + /* pre_delay of clock lane request setting*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x20, DSS_REDUCE(dsi->phy.clk_pre_delay)); |
| + |
| + /* post_delay of clock lane request setting*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x21, DSS_REDUCE(dsi->phy.clk_post_delay)); |
| + |
| + /* clock lane timing ctrl - t_lpx*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x22, DSS_REDUCE(dsi->phy.clk_t_lpx)); |
| + |
| + /* clock lane timing ctrl - t_hs_prepare*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x23, DSS_REDUCE(dsi->phy.clk_t_hs_prepare)); |
| + |
| + /* clock lane timing ctrl - t_hs_zero*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x24, DSS_REDUCE(dsi->phy.clk_t_hs_zero)); |
| + |
| + /* clock lane timing ctrl - t_hs_trial*/ |
| + dsi_phy_tst_set(mipi_dsi_base, 0x25, dsi->phy.clk_t_hs_trial); |
| + |
| + for (i = 0; i <= lanes; i++) { |
| + /* data lane pre_delay*/ |
| + tmp = 0x30 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_pre_delay)); |
| + |
| + /*data lane post_delay*/ |
| + tmp = 0x31 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_post_delay)); |
| + |
| + /* data lane timing ctrl - t_lpx*/ |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_lpx)); |
| + |
| + /* data lane timing ctrl - t_hs_prepare*/ |
| + tmp = 0x33 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_hs_prepare)); |
| + |
| + /* data lane timing ctrl - t_hs_zero*/ |
| + tmp = 0x34 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_hs_zero)); |
| + |
| + /* data lane timing ctrl - t_hs_trial*/ |
| + tmp = 0x35 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_hs_trial)); |
| + |
| + /* data lane timing ctrl - t_ta_go*/ |
| + tmp = 0x36 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_ta_go)); |
| + |
| + /* data lane timing ctrl - t_ta_get*/ |
| + tmp = 0x37 + (i << 4); |
| + dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_ta_get)); |
| + } |
| + |
| + outp32(mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET, 0x00000007); |
| + |
| + is_ready = false; |
| + dw_jiffies = jiffies + HZ / 2; |
| + do { |
| + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); |
| + if ((tmp & 0x00000001) == 0x00000001) { |
| + is_ready = true; |
| + break; |
| + } |
| + } while (time_after(dw_jiffies, jiffies)); |
| + |
| + if (!is_ready) { |
| + DRM_INFO("phylock is not ready!MIPIDSI_PHY_STATUS_OFFSET=0x%x.\n", |
| + tmp); |
| + } |
| + |
| + if (lanes >= DSI_4_LANES) |
| + cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9) | BIT(11)); |
| + else if (lanes >= DSI_3_LANES) |
| + cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9)); |
| + else if (lanes >= DSI_2_LANES) |
| + cmp_stopstate_val = (BIT(4) | BIT(7)); |
| + else |
| + cmp_stopstate_val = (BIT(4)); |
| + |
| + is_ready = false; |
| + dw_jiffies = jiffies + HZ / 2; |
| + do { |
| + tmp = inp32(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET); |
| + if ((tmp & cmp_stopstate_val) == cmp_stopstate_val) { |
| + is_ready = true; |
| + break; |
| + } |
| + } while (time_after(dw_jiffies, jiffies)); |
| + |
| + if (!is_ready) { |
| + DRM_INFO("phystopstateclklane is not ready! MIPIDSI_PHY_STATUS_OFFSET=0x%x.\n", |
| + tmp); |
| + } |
| + |
| + /*************************Configure the DPHY end*************************/ |
| + |
| + /* phy_stop_wait_time*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_IF_CFG_OFFSET, dsi->phy.phy_stop_wait_time, 8, 8); |
| + |
| + /*--------------configuring the DPI packet transmission----------------*/ |
| + /* |
| + ** 2. Configure the DPI Interface: |
| + ** This defines how the DPI interface interacts with the controller. |
| + */ |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_VCID_OFFSET, mipi->vc, 2, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_COLOR_CODING_OFFSET, mipi->color_mode, 4, 0); |
| + |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, dsi->ldi.data_en_plr, 1, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, dsi->ldi.vsync_plr, 1, 1); |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, dsi->ldi.hsync_plr, 1, 2); |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, 0x0, 1, 3); |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, 0x0, 1, 4); |
| + |
| + /* |
| + ** 3. Select the Video Transmission Mode: |
| + ** This defines how the processor requires the video line to be |
| + ** transported through the DSI link. |
| + */ |
| + /* video mode: low power mode*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x3f, 6, 8); |
| + /* set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x0, 1, 14); */ |
| + |
| + /* TODO: fix blank display bug when set backlight*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_DPI_LP_CMD_TIM_OFFSET, 0x4, 8, 16); |
| + /* video mode: send read cmd by lp mode*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x1, 1, 15); |
| + |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_PKT_SIZE_OFFSET, rect.w, 14, 0); |
| + |
| + /* burst mode*/ |
| + dsi_set_burst_mode(mipi_dsi_base, dsi->client[id].mode_flags); |
| + /* for dsi read, BTA enable*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_PCKHDL_CFG_OFFSET, 0x1, 1, 2); |
| + |
| + /* |
| + ** 4. Define the DPI Horizontal timing configuration: |
| + ** |
| + ** Hsa_time = HSA*(PCLK period/Clk Lane Byte Period); |
| + ** Hbp_time = HBP*(PCLK period/Clk Lane Byte Period); |
| + ** Hline_time = (HSA+HBP+HACT+HFP)*(PCLK period/Clk Lane Byte Period); |
| + */ |
| + pixel_clk = dsi->cur_mode.clock * 1000; |
| + /*htot = dsi->cur_mode.htotal;*/ |
| + /*vtot = dsi->cur_mode.vtotal;*/ |
| + dsi->ldi.h_front_porch = dsi->cur_mode.hsync_start - dsi->cur_mode.hdisplay; |
| + dsi->ldi.h_back_porch = dsi->cur_mode.htotal - dsi->cur_mode.hsync_end; |
| + dsi->ldi.h_pulse_width = dsi->cur_mode.hsync_end - dsi->cur_mode.hsync_start; |
| + dsi->ldi.v_front_porch = dsi->cur_mode.vsync_start - dsi->cur_mode.vdisplay; |
| + dsi->ldi.v_back_porch = dsi->cur_mode.vtotal - dsi->cur_mode.vsync_end; |
| + dsi->ldi.v_pulse_width = dsi->cur_mode.vsync_end - dsi->cur_mode.vsync_start; |
| + if (dsi->ldi.v_pulse_width > 15) { |
| + DRM_DEBUG_DRIVER("vsw exceeded 15\n"); |
| + dsi->ldi.v_pulse_width = 15; |
| + } |
| + hsa_time = dsi->ldi.h_pulse_width * dsi->phy.lane_byte_clk / pixel_clk; |
| + hbp_time = dsi->ldi.h_back_porch * dsi->phy.lane_byte_clk / pixel_clk; |
| + hline_time = ROUND1((dsi->ldi.h_pulse_width + dsi->ldi.h_back_porch + |
| + rect.w + dsi->ldi.h_front_porch) * dsi->phy.lane_byte_clk, pixel_clk); |
| + |
| + DRM_INFO("hsa_time=%d, hbp_time=%d, hline_time=%d\n", |
| + hsa_time, hbp_time, hline_time); |
| + DRM_INFO("lane_byte_clk=%llu, pixel_clk=%llu\n", |
| + dsi->phy.lane_byte_clk, pixel_clk); |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_HSA_TIME_OFFSET, hsa_time, 12, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_HBP_TIME_OFFSET, hbp_time, 12, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_HLINE_TIME_OFFSET, hline_time, 15, 0); |
| + |
| + /* Define the Vertical line configuration*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_VSA_LINES_OFFSET, dsi->ldi.v_pulse_width, 10, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_VBP_LINES_OFFSET, dsi->ldi.v_back_porch, 10, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_VFP_LINES_OFFSET, dsi->ldi.v_front_porch, 10, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_VID_VACTIVE_LINES_OFFSET, rect.h, 14, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_TO_CNT_CFG_OFFSET, 0x7FF, 16, 0); |
| + |
| + /* Configure core's phy parameters*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET, dsi->phy.clk_lane_lp2hs_time, 10, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET, dsi->phy.clk_lane_hs2lp_time, 10, 16); |
| + |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_RD_CFG_OFFSET, 0x7FFF, 15, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_CFG_OFFSET, dsi->phy.data_lane_lp2hs_time, 10, 0); |
| + set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_CFG_OFFSET, dsi->phy.data_lane_hs2lp_time, 10, 16); |
| + |
| + /* Waking up Core*/ |
| + set_reg(mipi_dsi_base + MIPIDSI_PWR_UP_OFFSET, 0x1, 1, 0); |
| +} |
| + |
| +static int mipi_dsi_on_sub1(struct dw_dsi *dsi, char __iomem *mipi_dsi_base) |
| +{ |
| + WARN_ON(!mipi_dsi_base); |
| + |
| + /* mipi init */ |
| + dsi_mipi_init(dsi, mipi_dsi_base); |
| + DRM_INFO("dsi_mipi_init ok\n"); |
| + /* switch to cmd mode */ |
| + set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x1, 1, 0); |
| + /* cmd mode: low power mode */ |
| + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x7f, 7, 8); |
| + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0xf, 4, 16); |
| + set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x1, 1, 24); |
| + /* disable generate High Speed clock */ |
| + /* delete? */ |
| + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x0, 1, 0); |
| + |
| + return 0; |
| +} |
| + |
| +static int mipi_dsi_on_sub2(struct dw_dsi *dsi, char __iomem *mipi_dsi_base) |
| +{ |
| + WARN_ON(!mipi_dsi_base); |
| + |
| + /* switch to video mode */ |
| + set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x0, 1, 0); |
| + |
| + /* enable EOTP TX */ |
| + set_reg(mipi_dsi_base + MIPIDSI_PCKHDL_CFG_OFFSET, 0x1, 1, 0); |
| + |
| + /* enable generate High Speed clock, continue clock */ |
| + set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x1, 2, 0); |
| + |
| + return 0; |
| +} |
| + |
| +static void dsi_encoder_enable_sub(struct drm_encoder *encoder) |
| +{ |
| + struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| + struct dsi_hw_ctx *ctx = dsi->ctx; |
| + int ret; |
| + |
| + if (dsi->enable) |
| + return; |
| + |
| + ret = clk_prepare_enable(ctx->dss_dphy0_ref_clk); |
| + if (ret) { |
| + DRM_ERROR("fail to enable dss_dphy0_ref_clk: %d\n", ret); |
| + return; |
| + } |
| + |
| + ret = clk_prepare_enable(ctx->dss_dphy0_cfg_clk); |
| + if (ret) { |
| + DRM_ERROR("fail to enable dss_dphy0_cfg_clk: %d\n", ret); |
| + return; |
| + } |
| + |
| + ret = clk_prepare_enable(ctx->dss_pclk_dsi0_clk); |
| + if (ret) { |
| + DRM_ERROR("fail to enable dss_pclk_dsi0_clk: %d\n", ret); |
| + return; |
| + } |
| + |
| + mipi_dsi_on_sub1(dsi, ctx->base); |
| + |
| + mipi_dsi_on_sub2(dsi, ctx->base); |
| +} |
| + |
| +static int dsi_host_attach(struct mipi_dsi_host *host, |
| + struct mipi_dsi_device *mdsi) |
| +{ |
| + struct dw_dsi *dsi = host_to_dsi(host); |
| + u32 id = mdsi->channel >= 1 ? OUT_PANEL : OUT_HDMI; |
| + |
| + if (mdsi->lanes < 1 || mdsi->lanes > 4) { |
| + DRM_ERROR("dsi device params invalid\n"); |
| + return -EINVAL; |
| + } |
| + |
| + dsi->client[id].lanes = mdsi->lanes; |
| + dsi->client[id].format = mdsi->format; |
| + dsi->client[id].mode_flags = mdsi->mode_flags; |
| + dsi->client[id].phy_clock = 0;//mdsi->phy_clock; |
| + |
| + DRM_INFO("host attach, client name=[%s], id=%d\n", mdsi->name, id); |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_host_detach(struct mipi_dsi_host *host, |
| + struct mipi_dsi_device *mdsi) |
| +{ |
| + /* do nothing */ |
| + return 0; |
| +} |
| + |
| +static int dsi_gen_pkt_hdr_write(void __iomem *base, u32 val) |
| +{ |
| + u32 status; |
| + int ret; |
| + |
| + ret = readx_poll_timeout(readl, base + CMD_PKT_STATUS, status, |
| + !(status & GEN_CMD_FULL), 1000, |
| + CMD_PKT_STATUS_TIMEOUT_US); |
| + if (ret < 0) { |
| + DRM_ERROR("failed to get available command FIFO\n"); |
| + return ret; |
| + } |
| + |
| + writel(val, base + GEN_HDR); |
| + |
| + ret = readx_poll_timeout(readl, base + CMD_PKT_STATUS, status, |
| + status & (GEN_CMD_EMPTY | GEN_PLD_W_EMPTY), |
| + 1000, CMD_PKT_STATUS_TIMEOUT_US); |
| + if (ret < 0) { |
| + DRM_ERROR("failed to write command FIFO\n"); |
| + return ret; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_dcs_short_write(void __iomem *base, |
| + const struct mipi_dsi_msg *msg) |
| +{ |
| + const u16 *tx_buf = msg->tx_buf; |
| + u32 val = GEN_HDATA(*tx_buf) | GEN_HTYPE(msg->type); |
| + |
| + if (msg->tx_len > 2) { |
| + DRM_ERROR("too long tx buf length %zu for short write\n", |
| + msg->tx_len); |
| + return -EINVAL; |
| + } |
| + |
| + return dsi_gen_pkt_hdr_write(base, val); |
| +} |
| + |
| +static int dsi_dcs_long_write(void __iomem *base, |
| + const struct mipi_dsi_msg *msg) |
| +{ |
| + const u32 *tx_buf = msg->tx_buf; |
| + int len = msg->tx_len, pld_data_bytes = sizeof(*tx_buf), ret; |
| + u32 val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type); |
| + u32 remainder = 0; |
| + u32 status; |
| + |
| + if (msg->tx_len < 3) { |
| + DRM_ERROR("wrong tx buf length %zu for long write\n", |
| + msg->tx_len); |
| + return -EINVAL; |
| + } |
| + |
| + while (DIV_ROUND_UP(len, pld_data_bytes)) { |
| + if (len < pld_data_bytes) { |
| + memcpy(&remainder, tx_buf, len); |
| + writel(remainder, base + GEN_PLD_DATA); |
| + len = 0; |
| + } else { |
| + writel(*tx_buf, base + GEN_PLD_DATA); |
| + tx_buf++; |
| + len -= pld_data_bytes; |
| + } |
| + |
| + ret = readx_poll_timeout(readl, base + CMD_PKT_STATUS, |
| + status, !(status & GEN_PLD_W_FULL), 1000, |
| + CMD_PKT_STATUS_TIMEOUT_US); |
| + if (ret < 0) { |
| + DRM_ERROR("failed to get available write payload FIFO\n"); |
| + return ret; |
| + } |
| + } |
| + |
| + return dsi_gen_pkt_hdr_write(base, val); |
| +} |
| + |
| +static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, |
| + const struct mipi_dsi_msg *msg) |
| +{ |
| + struct dw_dsi *dsi = host_to_dsi(host); |
| + struct dsi_hw_ctx *ctx = dsi->ctx; |
| + void __iomem *base = ctx->base; |
| + int ret; |
| + |
| + switch (msg->type) { |
| + case MIPI_DSI_DCS_SHORT_WRITE: |
| + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: |
| + case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: |
| + ret = dsi_dcs_short_write(base, msg); |
| + break; |
| + case MIPI_DSI_DCS_LONG_WRITE: |
| + ret = dsi_dcs_long_write(base, msg); |
| + break; |
| + default: |
| + DRM_ERROR("unsupported message type\n"); |
| + ret = -EINVAL; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +static const struct mipi_dsi_host_ops dsi_host_ops = { |
| + .attach = dsi_host_attach, |
| + .detach = dsi_host_detach, |
| + .transfer = dsi_host_transfer, |
| +}; |
| + |
| +static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) |
| +{ |
| + struct mipi_dsi_host *host = &dsi->host; |
| + struct mipi_panel_info *mipi = &dsi->mipi; |
| + int ret; |
| + |
| + host->dev = dev; |
| + host->ops = &dsi_host_ops; |
| + |
| + mipi->max_tx_esc_clk = 10 * 1000000UL; |
| + mipi->vc = 0; |
| + mipi->color_mode = DSI_24BITS_1; |
| + mipi->clk_post_adjust = 120; |
| + mipi->clk_pre_adjust = 0; |
| + mipi->clk_t_hs_prepare_adjust = 0; |
| + mipi->clk_t_lpx_adjust = 0; |
| + mipi->clk_t_hs_trial_adjust = 0; |
| + mipi->clk_t_hs_exit_adjust = 0; |
| + mipi->clk_t_hs_zero_adjust = 0; |
| + |
| + dsi->ldi.data_en_plr = 0; |
| + dsi->ldi.vsync_plr = 0; |
| + dsi->ldi.hsync_plr = 0; |
| + |
| + ret = mipi_dsi_host_register(host); |
| + if (ret) { |
| + DRM_ERROR("failed to register dsi host\n"); |
| + return ret; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_parse_bridge_endpoint(struct dw_dsi *dsi, |
| + struct device_node *endpoint) |
| +{ |
| + struct device_node *bridge_node; |
| + struct drm_bridge *bridge; |
| + |
| + bridge_node = of_graph_get_remote_port_parent(endpoint); |
| + if (!bridge_node) { |
| + DRM_ERROR("no valid bridge node\n"); |
| + return -ENODEV; |
| + } |
| + of_node_put(bridge_node); |
| + |
| + bridge = of_drm_find_bridge(bridge_node); |
| + if (!bridge) { |
| + DRM_INFO("wait for external HDMI bridge driver.\n"); |
| + return -EPROBE_DEFER; |
| + } |
| + dsi->bridge = bridge; |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_parse_panel_endpoint(struct dw_dsi *dsi, |
| + struct device_node *endpoint) |
| +{ |
| + struct device_node *panel_node; |
| + struct drm_panel *panel; |
| + |
| + panel_node = of_graph_get_remote_port_parent(endpoint); |
| + if (!panel_node) { |
| + DRM_ERROR("no valid panel node\n"); |
| + return -ENODEV; |
| + } |
| + of_node_put(panel_node); |
| + |
| + panel = of_drm_find_panel(panel_node); |
| + if (IS_ERR(panel)) { |
| + DRM_DEBUG_DRIVER("skip this panel endpoint.\n"); |
| + return 0; |
| + } |
| + dsi->panel = panel; |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_parse_endpoint(struct dw_dsi *dsi, |
| + struct device_node *np, |
| + enum dsi_output_client client) |
| +{ |
| + struct device_node *ep_node; |
| + struct of_endpoint ep; |
| + int ret = 0; |
| + |
| + if (client == OUT_MAX) |
| + return -EINVAL; |
| + |
| + for_each_endpoint_of_node(np, ep_node) { |
| + ret = of_graph_parse_endpoint(ep_node, &ep); |
| + if (ret) { |
| + of_node_put(ep_node); |
| + return ret; |
| + } |
| + |
| + /* skip dsi input port, port == 0 is input port */ |
| + if (ep.port == 0) |
| + continue; |
| + |
| + /* parse bridge endpoint */ |
| + if (client == OUT_HDMI) { |
| + if (ep.id == 0) { |
| + ret = dsi_parse_bridge_endpoint(dsi, ep_node); |
| + if (dsi->bridge) |
| + break; |
| + } |
| + } else { /* parse panel endpoint */ |
| + if (ep.id > 0) { |
| + ret = dsi_parse_panel_endpoint(dsi, ep_node); |
| + if (dsi->panel) |
| + break; |
| + } |
| + } |
| + |
| + if (ret) { |
| + of_node_put(ep_node); |
| + return ret; |
| + } |
| + } |
| + |
| + if (!dsi->bridge && !dsi->panel) { |
| + DRM_ERROR("at least one bridge or panel node is required\n"); |
| + return -ENODEV; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) |
| +{ |
| + struct dsi_hw_ctx *ctx = dsi->ctx; |
| + int ret = 0; |
| + struct device_node *np = pdev->dev.of_node; |
| + |
| + /* parse HDMI bridge endpoint */ |
| + ret = dsi_parse_endpoint(dsi, np, OUT_HDMI); |
| + if (ret) |
| + return ret; |
| + |
| + |
| + |
| + /* parse panel endpoint */ |
| + ret = dsi_parse_endpoint(dsi, np, OUT_PANEL); |
| + if (ret) |
| + return ret; |
| + |
| + |
| + np = of_find_compatible_node(NULL, NULL, DTS_COMP_DSI_NAME); |
| + if (!np) { |
| + DRM_ERROR("NOT FOUND device node %s!\n", |
| + DTS_COMP_DSI_NAME); |
| + return -ENXIO; |
| + } |
| + |
| + ctx->base = of_iomap(np, 0); |
| + if (!(ctx->base)) { |
| + DRM_ERROR ("failed to get base resource.\n"); |
| + return -ENXIO; |
| + } |
| + |
| + ctx->peri_crg_base = of_iomap(np, 1); |
| + if (!(ctx->peri_crg_base)) { |
| + DRM_ERROR ("failed to get peri_crg_base resource.\n"); |
| + return -ENXIO; |
| + } |
| + |
| + dsi->gpio_mux = devm_gpiod_get(&pdev->dev, "mux", GPIOD_OUT_HIGH); |
| + if (IS_ERR(dsi->gpio_mux)) |
| + return PTR_ERR(dsi->gpio_mux); |
| + /* set dsi default output to panel */ |
| + dsi->cur_client = OUT_PANEL; |
| + |
| + /*dis-reset*/ |
| + /*ip_reset_dis_dsi0, ip_reset_dis_dsi1*/ |
| + outp32(ctx->peri_crg_base + PERRSTDIS3, 0x30000000); |
| + |
| + ctx->dss_dphy0_ref_clk = devm_clk_get(&pdev->dev, "clk_txdphy0_ref"); |
| + if (IS_ERR(ctx->dss_dphy0_ref_clk)) { |
| + DRM_ERROR("failed to get dss_dphy0_ref_clk clock\n"); |
| + return PTR_ERR(ctx->dss_dphy0_ref_clk); |
| + } |
| + |
| + ret = clk_set_rate(ctx->dss_dphy0_ref_clk, DEFAULT_MIPI_CLK_RATE); |
| + if (ret < 0) { |
| + DRM_ERROR("dss_dphy0_ref_clk clk_set_rate(%lu) failed, error=%d!\n", |
| + DEFAULT_MIPI_CLK_RATE, ret); |
| + return -EINVAL; |
| + } |
| + |
| + DRM_DEBUG("dss_dphy0_ref_clk:[%lu]->[%lu].\n", |
| + DEFAULT_MIPI_CLK_RATE, clk_get_rate(ctx->dss_dphy0_ref_clk)); |
| + |
| + ctx->dss_dphy0_cfg_clk = devm_clk_get(&pdev->dev, "clk_txdphy0_cfg"); |
| + if (IS_ERR(ctx->dss_dphy0_cfg_clk)) { |
| + DRM_ERROR("failed to get dss_dphy0_cfg_clk clock\n"); |
| + return PTR_ERR(ctx->dss_dphy0_cfg_clk); |
| + } |
| + |
| + ret = clk_set_rate(ctx->dss_dphy0_cfg_clk, DEFAULT_MIPI_CLK_RATE); |
| + if (ret < 0) { |
| + DRM_ERROR("dss_dphy0_cfg_clk clk_set_rate(%lu) failed, error=%d!\n", |
| + DEFAULT_MIPI_CLK_RATE, ret); |
| + return -EINVAL; |
| + } |
| + |
| + DRM_DEBUG("dss_dphy0_cfg_clk:[%lu]->[%lu].\n", |
| + DEFAULT_MIPI_CLK_RATE, clk_get_rate(ctx->dss_dphy0_cfg_clk)); |
| + |
| + ctx->dss_pclk_dsi0_clk = devm_clk_get(&pdev->dev, "pclk_dsi0"); |
| + if (IS_ERR(ctx->dss_pclk_dsi0_clk)) { |
| + DRM_ERROR("failed to get dss_pclk_dsi0_clk clock\n"); |
| + return PTR_ERR(ctx->dss_pclk_dsi0_clk); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +const struct kirin_dsi_ops kirin_dsi_960 = { |
| + .version = KIRIN960_DSI, |
| + .parse_dt = dsi_parse_dt, |
| + .host_init = dsi_host_init, |
| + .encoder_enable = dsi_encoder_enable_sub, |
| + .encoder_valid = dsi_encoder_mode_valid |
| +}; |
| + |
| +MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver"); |
| +MODULE_LICENSE("GPL v2"); |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h |
| old mode 100644 |
| new mode 100755 |
| index 0da860200410..ab7fe962d211 |
| --- a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h |
| @@ -1,7 +1,7 @@ |
| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| - * Copyright (c) 2016 Linaro Limited. |
| - * Copyright (c) 2014-2016 Hisilicon Limited. |
| + * Copyright (c) 2016,2019 Linaro Limited. |
| + * Copyright (c) 2014-2016,2019 Hisilicon Limited. |
| */ |
| |
| #ifndef __KIRIN_ADE_REG_H__ |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_dpe_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_dpe_reg.h |
| new file mode 100644 |
| index 000000000000..f0ea3ee3fc86 |
| --- /dev/null |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_dpe_reg.h |
| @@ -0,0 +1,393 @@ |
| +/* SPDX-License-Identifier: GPL-2.0+ |
| + * |
| + * Copyright (c) 2016 Linaro Limited. |
| + * Copyright (c) 2014-2016 Hisilicon Limited. |
| + * |
| + * This program is free software; you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 2 as |
| + * published by the Free Software Foundation. |
| + * |
| + */ |
| +#ifndef __KIRIN_DPE_REG_H__ |
| +#define __KIRIN_DPE_REG_H__ |
| + |
| +#define BIT_MMU_IRPT_NS BIT(28) |
| +#define BIT_ITF0_INTS BIT(16) |
| +#define BIT_DPP_INTS BIT(15) |
| +#define BIT_VACTIVE0_END BIT(8) |
| +#define BIT_VACTIVE0_START BIT(7) |
| +#define BIT_VSYNC BIT(4) |
| +#define BIT_LDI_UNFLOW BIT(2) |
| + |
| +#define DFS_TIME (80) |
| +#define DFS_TIME_MIN (50) |
| +#define DFS_TIME_MIN_4K (10) |
| +#define DBUF0_DEPTH (1408) |
| +#define DBUF_WIDTH_BIT (144) |
| +#define PERRSTDIS3 (0x088) |
| + |
| +#define DPE_GLB0_OFFSET (0x12000) |
| +#define DPE_DBG_OFFSET (0x11000) |
| +#define DPE_CMDLIST_OFFSET (0x02000) |
| +#define DPE_SMMU_OFFSET (0x08000) |
| +#define DPE_MIF_OFFSET (0x0A000) |
| +#define DPE_MCTRL_SYS_OFFSET (0x10000) |
| +#define DPE_MCTRL_CTL0_OFFSET (0x10800) |
| +#define DPE_RCH_VG0_DMA_OFFSET (0x20000) |
| +#define DPE_RCH_VG0_SCL_OFFSET (0x20200) |
| +#define DPE_RCH_VG0_ARSR_OFFSET (0x20300) |
| +#define DPE_RCH_VG1_DMA_OFFSET (0x28000) |
| +#define DPE_RCH_VG1_SCL_OFFSET (0x28200) |
| +#define DPE_RCH_VG2_DMA_OFFSET (0x30000) |
| +#define DPE_RCH_VG2_SCL_OFFSET (0x30200) |
| +#define DPE_RCH_G0_DMA_OFFSET (0x38000) |
| +#define DPE_RCH_G0_SCL_OFFSET (0x38200) |
| +#define DPE_RCH_G1_DMA_OFFSET (0x40000) |
| +#define DPE_RCH_G1_SCL_OFFSET (0x40200) |
| +#define DPE_RCH_D2_DMA_OFFSET (0x50000) |
| +#define DPE_RCH_D3_DMA_OFFSET (0x51000) |
| +#define DPE_RCH_D0_DMA_OFFSET (0x52000) |
| +#define DPE_RCH_D0_DFC_OFFSET (0x52100) |
| +#define DPE_RCH_D1_DMA_OFFSET (0x53000) |
| +#define DPE_WCH0_DMA_OFFSET (0x5A000) |
| +#define DPE_WCH1_DMA_OFFSET (0x5C000) |
| +#define DPE_WCH2_DMA_OFFSET (0x5E000) |
| +#define DPE_WCH2_DFC_OFFSET (0x5E100) |
| +#define DPE_OVL0_OFFSET (0x60000) |
| +#define DPE_DBUF0_OFFSET (0x6D000) |
| +#define DPE_DPP_OFFSET (0x70000) |
| +#define DPE_DPP_DITHER_OFFSET (0x70200) |
| +#define DPE_LDI0_OFFSET (0x7D000) |
| +#define DPE_IFBC_OFFSET (0x7D800) |
| +#define DPE_DSC_OFFSET (0x7DC00) |
| + |
| +#define GLB_CPU_PDP_INTS (DPE_GLB0_OFFSET + 0x224) |
| +#define GLB_CPU_PDP_INT_MSK (DPE_GLB0_OFFSET + 0x228) |
| +#define GLB_CPU_SDP_INTS (DPE_GLB0_OFFSET + 0x22C) |
| +#define GLB_CPU_SDP_INT_MSK (DPE_GLB0_OFFSET + 0x230) |
| + |
| +#define DBG_MCTL_INTS (0x023C) |
| +#define DBG_MCTL_INT_MSK (0x0240) |
| +#define DBG_WCH0_INTS (0x0244) |
| +#define DBG_WCH0_INT_MSK (0x0248) |
| +#define DBG_WCH1_INTS (0x024C) |
| +#define DBG_WCH1_INT_MSK (0x0250) |
| +#define DBG_RCH0_INTS (0x0254) |
| +#define DBG_RCH0_INT_MSK (0x0258) |
| +#define DBG_RCH1_INTS (0x025C) |
| +#define DBG_RCH1_INT_MSK (0x0260) |
| +#define DBG_RCH2_INTS (0x0264) |
| +#define DBG_RCH2_INT_MSK (0x0268) |
| +#define DBG_RCH3_INTS (0x026C) |
| +#define DBG_RCH3_INT_MSK (0x0270) |
| +#define DBG_RCH4_INTS (0x0274) |
| +#define DBG_RCH4_INT_MSK (0x0278) |
| +#define DBG_RCH5_INTS (0x027C) |
| +#define DBG_RCH5_INT_MSK (0x0280) |
| +#define DBG_RCH6_INTS (0x0284) |
| +#define DBG_RCH6_INT_MSK (0x0288) |
| +#define DBG_RCH7_INTS (0x028C) |
| +#define DBG_RCH7_INT_MSK (0x0290) |
| +#define DBG_DPE_GLB_INTS (0x0294) |
| +#define DBG_DPE_GLB_INT_MSK (0x0298) |
| + |
| +#define AIF0_CH0_OFFSET (0x7000) |
| +#define AIF0_CH0_ADD_OFFSET (0x7004) |
| + |
| +#define MIF_ENABLE (0x0000) |
| +#define MIF_MEM_CTRL (0x0004) |
| +#define MIF_CTRL0 (0x0000) |
| +#define MIF_CTRL1 (0x0004) |
| +#define MIF_CTRL2 (0x0008) |
| +#define MIF_CTRL3 (0x000C) |
| +#define MIF_CTRL4 (0x0010) |
| +#define MIF_CTRL5 (0x0014) |
| +#define MIF_CTRL_OFFSET (0x0020) |
| +#define MIF_CH0_OFFSET (DPE_MIF_OFFSET + MIF_CTRL_OFFSET * 1) |
| + |
| +#define SMMU_SCR (0x0000) |
| +#define SMMU_MEMCTRL (0x0004) |
| +#define SMMU_LP_CTRL (0x0008) |
| +#define SMMU_INTMASK_NS (0x0010) |
| +#define SMMU_INTRAW_NS (0x0014) |
| +#define SMMU_INTSTAT_NS (0x0018) |
| +#define SMMU_INTCLR_NS (0x001C) |
| +#define SMMU_SMRx_NS (0x0020) |
| + |
| +#define DMA_OFT_X0 (0x0000) |
| +#define DMA_OFT_Y0 (0x0004) |
| +#define DMA_OFT_X1 (0x0008) |
| +#define DMA_OFT_Y1 (0x000C) |
| +#define DMA_MASK0 (0x0010) |
| +#define DMA_MASK1 (0x0014) |
| +#define DMA_STRETCH_SIZE_VRT (0x0018) |
| +#define DMA_CTRL (0x001C) |
| +#define DMA_TILE_SCRAM (0x0020) |
| +#define DMA_PULSE (0x0028) |
| +#define DMA_CORE_GT (0x002C) |
| +#define DMA_DATA_ADDR0 (0x0060) |
| +#define DMA_STRIDE0 (0x0064) |
| +#define DMA_STRETCH_STRIDE0 (0x0068) |
| +#define DMA_DATA_NUM0 (0x006C) |
| +#define DMA_CH_CTL (0x00D4) |
| +#define DMA_CH_REG_DEFAULT (0x0A00) |
| +#define DMA_ALIGN_BYTES (128 / BITS_PER_BYTE) |
| +#define DMA_ADDR_ALIGN (128 / BITS_PER_BYTE) |
| +#define DMA_STRIDE_ALIGN (128 / BITS_PER_BYTE) |
| + |
| +#define DFC_DISP_SIZE (0x0000) |
| +#define DFC_PIX_IN_NUM (0x0004) |
| +#define DFC_GLB_ALPHA (0x0008) |
| +#define DFC_DISP_FMT (0x000C) |
| +#define DFC_CLIP_CTL_HRZ (0x0010) |
| +#define DFC_CLIP_CTL_VRZ (0x0014) |
| +#define DFC_CTL_CLIP_EN (0x0018) |
| +#define DFC_ICG_MODULE (0x001C) |
| +#define DFC_DITHER_ENABLE (0x0020) |
| +#define DFC_PADDING_CTL (0x0024) |
| + |
| +#define MCTL_CTL_EN (0x0000) |
| +#define MCTL_CTL_MUTEX (0x0004) |
| +#define MCTL_CTL_MUTEX_STATUS (0x0008) |
| +#define MCTL_CTL_MUTEX_ITF (0x000C) |
| +#define MCTL_CTL_MUTEX_DBUF (0x0010) |
| +#define MCTL_CTL_MUTEX_SCF (0x0014) |
| +#define MCTL_CTL_MUTEX_OV (0x0018) |
| +#define MCTL_CTL_MUTEX_WCH0 (0x0020) |
| +#define MCTL_CTL_MUTEX_RCH0 (0x0030) |
| +#define MCTL_CTL_TOP (0x0050) |
| +#define MCTL_CTL_DBG (0x00E0) |
| +#define MCTL_RCH0_FLUSH_EN (0x0100) |
| +#define MCTL_OV0_FLUSH_EN (0x0128) |
| +#define MCTL_RCH0_OV_OEN (0x0160) |
| +#define MCTL_RCH_OV0_SEL (0x0180) |
| + |
| +#define OVL_SIZE (0x0000) |
| +#define OVL_BG_COLOR (0x0004) |
| +#define OVL_DST_STARTPOS (0x0008) |
| +#define OVL_DST_ENDPOS (0x000C) |
| +#define OVL_GCFG (0x0010) |
| +#define OVL_LAYER0_POS (0x0014) |
| +#define OVL_LAYER0_SIZE (0x0018) |
| +#define OVL_LAYER0_ALPHA (0x0030) |
| +#define OVL_LAYER0_CFG (0x0034) |
| +#define OVL6_REG_DEFAULT (0x01A8) |
| + |
| +#define DBUF_FRM_SIZE (0x0000) |
| +#define DBUF_FRM_HSIZE (0x0004) |
| +#define DBUF_SRAM_VALID_NUM (0x0008) |
| +#define DBUF_WBE_EN (0x000C) |
| +#define DBUF_THD_FILL_LEV0 (0x0010) |
| +#define DBUF_DFS_FILL_LEV1 (0x0014) |
| +#define DBUF_THD_RQOS (0x0018) |
| +#define DBUF_THD_WQOS (0x001C) |
| +#define DBUF_THD_CG (0x0020) |
| +#define DBUF_THD_OTHER (0x0024) |
| +#define DBUF_ONLINE_FILL_LEVEL (0x003C) |
| +#define DBUF_WB_FILL_LEVEL (0x0040) |
| +#define DBUF_DFS_STATUS (0x0044) |
| +#define DBUF_THD_FLUX_REQ_BEF (0x0048) |
| +#define DBUF_DFS_LP_CTRL (0x004C) |
| +#define DBUF_RD_SHADOW_SEL (0x0050) |
| +#define DBUF_MEM_CTRL (0x0054) |
| +#define DBUF_THD_FLUX_REQ_AFT (0x0064) |
| +#define DBUF_THD_DFS_OK (0x0068) |
| +#define DBUF_FLUX_REQ_CTRL (0x006C) |
| +#define DBUF_REG_DEFAULT (0x00A4) |
| + |
| +#define DPP_IMG_SIZE_BEF_SR (0x000C) |
| +#define DPP_IMG_SIZE_AFT_SR (0x0010) |
| +#define DPP_INTS (0x0040) |
| +#define DPP_INT_MSK (0x0044) |
| + |
| +#define SCF_COEF_MEM_CTRL (0x0018) |
| +#define IFBC_MEM_CTRL (0x001C) |
| +#define DITHER_MEM_CTRL (0x002C) |
| +#define DSC_MEM_CTRL (0x0084) |
| +#define ARSR2P_LB_MEM_CTRL (0x0084) |
| +#define SCF_LB_MEM_CTRL (0x0090) |
| +#define ROT_MEM_CTRL (0x0538) |
| +#define VPP_MEM_CTRL (0x0704) |
| +#define CMD_MEM_CTRL (0x073C) |
| +#define DMA_BUF_MEM_CTRL (0x0854) |
| +#define AFBCD_MEM_CTRL (0x093C) |
| +#define AFBCE_MEM_CTRL (0x0924) |
| + |
| +#define LDI_DPI0_HRZ_CTRL0 (0x0000) |
| +#define LDI_DPI0_HRZ_CTRL1 (0x0004) |
| +#define LDI_DPI0_HRZ_CTRL2 (0x0008) |
| +#define LDI_VRT_CTRL0 (0x000C) |
| +#define LDI_VRT_CTRL1 (0x0010) |
| +#define LDI_VRT_CTRL2 (0x0014) |
| +#define LDI_PLR_CTRL (0x0018) |
| +#define LDI_CTRL (0x0024) |
| +#define LDI_WORK_MODE (0x0028) |
| +#define LDI_DSI_CMD_MOD_CTRL (0x0030) |
| +#define LDI_VINACT_MSK_LEN (0x0050) |
| +#define LDI_CMD_EVENT_SEL (0x0060) |
| +#define LDI_MEM_CTRL (0x0100) |
| +#define LDI_PXL0_DIV2_GT_EN (0x0210) |
| +#define LDI_PXL0_DIV4_GT_EN (0x0214) |
| +#define LDI_PXL0_GT_EN (0x0218) |
| +#define LDI_PXL0_DSI_GT_EN (0x021C) |
| +#define LDI_PXL0_DIVXCFG (0x0220) |
| +#define LDI_VESA_CLK_SEL (0x0228) |
| +#define LDI_CPU_ITF_INTS (0x0248) |
| +#define LDI_CPU_ITF_INT_MSK (0x024C) |
| + |
| +#define MIPIDSI_VERSION_OFFSET (0x0000) |
| +#define MIPIDSI_PWR_UP_OFFSET (0x0004) |
| +#define MIPIDSI_CLKMGR_CFG_OFFSET (0x0008) |
| +#define MIPIDSI_DPI_VCID_OFFSET (0x000c) |
| +#define MIPIDSI_DPI_COLOR_CODING_OFFSET (0x0010) |
| +#define MIPIDSI_DPI_CFG_POL_OFFSET (0x0014) |
| +#define MIPIDSI_DPI_LP_CMD_TIM_OFFSET (0x0018) |
| +#define MIPIDSI_PCKHDL_CFG_OFFSET (0x002c) |
| +#define MIPIDSI_GEN_VCID_OFFSET (0x0030) |
| +#define MIPIDSI_MODE_CFG_OFFSET (0x0034) |
| +#define MIPIDSI_VID_MODE_CFG_OFFSET (0x0038) |
| +#define MIPIDSI_VID_PKT_SIZE_OFFSET (0x003c) |
| +#define MIPIDSI_VID_NUM_CHUNKS_OFFSET (0x0040) |
| +#define MIPIDSI_VID_NULL_SIZE_OFFSET (0x0044) |
| +#define MIPIDSI_VID_HSA_TIME_OFFSET (0x0048) |
| +#define MIPIDSI_VID_HBP_TIME_OFFSET (0x004c) |
| +#define MIPIDSI_VID_HLINE_TIME_OFFSET (0x0050) |
| +#define MIPIDSI_VID_VSA_LINES_OFFSET (0x0054) |
| +#define MIPIDSI_VID_VBP_LINES_OFFSET (0x0058) |
| +#define MIPIDSI_VID_VFP_LINES_OFFSET (0x005c) |
| +#define MIPIDSI_VID_VACTIVE_LINES_OFFSET (0x0060) |
| +#define MIPIDSI_EDPI_CMD_SIZE_OFFSET (0x0064) |
| +#define MIPIDSI_CMD_MODE_CFG_OFFSET (0x0068) |
| +#define MIPIDSI_GEN_HDR_OFFSET (0x006c) |
| +#define MIPIDSI_GEN_PLD_DATA_OFFSET (0x0070) |
| +#define MIPIDSI_CMD_PKT_STATUS_OFFSET (0x0074) |
| +#define MIPIDSI_TO_CNT_CFG_OFFSET (0x0078) |
| +#define MIPIDSI_BTA_TO_CNT_OFFSET (0x008C) |
| +#define MIPIDSI_SDF_3D_OFFSET (0x0090) |
| +#define MIPIDSI_LPCLK_CTRL_OFFSET (0x0094) |
| +#define MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET (0x0098) |
| +#define MIPIDSI_PHY_TMR_CFG_OFFSET (0x009c) |
| +#define MIPIDSI_PHY_RSTZ_OFFSET (0x00a0) |
| +#define MIPIDSI_PHY_IF_CFG_OFFSET (0x00a4) |
| +#define MIPIDSI_PHY_ULPS_CTRL_OFFSET (0x00a8) |
| +#define MIPIDSI_PHY_TX_TRIGGERS_OFFSET (0x00ac) |
| +#define MIPIDSI_PHY_STATUS_OFFSET (0x00b0) |
| +#define MIPIDSI_PHY_TST_CTRL0_OFFSET (0x00b4) |
| +#define MIPIDSI_PHY_TST_CTRL1_OFFSET (0x00b8) |
| +#define MIPIDSI_PHY_TMR_RD_CFG_OFFSET (0x00f4) |
| + |
| +enum XRES_DIV { |
| + XRES_DIV_1 = 1, |
| + XRES_DIV_2, |
| +}; |
| + |
| +enum YRES_DIV { |
| + YRES_DIV_1 = 1, |
| + YRES_DIV_2, |
| +}; |
| + |
| +enum PXL0_DIVCFG { |
| + PXL0_DIVCFG_0 = 0, |
| + PXL0_DIVCFG_1, |
| +}; |
| + |
| +enum PXL0_DIV2_GT_EN { |
| + PXL0_DIV2_GT_EN_CLOSE = 0, |
| + PXL0_DIV2_GT_EN_OPEN, |
| +}; |
| + |
| +enum PXL0_DIV4_GT_EN { |
| + PXL0_DIV4_GT_EN_CLOSE = 0, |
| + PXL0_DIV4_GT_EN_OPEN, |
| +}; |
| + |
| +enum PXL0_DSI_GT_EN { |
| + PXL0_DSI_GT_EN_0 = 0, |
| + PXL0_DSI_GT_EN_1, |
| +}; |
| + |
| +enum lcd_format { |
| + LCD_RGB888 = 0, |
| + LCD_RGB101010, |
| + LCD_RGB565, |
| +}; |
| + |
| +enum lcd_rgb_order { |
| + LCD_RGB = 0, |
| + LCD_BGR, |
| +}; |
| + |
| +enum dpe_dfc_format { |
| + DFC_PIXEL_FORMAT_RGB_565 = 0, |
| + DFC_PIXEL_FORMAT_XRGB_4444, |
| + DFC_PIXEL_FORMAT_ARGB_4444, |
| + DFC_PIXEL_FORMAT_XRGB_5551, |
| + DFC_PIXEL_FORMAT_ARGB_5551, |
| + DFC_PIXEL_FORMAT_XRGB_8888, |
| + DFC_PIXEL_FORMAT_ARGB_8888, |
| + DFC_PIXEL_FORMAT_BGR_565, |
| + DFC_PIXEL_FORMAT_XBGR_4444, |
| + DFC_PIXEL_FORMAT_ABGR_4444, |
| + DFC_PIXEL_FORMAT_XBGR_5551, |
| + DFC_PIXEL_FORMAT_ABGR_5551, |
| + DFC_PIXEL_FORMAT_XBGR_8888, |
| + DFC_PIXEL_FORMAT_ABGR_8888, |
| + DFC_PIXEL_FORMAT_YUV444, |
| + DFC_PIXEL_FORMAT_YVU444, |
| + DFC_PIXEL_FORMAT_YUYV422, |
| + DFC_PIXEL_FORMAT_YVYU422, |
| + DFC_PIXEL_FORMAT_VYUY422, |
| + DFC_PIXEL_FORMAT_UYVY422, |
| +}; |
| + |
| +enum dpe_dma_format { |
| + DMA_PIXEL_FORMAT_RGB_565 = 0, |
| + DMA_PIXEL_FORMAT_ARGB_4444, |
| + DMA_PIXEL_FORMAT_XRGB_4444, |
| + DMA_PIXEL_FORMAT_ARGB_5551, |
| + DMA_PIXEL_FORMAT_XRGB_5551, |
| + DMA_PIXEL_FORMAT_ARGB_8888, |
| + DMA_PIXEL_FORMAT_XRGB_8888, |
| + DMA_PIXEL_FORMAT_RESERVED0, |
| + DMA_PIXEL_FORMAT_YUYV_422_Pkg, |
| + DMA_PIXEL_FORMAT_YUV_420_SP_HP, |
| + DMA_PIXEL_FORMAT_YUV_420_P_HP, |
| + DMA_PIXEL_FORMAT_YUV_422_SP_HP, |
| + DMA_PIXEL_FORMAT_YUV_422_P_HP, |
| + DMA_PIXEL_FORMAT_AYUV_4444, |
| +}; |
| + |
| +enum dpe_fb_format { |
| + DPE_RGB_565 = 0, |
| + DPE_RGBX_4444, |
| + DPE_RGBA_4444, |
| + DPE_RGBX_5551, |
| + DPE_RGBA_5551, |
| + DPE_RGBX_8888, |
| + DPE_RGBA_8888, |
| + DPE_BGR_565, |
| + DPE_BGRX_4444, |
| + DPE_BGRA_4444, |
| + DPE_BGRX_5551, |
| + DPE_BGRA_5551, |
| + DPE_BGRX_8888, |
| + DPE_BGRA_8888, |
| + DPE_YUV_422_I, |
| + /* YUV Semi-planar */ |
| + DPE_YCbCr_422_SP, |
| + DPE_YCrCb_422_SP, |
| + DPE_YCbCr_420_SP, |
| + DPE_YCrCb_420_SP, |
| + /* YUV Planar */ |
| + DPE_YCbCr_422_P, |
| + DPE_YCrCb_422_P, |
| + DPE_YCbCr_420_P, |
| + DPE_YCrCb_420_P, |
| + /* YUV Package */ |
| + DPE_YUYV_422_Pkg, |
| + DPE_UYVY_422_Pkg, |
| + DPE_YVYU_422_Pkg, |
| + DPE_VYUY_422_Pkg, |
| +}; |
| + |
| +#endif |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c |
| old mode 100644 |
| new mode 100755 |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c |
| index d3145ae877d7..63b519845ad6 100644 |
| --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c |
| @@ -336,9 +336,11 @@ static int kirin_drm_platform_remove(struct platform_device *pdev) |
| } |
| |
| static const struct of_device_id kirin_drm_dt_ids[] = { |
| +#ifdef CONFIG_DRM_HISI_KIRIN620 |
| { .compatible = "hisilicon,hi6220-ade", |
| .data = &ade_driver_data, |
| }, |
| +#endif |
| { /* end node */ }, |
| }; |
| MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h |
| old mode 100644 |
| new mode 100755 |
| index 4d5c05a24065..5e8ac676c6e2 |
| --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h |
| @@ -53,6 +53,8 @@ struct kirin_drm_data { |
| void (*cleanup_hw_ctx)(void *hw_ctx); |
| }; |
| |
| +#ifdef CONFIG_DRM_HISI_KIRIN620 |
| extern struct kirin_drm_data ade_driver_data; |
| +#endif |
| |
| #endif /* __KIRIN_DRM_DRV_H__ */ |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.c |
| new file mode 100644 |
| index 000000000000..f91b57848ebf |
| --- /dev/null |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.c |
| @@ -0,0 +1,444 @@ |
| +/* |
| + * DesignWare MIPI DSI Host Controller v1.02 driver |
| + * |
| + * Copyright (c) 2016 Linaro Limited. |
| + * Copyright (c) 2014-2016 Hisilicon Limited. |
| + * |
| + * Author: |
| + * <shizongxuan@huawei.com> |
| + * <zhangxiubin@huawei.com> |
| + * <lvda3@hisilicon.com> |
| + * |
| + * This program is free software; you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 2 as |
| + * published by the Free Software Foundation. |
| + * |
| + */ |
| +#include <linux/clk.h> |
| +#include <linux/component.h> |
| +#include <linux/delay.h> |
| +#include <linux/module.h> |
| +#include <linux/platform_device.h> |
| + |
| +#include <drm/drm_atomic_helper.h> |
| +#include <drm/drm_device.h> |
| +#include <drm/drm_encoder_slave.h> |
| +#include <drm/drm_mipi_dsi.h> |
| +#include <drm/drm_of.h> |
| +#include <drm/drm_print.h> |
| +#include <drm/drm_probe_helper.h> |
| +#include <drm/drm_sysfs.h> |
| + |
| +#include "kirin_drm_dsi.h" |
| +#include "dw_dsi_reg.h" |
| + |
| +static struct kirin_dsi_ops *hisi_dsi_ops; |
| + |
| +void dsi_set_output_client(struct drm_device *dev) |
| +{ |
| + enum dsi_output_client client; |
| + struct drm_connector *connector; |
| + struct drm_encoder *encoder; |
| + struct drm_connector_list_iter conn_iter; |
| + struct dw_dsi *dsi; |
| + |
| + mutex_lock(&dev->mode_config.mutex); |
| + |
| + /* find dsi encoder */ |
| + drm_for_each_encoder(encoder, dev) |
| + if (encoder->encoder_type == DRM_MODE_ENCODER_DSI) |
| + break; |
| + dsi = encoder_to_dsi(encoder); |
| + |
| + /* find HDMI connector */ |
| + drm_connector_list_iter_begin(dev, &conn_iter); |
| + drm_for_each_connector_iter(connector, &conn_iter) |
| + if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) |
| + break; |
| + drm_connector_list_iter_end(&conn_iter); |
| + |
| + /* |
| + * set the proper dsi output client |
| + */ |
| + client = connector->status == connector_status_connected ? |
| + OUT_HDMI : OUT_PANEL; |
| + if (client != dsi->cur_client) { |
| + /* associate bridge and dsi encoder */ |
| + if (client == OUT_HDMI) |
| + encoder->bridge = dsi->bridge; |
| + else |
| + encoder->bridge = NULL; |
| + /* |
| + * set the switch ic to select the HDMI or MIPI_DSI |
| + */ |
| + if (KIRIN960_DSI == hisi_dsi_ops->version) { |
| + gpiod_set_value_cansleep(dsi->gpio_mux, client); |
| + }else if (KIRIN620_DSI == hisi_dsi_ops->version) { |
| + /*the gpio0_1*/ |
| + } |
| + dsi->cur_client = client; |
| + /* let the userspace know panel connector status has changed */ |
| + drm_sysfs_hotplug_event(dev); |
| + DRM_INFO("client change to %s\n", client == OUT_HDMI ? |
| + "HDMI" : "panel"); |
| + } |
| + |
| + mutex_unlock(&dev->mode_config.mutex); |
| +} |
| +EXPORT_SYMBOL(dsi_set_output_client); |
| +/************************for the panel attach to dsi*****************************/ |
| +static int dsi_connector_get_modes(struct drm_connector *connector) |
| +{ |
| + struct dw_dsi *dsi = connector_to_dsi(connector); |
| + |
| + return drm_panel_get_modes(dsi->panel); |
| +} |
| + |
| +static enum drm_mode_status |
| +dsi_connector_mode_valid(struct drm_connector *connector, |
| + struct drm_display_mode *mode) |
| +{ |
| + enum drm_mode_status mode_status = MODE_OK; |
| + |
| + return mode_status; |
| +} |
| + |
| +static struct drm_encoder * |
| +dsi_connector_best_encoder(struct drm_connector *connector) |
| +{ |
| + struct dw_dsi *dsi = connector_to_dsi(connector); |
| + |
| + return &dsi->encoder; |
| +} |
| + |
| +static struct drm_connector_helper_funcs dsi_connector_helper_funcs = { |
| + .get_modes = dsi_connector_get_modes, |
| + .mode_valid = dsi_connector_mode_valid, |
| + .best_encoder = dsi_connector_best_encoder, |
| +}; |
| + |
| +static enum drm_connector_status |
| +dsi_connector_detect(struct drm_connector *connector, bool force) |
| +{ |
| + struct dw_dsi *dsi = connector_to_dsi(connector); |
| + enum drm_connector_status status; |
| + |
| + status = dsi->cur_client == OUT_PANEL ? connector_status_connected : |
| + connector_status_disconnected; |
| + |
| + return status; |
| +} |
| + |
| +static void dsi_connector_destroy(struct drm_connector *connector) |
| +{ |
| + drm_connector_unregister(connector); |
| + drm_connector_cleanup(connector); |
| +} |
| + |
| +static struct drm_connector_funcs dsi_atomic_connector_funcs = { |
| + .fill_modes = drm_helper_probe_single_connector_modes, |
| + .detect = dsi_connector_detect, |
| + .destroy = dsi_connector_destroy, |
| + .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 int dsi_connector_init(struct drm_device *dev, struct dw_dsi *dsi) |
| +{ |
| + struct drm_encoder *encoder = &dsi->encoder; |
| + struct drm_connector *connector = &dsi->connector; |
| + int ret; |
| + |
| + connector->polled = DRM_CONNECTOR_POLL_HPD; |
| + drm_connector_helper_add(connector, |
| + &dsi_connector_helper_funcs); |
| + |
| + ret = drm_connector_init(dev, &dsi->connector, |
| + &dsi_atomic_connector_funcs, |
| + DRM_MODE_CONNECTOR_DSI); |
| + if (ret) |
| + return ret; |
| + |
| + ret = drm_connector_attach_encoder(connector, encoder); |
| + if (ret) |
| + return ret; |
| + |
| + ret = drm_panel_attach(dsi->panel, connector); |
| + if (ret) |
| + return ret; |
| + |
| + DRM_INFO("connector init\n"); |
| + return 0; |
| +} |
| +/****************************************************************************/ |
| + |
| +/***************************for the encoder_helper_funcs****************************************/ |
| +static const struct drm_encoder_funcs dw_encoder_funcs = { |
| + .destroy = drm_encoder_cleanup, |
| +}; |
| + |
| + |
| +static int dsi_encoder_atomic_check(struct drm_encoder *encoder, |
| + struct drm_crtc_state *crtc_state, |
| + struct drm_connector_state *conn_state) |
| +{ |
| + /* do nothing */ |
| + return 0; |
| +} |
| + |
| +static enum drm_mode_status dsi_encoder_mode_valid(struct drm_encoder *encoder, |
| + const struct drm_display_mode *mode) |
| + |
| +{ |
| + return hisi_dsi_ops->encoder_valid(encoder,mode); |
| +} |
| + |
| +static void dsi_encoder_mode_set(struct drm_encoder *encoder, |
| + struct drm_display_mode *mode, |
| + struct drm_display_mode *adj_mode) |
| +{ |
| + struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| + |
| + drm_mode_copy(&dsi->cur_mode, adj_mode); |
| +} |
| + |
| +static void dsi_encoder_enable(struct drm_encoder *encoder) |
| +{ |
| + struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| + |
| + |
| + |
| + if (dsi->enable) |
| + return; |
| + |
| + hisi_dsi_ops->encoder_enable(encoder); |
| + |
| + if (KIRIN960_DSI == hisi_dsi_ops->version) { |
| + /* turn on panel */ |
| + if (dsi->panel && drm_panel_prepare(dsi->panel)) |
| + DRM_ERROR("failed to prepare panel\n"); |
| + |
| + /*dw_dsi_set_mode(dsi, DSI_VIDEO_MODE);*/ |
| + |
| + /* turn on panel's back light */ |
| + if (dsi->panel && drm_panel_enable(dsi->panel)) |
| + DRM_ERROR("failed to enable panel\n"); |
| + |
| + } |
| + |
| + dsi->enable = true; |
| +} |
| + |
| +static void dw_dsi_set_mode(struct dw_dsi *dsi, enum dsi_work_mode mode) |
| +{ |
| + struct dsi_hw_ctx *ctx = dsi->ctx; |
| + void __iomem *base = ctx->base; |
| + |
| + writel(RESET, base + PWR_UP); |
| + writel(mode, base + MODE_CFG); |
| + writel(POWERUP, base + PWR_UP); |
| +} |
| + |
| +static void dsi_encoder_disable(struct drm_encoder *encoder) |
| +{ |
| + struct dw_dsi *dsi = encoder_to_dsi(encoder); |
| + struct dsi_hw_ctx *ctx = dsi->ctx; |
| + |
| + if (!dsi->enable) |
| + return; |
| + |
| + dw_dsi_set_mode(dsi, DSI_COMMAND_MODE); |
| + |
| + if (KIRIN960_DSI == hisi_dsi_ops->version) { |
| + /* turn off panel's backlight */ |
| + if (dsi->panel && drm_panel_disable(dsi->panel)) |
| + DRM_ERROR("failed to disable panel\n"); |
| + |
| + /* turn off panel */ |
| + if (dsi->panel && drm_panel_unprepare(dsi->panel)) |
| + DRM_ERROR("failed to unprepare panel\n"); |
| + |
| + clk_disable_unprepare(ctx->dss_dphy0_ref_clk); |
| + clk_disable_unprepare(ctx->dss_dphy0_cfg_clk); |
| + clk_disable_unprepare(ctx->dss_pclk_dsi0_clk); |
| + } |
| + |
| + dsi->enable = false; |
| +} |
| + |
| +static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = { |
| + .atomic_check = dsi_encoder_atomic_check, |
| + .mode_valid = dsi_encoder_mode_valid, |
| + .mode_set = dsi_encoder_mode_set, |
| + .enable = dsi_encoder_enable, |
| + .disable = dsi_encoder_disable |
| +}; |
| + |
| +/****************************************************************************/ |
| +static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) |
| +{ |
| + struct drm_encoder *encoder = &dsi->encoder; |
| + struct drm_bridge *bridge = dsi->bridge; |
| + int ret; |
| + |
| + /* associate the bridge to dsi encoder */ |
| + ret = drm_bridge_attach(encoder, bridge, NULL); |
| + if (ret) { |
| + DRM_ERROR("failed to attach external bridge\n"); |
| + return ret; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int dw_drm_encoder_init(struct device *dev, |
| + struct drm_device *drm_dev, |
| + struct drm_encoder *encoder) |
| +{ |
| + int ret; |
| + u32 crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node); |
| + |
| + if (!crtc_mask) { |
| + DRM_ERROR("failed to find crtc mask\n"); |
| + return -EINVAL; |
| + } |
| + |
| + encoder->possible_crtcs = crtc_mask; |
| + ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs, |
| + DRM_MODE_ENCODER_DSI, NULL); |
| + if (ret) { |
| + DRM_ERROR("failed to init dsi encoder\n"); |
| + return ret; |
| + } |
| + |
| + drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs); |
| + |
| + return 0; |
| +} |
| + |
| +static int dsi_bind(struct device *dev, struct device *master, void *data) |
| +{ |
| + struct dsi_data *ddata = dev_get_drvdata(dev); |
| + struct dw_dsi *dsi = &ddata->dsi; |
| + struct drm_device *drm_dev = data; |
| + int ret; |
| + |
| + DRM_INFO("+. \n"); |
| + ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder); |
| + if (ret) |
| + return ret; |
| + |
| + if (dsi->bridge) { |
| + ret = dsi_bridge_init(drm_dev, dsi); |
| + if (ret) |
| + return ret; |
| + } |
| + |
| + if (KIRIN960_DSI == hisi_dsi_ops->version) { |
| + if (dsi->panel) { |
| + ret = dsi_connector_init(drm_dev, dsi); |
| + if (ret) |
| + return ret; |
| + } |
| + }else if (KIRIN620_DSI == hisi_dsi_ops->version) { |
| + /*the panel for the kirin620 drm have not support*/ |
| + } |
| + |
| + DRM_INFO("-. \n"); |
| + return 0; |
| +} |
| + |
| +static void dsi_unbind(struct device *dev, struct device *master, void *data) |
| +{ |
| + /* do nothing */ |
| +} |
| + |
| +static const struct component_ops dsi_ops = { |
| + .bind = dsi_bind, |
| + .unbind = dsi_unbind, |
| +}; |
| + |
| + |
| + |
| +static int dsi_probe(struct platform_device *pdev) |
| +{ |
| + struct device *dev = &pdev->dev; |
| + struct dsi_data *data; |
| + struct dw_dsi *dsi; |
| + struct dsi_hw_ctx *ctx; |
| + int ret; |
| + |
| + hisi_dsi_ops = (struct kirin_dsi_ops *) of_device_get_match_data(dev); |
| + |
| + DRM_INFO("+. \n"); |
| + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); |
| + if (!data) { |
| + DRM_ERROR("failed to allocate dsi data.\n"); |
| + return -ENOMEM; |
| + } |
| + dsi = &data->dsi; |
| + ctx = &data->ctx; |
| + dsi->ctx = ctx; |
| + |
| + if (NULL == hisi_dsi_ops) { |
| + DRM_ERROR("hisi_dsi_ops is not bind\n"); |
| + } |
| + ret = hisi_dsi_ops->host_init(dev, dsi); |
| + if (ret) |
| + return ret; |
| + |
| + ret = hisi_dsi_ops->parse_dt(pdev, dsi); |
| + if (ret) |
| + goto err_host_unregister; |
| + |
| + platform_set_drvdata(pdev, data); |
| + |
| + ret = component_add(dev, &dsi_ops); |
| + if (ret) |
| + goto err_host_unregister; |
| + |
| + DRM_INFO("-. \n"); |
| + return 0; |
| + |
| +err_host_unregister: |
| + mipi_dsi_host_unregister(&dsi->host); |
| + return ret; |
| +} |
| +static int dsi_remove(struct platform_device *pdev) |
| +{ |
| + component_del(&pdev->dev, &dsi_ops); |
| + |
| + return 0; |
| +} |
| + |
| +static const struct of_device_id dsi_of_match[] = { |
| +#ifdef CONFIG_DRM_HISI_KIRIN960 |
| + { |
| + .compatible = "hisilicon,hi3660-dsi", |
| + .data = &kirin_dsi_960, |
| + }, |
| +#endif |
| +#ifdef CONFIG_DRM_HISI_KIRIN620 |
| + { |
| + .compatible = "hisilicon,hi6220-dsi", |
| + .data = &kirin_dsi_620, |
| + }, |
| +#endif |
| + { /* end node */} |
| +}; |
| +MODULE_DEVICE_TABLE(of, dsi_of_match); |
| + |
| +static struct platform_driver dsi_driver = { |
| + .probe = dsi_probe, |
| + .remove = dsi_remove, |
| + .driver = { |
| + .name = "dw-dsi", |
| + .of_match_table = dsi_of_match, |
| + }, |
| +}; |
| + |
| +module_platform_driver(dsi_driver); |
| + |
| +MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver"); |
| +MODULE_LICENSE("GPL v2"); |
| diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.h |
| new file mode 100644 |
| index 000000000000..4043784bd54c |
| --- /dev/null |
| +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dsi.h |
| @@ -0,0 +1,259 @@ |
| +#ifndef __KIRIN_DRM_DSI_H__ |
| +#define __KIRIN_DRM_DSI_H__ |
| + |
| + |
| +#include <linux/clk.h> |
| +#include <linux/component.h> |
| +#include <linux/of_graph.h> |
| +#include <linux/iopoll.h> |
| +#include <video/mipi_display.h> |
| +#include <linux/gpio/consumer.h> |
| +#include <linux/of_address.h> |
| +#include <linux/of_platform.h> |
| + |
| +#include <drm/drm_of.h> |
| +#include <drm/drm_crtc_helper.h> |
| +#include <drm/drm_mipi_dsi.h> |
| +#include <drm/drm_encoder_slave.h> |
| +#include <drm/drm_atomic_helper.h> |
| +#include <drm/drm_panel.h> |
| + |
| +#define ROUND(x, y) ((x) / (y) + \ |
| + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) |
| +#define PHY_REF_CLK_RATE 19200000 |
| +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000)) |
| + |
| +#define encoder_to_dsi(encoder) \ |
| + container_of(encoder, struct dw_dsi, encoder) |
| +#define host_to_dsi(host) \ |
| + container_of(host, struct dw_dsi, host) |
| +#define connector_to_dsi(connector) \ |
| + container_of(connector, struct dw_dsi, connector) |
| + |
| +enum dsi_output_client { |
| + OUT_HDMI = 0, |
| + OUT_PANEL, |
| + OUT_MAX |
| +}; |
| + |
| +struct dsi_phy_range { |
| + u32 min_range_kHz; |
| + u32 max_range_kHz; |
| + u32 pll_vco_750M; |
| + u32 hstx_ckg_sel; |
| +}; |
| + |
| +static const struct dsi_phy_range dphy_range_info[] = { |
| + { 46875, 62500, 1, 7 }, |
| + { 62500, 93750, 0, 7 }, |
| + { 93750, 125000, 1, 6 }, |
| + { 125000, 187500, 0, 6 }, |
| + { 187500, 250000, 1, 5 }, |
| + { 250000, 375000, 0, 5 }, |
| + { 375000, 500000, 1, 4 }, |
| + { 500000, 750000, 0, 4 }, |
| + { 750000, 1000000, 1, 0 }, |
| + { 1000000, 1500000, 0, 0 } |
| +}; |
| + |
| +struct dsi_hw_ctx { |
| + void __iomem *base; |
| + char __iomem *peri_crg_base; |
| + |
| + struct clk *pclk; |
| + struct clk *dss_dphy0_ref_clk; |
| + struct clk *dss_dphy1_ref_clk; |
| + struct clk *dss_dphy0_cfg_clk; |
| + struct clk *dss_dphy1_cfg_clk; |
| + struct clk *dss_pclk_dsi0_clk; |
| + struct clk *dss_pclk_dsi1_clk; |
| +}; |
| + |
| +struct mipi_panel_info { |
| + u8 dsi_version; |
| + u8 vc; |
| + u8 lane_nums; |
| + u8 lane_nums_select_support; |
| + u8 color_mode; |
| + u32 dsi_bit_clk; /* clock lane(p/n) */ |
| + u32 burst_mode; |
| + u32 max_tx_esc_clk; |
| + u8 non_continue_en; |
| + |
| + u32 dsi_bit_clk_val1; |
| + u32 dsi_bit_clk_val2; |
| + u32 dsi_bit_clk_val3; |
| + u32 dsi_bit_clk_val4; |
| + u32 dsi_bit_clk_val5; |
| + u32 dsi_bit_clk_upt; |
| + /*uint32_t dsi_pclk_rate;*/ |
| + |
| + u32 hs_wr_to_time; |
| + |
| + /* dphy config parameter adjust*/ |
| + u32 clk_post_adjust; |
| + u32 clk_pre_adjust; |
| + u32 clk_pre_delay_adjust; |
| + u32 clk_t_hs_exit_adjust; |
| + u32 clk_t_hs_trial_adjust; |
| + u32 clk_t_hs_prepare_adjust; |
| + int clk_t_lpx_adjust; |
| + u32 clk_t_hs_zero_adjust; |
| + u32 data_post_delay_adjust; |
| + int data_t_lpx_adjust; |
| + u32 data_t_hs_prepare_adjust; |
| + u32 data_t_hs_zero_adjust; |
| + u32 data_t_hs_trial_adjust; |
| + u32 rg_vrefsel_vcm_adjust; |
| + |
| + /*only for Chicago<3660> use*/ |
| + u32 rg_vrefsel_vcm_clk_adjust; |
| + u32 rg_vrefsel_vcm_data_adjust; |
| +}; |
| + |
| +struct mipi_phy_params { |
| + u32 clk_t_lpx; |
| + u32 clk_t_hs_prepare; |
| + u32 clk_t_hs_zero; |
| + u32 clk_t_hs_trial; |
| + u32 clk_t_wakeup; |
| + u32 data_t_lpx; |
| + u32 data_t_hs_prepare; |
| + u32 data_t_hs_zero; |
| + u32 data_t_hs_trial; |
| + u32 data_t_ta_go; |
| + u32 data_t_ta_get; |
| + u32 data_t_wakeup; |
| + u32 hstx_ckg_sel; |
| + u32 pll_fbd_div5f; |
| + u32 pll_fbd_div1f; |
| + u32 pll_fbd_2p; |
| + u32 pll_enbwt; |
| + u32 pll_fbd_p; |
| + u32 pll_fbd_s; |
| + u32 pll_pre_div1p; |
| + u32 pll_pre_p; |
| + u32 pll_vco_750M; |
| + u32 pll_lpf_rs; |
| + u32 pll_lpf_cs; |
| + u32 clk_division; |
| + /********for hikey620************/ |
| + u32 clklp2hs_time; |
| + u32 clkhs2lp_time; |
| + u32 lp2hs_time; |
| + u32 hs2lp_time; |
| + u32 clk_to_data_delay; |
| + u32 data_to_clk_delay; |
| + u32 lane_byte_clk_kHz; |
| + /*****************/ |
| + |
| + /****for hikey960*****/ |
| + u64 lane_byte_clk; |
| + |
| + u32 clk_lane_lp2hs_time; |
| + u32 clk_lane_hs2lp_time; |
| + u32 data_lane_lp2hs_time; |
| + u32 data_lane_hs2lp_time; |
| + u32 clk2data_delay; |
| + u32 data2clk_delay; |
| + |
| + u32 clk_pre_delay; |
| + u32 clk_post_delay; |
| + u32 data_pre_delay; |
| + u32 data_post_delay; |
| + u32 phy_stop_wait_time; |
| + u32 rg_vrefsel_vcm; |
| + |
| + u32 rg_pll_enswc; |
| + u32 rg_pll_chp; |
| + |
| + u32 pll_register_override; /*0x1E[0]*/ |
| + u32 pll_power_down; /*0x1E[1]*/ |
| + u32 rg_band_sel; /*0x1E[2]*/ |
| + u32 rg_phase_gen_en; /*0x1E[3]*/ |
| + u32 reload_sel; /*0x1E[4]*/ |
| + u32 rg_pll_cp_p; /*0x1E[7:5]*/ |
| + u32 rg_pll_refsel; /*0x16[1:0]*/ |
| + u32 rg_pll_cp; /*0x16[7:5]*/ |
| + u32 load_command; |
| + /*********/ |
| +}; |
| + |
| +struct ldi_panel_info { |
| + u32 h_back_porch; |
| + u32 h_front_porch; |
| + u32 h_pulse_width; |
| + |
| + /* |
| + ** note: vbp > 8 if used overlay compose, |
| + ** also lcd vbp > 8 in lcd power on sequence |
| + */ |
| + u32 v_back_porch; |
| + u32 v_front_porch; |
| + u32 v_pulse_width; |
| + |
| + u8 hsync_plr; |
| + u8 vsync_plr; |
| + u8 pixelclk_plr; |
| + u8 data_en_plr; |
| + |
| + /* for cabc */ |
| + u8 dpi0_overlap_size; |
| + u8 dpi1_overlap_size; |
| +}; |
| + |
| +struct dw_dsi_client { |
| + u32 lanes; |
| + u32 phy_clock; /* in kHz */ |
| + enum mipi_dsi_pixel_format format; |
| + unsigned long mode_flags; |
| +}; |
| + |
| +struct dw_dsi { |
| + struct drm_encoder encoder; |
| + struct drm_bridge *bridge; |
| + struct drm_panel *panel; |
| + struct mipi_dsi_host host; |
| + struct drm_connector connector; /* connector for panel */ |
| + struct drm_display_mode cur_mode; |
| + struct dsi_hw_ctx *ctx; |
| + struct mipi_phy_params phy; |
| + struct mipi_panel_info mipi; |
| + struct ldi_panel_info ldi; |
| + u32 lanes; |
| + enum mipi_dsi_pixel_format format; |
| + unsigned long mode_flags; |
| + struct gpio_desc *gpio_mux; |
| + struct dw_dsi_client client[OUT_MAX]; |
| + enum dsi_output_client cur_client; |
| + bool enable; |
| +}; |
| + |
| +struct dsi_data { |
| + struct dw_dsi dsi; |
| + struct dsi_hw_ctx ctx; |
| +}; |
| + |
| +enum kirin_dsi_version { |
| + KIRIN620_DSI = 0, |
| + KIRIN960_DSI |
| +}; |
| + |
| +/* display controller init/cleanup ops */ |
| +struct kirin_dsi_ops { |
| + enum kirin_dsi_version version; |
| + int (*parse_dt)(struct platform_device *pdev, struct dw_dsi *dsi); |
| + int (*host_init)(struct device *dev, struct dw_dsi *dsi); |
| + void (*encoder_enable)(struct drm_encoder *encoder); |
| + enum drm_mode_status(*encoder_valid)(struct drm_encoder *encoder, |
| + const struct drm_display_mode *mode); |
| +}; |
| + |
| +#ifdef CONFIG_DRM_HISI_KIRIN960 |
| +extern const struct kirin_dsi_ops kirin_dsi_960; |
| +#endif |
| +#ifdef CONFIG_DRM_HISI_KIRIN620 |
| +extern const struct kirin_dsi_ops kirin_dsi_620; |
| +#endif |
| + |
| +#endif /* __KIRIN_DRM_DSI_H__ */ |