Message ID | 1411383728-2075-1-git-send-email-mark.yao@rock-chips.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mon, Sep 22, 2014 at 7:02 AM, Mark yao <mark.yao@rock-chips.com> wrote: > This adds support for Rockchip soc edp found on rk3288 > > Signed-off-by: Mark Yao <mark.yao@rock-chips.com> > Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com> > --- > Changes in v2: > - fix code sytle > - use some define from drm_dp_helper.h > - use panel-simple driver for primary display. > - remove unnecessary clock clk_24m_parent. > > Changes in v3: None > > Changes in v4: None > > drivers/gpu/drm/rockchip/Kconfig | 9 + > drivers/gpu/drm/rockchip/Makefile | 2 + > drivers/gpu/drm/rockchip/rockchip_edp_core.c | 853 ++++++++++++++++++ > drivers/gpu/drm/rockchip/rockchip_edp_core.h | 309 +++++++ > drivers/gpu/drm/rockchip/rockchip_edp_reg.c | 1202 ++++++++++++++++++++++++++ > drivers/gpu/drm/rockchip/rockchip_edp_reg.h | 345 ++++++++ > 6 files changed, 2720 insertions(+) > create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h > create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h > > diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig > index 7146c80..04b1f8c 100644 > --- a/drivers/gpu/drm/rockchip/Kconfig > +++ b/drivers/gpu/drm/rockchip/Kconfig > @@ -17,3 +17,12 @@ config DRM_ROCKCHIP > management to userspace. This driver does not provides > 2D or 3D acceleration; acceleration is performed by other > IP found on the SoC. > + > +config ROCKCHIP_EDP > + bool "Rockchip edp support" > + depends on DRM_ROCKCHIP > + help > + Choose this option if you have a Rockchip eDP. > + Rockchip rk3288 SoC has eDP TX Controller can be used. > + If you have an Embedded DisplayPort Panel, say Y to enable its > + driver. > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile > index 6e6d468..a0fc3a1 100644 > --- a/drivers/gpu/drm/rockchip/Makefile > +++ b/drivers/gpu/drm/rockchip/Makefile > @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip > rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ > rockchip_drm_gem.o rockchip_drm_vop.o > > +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o rockchip_edp_reg.o > + > obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o > diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c b/drivers/gpu/drm/rockchip/rockchip_edp_core.c > new file mode 100644 > index 0000000..5450d1fa > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c > @@ -0,0 +1,853 @@ > +/* > +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > +* Author: > +* Andy yan <andy.yan@rock-chips.com> > +* Jeff chen <jeff.chen@rock-chips.com> > +* > +* based on exynos_dp_core.c > +* hmm, did you look at all at drm_dp_helpers? The exynos code probably pre-dates the helpers, so might not be the best example to work off of.. If there is actually a valid reason not to use the dp-helpers, then you should mention the reasons, at least in the commit msg if not the code BR, -R > +* This program is free software; you can redistribute it and/or modify it > +* under the terms of the GNU General Public License as published by the > +* Free Software Foundation; either version 2 of the License, or (at your > +* option) any later version. > +*/ > + > +#include <drm/drmP.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_panel.h> > +#include <drm/drm_of.h> > + > +#include <linux/component.h> > +#include <linux/clk.h> > +#include <linux/mfd/syscon.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > + > +#include <video/of_videomode.h> > +#include <video/videomode.h> > + > +#include "rockchip_edp_core.h" > + > +#define connector_to_edp(c) \ > + container_of(c, struct rockchip_edp_device, connector) > + > +#define encoder_to_edp(c) \ > + container_of(c, struct rockchip_edp_device, encoder) > + > +static struct rockchip_edp_soc_data soc_data[2] = { > + /* rk3288 */ > + {.grf_soc_con6 = 0x025c, > + .grf_soc_con12 = 0x0274}, > + /* no edp switching needed */ > + {.grf_soc_con6 = -1, > + .grf_soc_con12 = -1}, > +}; > + > +static const struct of_device_id rockchip_edp_dt_ids[] = { > + {.compatible = "rockchip,rk3288-edp", > + .data = (void *)&soc_data[0] }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(of, rockchip_edp_dt_ids); > + > +static int rockchip_edp_clk_enable(struct rockchip_edp_device *edp) > +{ > + int ret = 0; > + > + if (!edp->clk_on) { > + ret = clk_prepare_enable(edp->pclk); > + if (ret < 0) { > + dev_err(edp->dev, "cannot enable edp pclk %d\n", ret); > + goto err_pclk; > + } > + > + ret = clk_prepare_enable(edp->clk_edp); > + if (ret < 0) { > + dev_err(edp->dev, "cannot enable clk_edp %d\n", ret); > + goto err_clk_edp; > + } > + > + ret = clk_set_rate(edp->clk_24m, 24000000); > + if (ret < 0) { > + dev_err(edp->dev, "cannot set edp clk_24m %d\n", > + ret); > + goto err_clk_24m; > + } > + > + ret = clk_prepare_enable(edp->clk_24m); > + if (ret < 0) { > + dev_err(edp->dev, "cannot enable edp clk_24m %d\n", > + ret); > + goto err_clk_24m; > + } > + > + edp->clk_on = true; > + } > + > + return 0; > + > +err_clk_24m: > + clk_disable_unprepare(edp->clk_edp); > +err_clk_edp: > + clk_disable_unprepare(edp->pclk); > +err_pclk: > + edp->clk_on = false; > + > + return ret; > +} > + > +static int rockchip_edp_clk_disable(struct rockchip_edp_device *edp) > +{ > + if (edp->clk_on) { > + clk_disable_unprepare(edp->pclk); > + clk_disable_unprepare(edp->clk_edp); > + clk_disable_unprepare(edp->clk_24m); > + edp->clk_on = false; > + } > + > + return 0; > +} > + > +static int rockchip_edp_pre_init(struct rockchip_edp_device *edp) > +{ > + u32 val; > + int ret; > + > + val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16); > + ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val); > + if (ret != 0) { > + dev_err(edp->dev, "Could not write to GRF: %d\n", ret); > + return ret; > + } > + > + reset_control_assert(edp->rst); > + usleep_range(10, 20); > + reset_control_deassert(edp->rst); > + > + return 0; > +} > + > +static int rockchip_edp_init_edp(struct rockchip_edp_device *edp) > +{ > + rockchip_edp_reset(edp); > + rockchip_edp_init_refclk(edp); > + rockchip_edp_init_interrupt(edp); > + rockchip_edp_enable_sw_function(edp); > + rockchip_edp_init_analog_func(edp); > + rockchip_edp_init_hpd(edp); > + rockchip_edp_init_aux(edp); > + > + return 0; > +} > + > +static int rockchip_edp_get_max_rx_bandwidth( > + struct rockchip_edp_device *edp, > + u8 *bandwidth) > +{ > + u8 data; > + int retval; > + > + /* > + * For DP rev.1.1, Maximum link rate of Main Link lanes > + * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps > + */ > + retval = rockchip_edp_read_byte_from_dpcd( > + edp, DP_MAX_LINK_RATE, &data); > + if (retval < 0) > + *bandwidth = 0; > + else > + *bandwidth = data; > + > + return retval; > +} > + > +static int rockchip_edp_get_max_rx_lane_count(struct rockchip_edp_device *edp, > + u8 *lane_count) > +{ > + u8 data; > + int retval; > + > + /* > + * For DP rev.1.1, Maximum number of Main Link lanes > + * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes > + */ > + retval = rockchip_edp_read_byte_from_dpcd( > + edp, DP_MAX_LANE_COUNT, &data); > + if (retval < 0) > + *lane_count = 0; > + else > + *lane_count = DPCD_MAX_LANE_COUNT(data); > + > + return retval; > +} > + > +static int rockchip_edp_init_training(struct rockchip_edp_device *edp) > +{ > + int retval; > + > + /* > + * MACRO_RST must be applied after the PLL_LOCK to avoid > + * the DP inter pair skew issue for at least 10 us > + */ > + rockchip_edp_reset_macro(edp); > + > + retval = rockchip_edp_get_max_rx_bandwidth( > + edp, &edp->link_train.link_rate); > + retval = rockchip_edp_get_max_rx_lane_count( > + edp, &edp->link_train.lane_count); > + dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n", > + edp->link_train.link_rate * 27 / 100, > + edp->link_train.link_rate * 27 % 100, > + edp->link_train.lane_count); > + > + if ((edp->link_train.link_rate != DP_LINK_BW_1_62) && > + (edp->link_train.link_rate != DP_LINK_BW_2_7)) { > + dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n" > + "use default link rate:%d.%dGps\n", > + edp->link_train.link_rate, > + edp->video_info.link_rate * 27 / 100, > + edp->video_info.link_rate * 27 % 100); > + edp->link_train.link_rate = edp->video_info.link_rate; > + } > + > + if (edp->link_train.lane_count == 0) { > + dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n" > + "use default lanes:%d\n", > + edp->link_train.lane_count, > + edp->video_info.lane_count); > + edp->link_train.lane_count = edp->video_info.lane_count; > + } > + > + rockchip_edp_analog_power_ctr(edp, 1); > + > + return 0; > +} > + > +static int rockchip_edp_hw_link_training(struct rockchip_edp_device *edp) > +{ > + u32 cnt = 50; > + u32 val; > + > + /* Set link rate and count as you want to establish*/ > + rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate); > + rockchip_edp_set_lane_count(edp, edp->link_train.lane_count); > + rockchip_edp_hw_link_training_en(edp); > + val = rockchip_edp_wait_hw_lt_done(edp); > + while (val) { > + if (cnt-- <= 0) { > + dev_err(edp->dev, "hw lt timeout"); > + return -ETIMEDOUT; > + } > + mdelay(1); > + val = rockchip_edp_wait_hw_lt_done(edp); > + } > + > + val = rockchip_edp_get_hw_lt_status(edp); > + if (val) > + dev_err(edp->dev, "hw lt err:%d\n", val); > + > + return val; > +} > + > +static int rockchip_edp_set_link_train(struct rockchip_edp_device *edp) > +{ > + int retval; > + > + rockchip_edp_init_training(edp); > + > + retval = rockchip_edp_hw_link_training(edp); > + if (retval < 0) > + dev_err(edp->dev, "DP hw LT failed!\n"); > + > + return retval; > +} > + > +static int rockchip_edp_config_video(struct rockchip_edp_device *edp, > + struct video_info *video_info) > +{ > + int retval = 0; > + int timeout_loop = 0; > + int done_count = 0; > + > + rockchip_edp_config_video_slave_mode(edp, video_info); > + > + rockchip_edp_set_video_color_format(edp, video_info->color_depth, > + video_info->color_space, > + video_info->dynamic_range, > + video_info->ycbcr_coeff); > + > + if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) { > + dev_err(edp->dev, "PLL is not locked yet.\n"); > + return -EINVAL; > + } > + > + for (;;) { > + timeout_loop++; > + if (rockchip_edp_is_slave_video_stream_clock_on(edp) == 0) > + break; > + > + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { > + dev_err(edp->dev, "Timeout of video streamclk ok\n"); > + return -ETIMEDOUT; > + } > + > + udelay(1); > + } > + > + /* Set to use the register calculated M/N video */ > + rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); > + > + /* Disable video mute */ > + rockchip_edp_enable_video_mute(edp, 0); > + > + /* Configure video slave mode */ > + rockchip_edp_enable_video_master(edp, 0); > + > + /* Enable video */ > + rockchip_edp_start_video(edp); > + > + timeout_loop = 0; > + > + for (;;) { > + timeout_loop++; > + if (rockchip_edp_is_video_stream_on(edp) == 0) { > + done_count++; > + if (done_count > 10) > + break; > + } else if (done_count) { > + done_count = 0; > + } > + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { > + dev_err(edp->dev, "Timeout of video streamclk ok\n"); > + return -ETIMEDOUT; > + } > + > + mdelay(1); > + } > + > + if (retval != 0) > + dev_err(edp->dev, "Video stream is not detected!\n"); > + > + return retval; > +} > + > +static irqreturn_t rockchip_edp_isr(int irq, void *arg) > +{ > + struct rockchip_edp_device *edp = arg; > + enum dp_irq_type irq_type; > + > + irq_type = rockchip_edp_get_irq_type(edp); > + switch (irq_type) { > + case DP_IRQ_TYPE_HP_CABLE_IN: > + dev_dbg(edp->dev, "Received irq - cable in\n"); > + rockchip_edp_clear_hotplug_interrupts(edp); > + break; > + case DP_IRQ_TYPE_HP_CABLE_OUT: > + dev_dbg(edp->dev, "Received irq - cable out\n"); > + rockchip_edp_clear_hotplug_interrupts(edp); > + break; > + case DP_IRQ_TYPE_HP_CHANGE: > + /* > + * We get these change notifications once in a while, but there > + * is nothing we can do with them. Just ignore it for now and > + * only handle cable changes. > + */ > + dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n"); > + rockchip_edp_clear_hotplug_interrupts(edp); > + break; > + default: > + dev_err(edp->dev, "Received irq - unknown type[%x]!\n", > + irq_type); > + rockchip_edp_clear_hotplug_interrupts(edp); > + break; > + } > + > + return IRQ_HANDLED; > +} > + > +static void rockchip_edp_commit(struct drm_encoder *encoder) > +{ > + struct rockchip_edp_device *edp = encoder_to_edp(encoder); > + int ret; > + > + ret = rockchip_edp_set_link_train(edp); > + if (ret) > + dev_err(edp->dev, "link train failed!\n"); > + else > + dev_dbg(edp->dev, "link training success.\n"); > + > + rockchip_edp_set_lane_count(edp, edp->link_train.lane_count); > + rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate); > + rockchip_edp_init_video(edp); > + > + ret = rockchip_edp_config_video(edp, &edp->video_info); > + if (ret) > + dev_err(edp->dev, "unable to config video\n"); > +} > + > +static void rockchip_edp_poweron(struct drm_encoder *encoder) > +{ > + struct rockchip_edp_device *edp = encoder_to_edp(encoder); > + int ret; > + > + if (edp->dpms_mode == DRM_MODE_DPMS_ON) > + return; > + > + if (edp->panel) > + edp->panel->funcs->enable(edp->panel); > + > + ret = rockchip_edp_clk_enable(edp); > + if (ret < 0) { > + dev_err(edp->dev, "cannot enable edp clk %d\n", ret); > + return; > + } > + > + ret = rockchip_edp_pre_init(edp); > + if (ret < 0) { > + dev_err(edp->dev, "edp pre init fail %d\n", ret); > + return; > + } > + > + ret = rockchip_edp_init_edp(edp); > + if (ret < 0) { > + dev_err(edp->dev, "edp init fail %d\n", ret); > + return; > + } > + > + enable_irq(edp->irq); > + rockchip_edp_commit(encoder); > +} > + > +static void rockchip_edp_poweroff(struct drm_encoder *encoder) > +{ > + struct rockchip_edp_device *edp = encoder_to_edp(encoder); > + > + if (edp->dpms_mode == DRM_MODE_DPMS_OFF) > + return; > + > + disable_irq(edp->irq); > + rockchip_edp_reset(edp); > + rockchip_edp_analog_power_ctr(edp, 0); > + rockchip_edp_clk_disable(edp); > + if (edp->panel) > + edp->panel->funcs->disable(edp->panel); > +} > + > +static enum drm_connector_status > +rockchip_connector_detect(struct drm_connector *connector, bool force) > +{ > + return connector_status_connected; > +} > + > +static void rockchip_connector_destroy(struct drm_connector *connector) > +{ > + drm_sysfs_connector_remove(connector); > + drm_connector_cleanup(connector); > +} > + > +static struct drm_connector_funcs rockchip_connector_funcs = { > + .dpms = drm_helper_connector_dpms, > + .detect = rockchip_connector_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = rockchip_connector_destroy, > +}; > + > +static int rockchip_connector_get_modes(struct drm_connector *connector) > +{ > + struct rockchip_edp_device *edp = connector_to_edp(connector); > + struct drm_panel *panel = edp->panel; > + > + return panel->funcs->get_modes(panel); > +} > + > +static struct drm_encoder * > + rockchip_connector_best_encoder(struct drm_connector *connector) > +{ > + struct rockchip_edp_device *edp = connector_to_edp(connector); > + > + return &edp->encoder; > +} > + > +static enum drm_mode_status rockchip_connector_mode_valid( > + struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + /* TODO(rk): verify that the mode is really valid */ > + return MODE_OK; > +} > + > +static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = { > + .get_modes = rockchip_connector_get_modes, > + .mode_valid = rockchip_connector_mode_valid, > + .best_encoder = rockchip_connector_best_encoder, > +}; > + > +static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode) > +{ > + struct rockchip_edp_device *edp = encoder_to_edp(encoder); > + > + if (edp->dpms_mode == mode) > + return; > + > + switch (mode) { > + case DRM_MODE_DPMS_ON: > + rockchip_edp_poweron(encoder); > + break; > + case DRM_MODE_DPMS_STANDBY: > + case DRM_MODE_DPMS_SUSPEND: > + case DRM_MODE_DPMS_OFF: > + rockchip_edp_poweroff(encoder); > + break; > + default: > + break; > + } > + > + edp->dpms_mode = mode; > +} > + > +static bool > +rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder, > + const struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + if (!adjusted_mode->private) { > + struct rockchip_display_mode *priv_mode; > + > + priv_mode = kzalloc(sizeof(*priv_mode), GFP_KERNEL); > + priv_mode->out_type = ROCKCHIP_DISPLAY_TYPE_EDP; > + adjusted_mode->private = (int *)priv_mode; > + } > + > + return true; > +} > + > +static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted) > +{ > + struct rockchip_edp_device *edp = encoder_to_edp(encoder); > + u32 val; > + int ret; > + > + ret = rockchip_drm_encoder_get_mux_id(edp->dev->of_node, encoder); > + if (ret < 0) > + return; > + > + if (ret == ROCKCHIP_CRTC_VOPL) > + val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16); > + else > + val = EDP_SEL_VOP_LIT << 16; > + > + dev_info(edp->dev, "vop %s output to edp\n", > + (ret == ROCKCHIP_CRTC_VOPL) ? "LIT" : "BIG"); > + ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val); > + if (ret != 0) { > + dev_err(edp->dev, "Could not write to GRF: %d\n", ret); > + return; > + } > + > + memcpy(&edp->mode, adjusted, sizeof(*mode)); > +} > + > +static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder) > +{ > +} > + > +static void rockchip_drm_encoder_commit(struct drm_encoder *encoder) > +{ > + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON); > +} > + > +static void rockchip_drm_encoder_disable(struct drm_encoder *encoder) > +{ > + struct drm_plane *plane; > + struct drm_device *dev = encoder->dev; > + > + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); > + > + /* all planes connected to this encoder should be also disabled. */ > + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > + if (plane->crtc && (plane->crtc == encoder->crtc)) > + plane->funcs->disable_plane(plane); > + } > +} > + > +static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = { > + .dpms = rockchip_drm_encoder_dpms, > + .mode_fixup = rockchip_drm_encoder_mode_fixup, > + .mode_set = rockchip_drm_encoder_mode_set, > + .prepare = rockchip_drm_encoder_prepare, > + .commit = rockchip_drm_encoder_commit, > + .disable = rockchip_drm_encoder_disable, > +}; > + > +static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder) > +{ > + drm_encoder_cleanup(encoder); > +} > + > +static struct drm_encoder_funcs rockchip_encoder_funcs = { > + .destroy = rockchip_drm_encoder_destroy, > +}; > + > +static int rockchip_edp_init(struct rockchip_edp_device *edp) > +{ > + struct device *dev = edp->dev; > + struct device_node *np = dev->of_node; > + struct platform_device *pdev = to_platform_device(dev); > + struct resource *res; > + const struct of_device_id *match; > + int ret; > + > + if (!np) { > + dev_err(dev, "Missing device tree node.\n"); > + return -EINVAL; > + } > + > + match = of_match_node(rockchip_edp_dt_ids, np); > + edp->soc_data = (struct rockchip_edp_soc_data *)match->data; > + /* > + * The control bit is located in the GRF register space. > + */ > + if (edp->soc_data->grf_soc_con6 >= 0) { > + edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); > + if (IS_ERR(edp->grf)) { > + dev_err(dev, > + "rk3288-edp needs rockchip,grf property\n"); > + return PTR_ERR(edp->grf); > + } > + } > + > + edp->video_info.h_sync_polarity = 0; > + edp->video_info.v_sync_polarity = 0; > + edp->video_info.interlaced = 0; > + edp->video_info.color_space = CS_RGB; > + edp->video_info.dynamic_range = VESA; > + edp->video_info.ycbcr_coeff = COLOR_YCBCR601; > + edp->video_info.color_depth = COLOR_8; > + > + edp->video_info.link_rate = DP_LINK_BW_1_62; > + edp->video_info.lane_count = LANE_CNT4; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + edp->regs = devm_ioremap_resource(dev, res); > + if (IS_ERR(edp->regs)) { > + dev_err(dev, "ioremap reg failed\n"); > + return PTR_ERR(edp->regs); > + } > + > + edp->clk_edp = devm_clk_get(dev, "clk_edp"); > + if (IS_ERR(edp->clk_edp)) { > + dev_err(dev, "cannot get clk_edp\n"); > + return PTR_ERR(edp->clk_edp); > + } > + > + edp->clk_24m = devm_clk_get(dev, "clk_edp_24m"); > + if (IS_ERR(edp->clk_24m)) { > + dev_err(dev, "cannot get clk_edp_24m\n"); > + return PTR_ERR(edp->clk_24m); > + } > + > + edp->pclk = devm_clk_get(dev, "pclk_edp"); > + if (IS_ERR(edp->pclk)) { > + dev_err(dev, "cannot get pclk\n"); > + return PTR_ERR(edp->pclk); > + } > + > + edp->rst = devm_reset_control_get(dev, "edp"); > + if (IS_ERR(edp->rst)) { > + dev_err(dev, "failed to get reset\n"); > + return PTR_ERR(edp->rst); > + } > + > + ret = rockchip_edp_clk_enable(edp); > + if (ret < 0) { > + dev_err(edp->dev, "cannot enable edp clk %d\n", ret); > + return ret; > + } > + > + ret = rockchip_edp_pre_init(edp); > + if (ret < 0) { > + dev_err(edp->dev, "failed to pre init %d\n", ret); > + return ret; > + } > + > + edp->irq = platform_get_irq(pdev, 0); > + if (edp->irq < 0) { > + dev_err(dev, "cannot find IRQ\n"); > + return edp->irq; > + } > + > + ret = devm_request_irq(dev, edp->irq, rockchip_edp_isr, 0, > + dev_name(dev), edp); > + if (ret) { > + dev_err(dev, "cannot claim IRQ %d\n", edp->irq); > + return ret; > + } > + > + disable_irq_nosync(edp->irq); > + > + edp->dpms_mode = DRM_MODE_DPMS_OFF; > + > + dev_set_name(edp->dev, "rockchip-edp"); > + > + return 0; > +} > + > +static int rockchip_edp_bind(struct device *dev, struct device *master, > + void *data) > +{ > + struct rockchip_edp_device *edp = dev_get_drvdata(dev); > + struct drm_encoder *encoder; > + struct drm_connector *connector; > + struct drm_device *drm_dev = data; > + int ret; > + > + ret = rockchip_edp_init(edp); > + if (ret < 0) > + return ret; > + > + edp->drm_dev = drm_dev; > + > + encoder = &edp->encoder; > + > + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, > + dev->of_node); > + DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); > + > + ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs, > + DRM_MODE_ENCODER_LVDS); > + if (ret) { > + DRM_ERROR("failed to initialize encoder with drm\n"); > + return ret; > + } > + > + drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs); > + > + connector = &edp->connector; > + connector->polled = DRM_CONNECTOR_POLL_HPD; > + connector->dpms = DRM_MODE_DPMS_OFF; > + > + ret = drm_connector_init(drm_dev, connector, > + &rockchip_connector_funcs, > + DRM_MODE_CONNECTOR_eDP); > + if (ret) { > + DRM_ERROR("failed to initialize connector with drm\n"); > + goto err_free_encoder; > + } > + > + drm_connector_helper_add(connector, > + &rockchip_connector_helper_funcs); > + > + ret = drm_sysfs_connector_add(connector); > + if (ret) { > + DRM_ERROR("failed to add drm_sysfs\n"); > + goto err_free_connector; > + } > + > + ret = drm_mode_connector_attach_encoder(connector, encoder); > + if (ret) { > + DRM_ERROR("failed to attach connector and encoder\n"); > + goto err_free_connector_sysfs; > + } > + > + ret = drm_panel_attach(edp->panel, connector); > + if (ret) { > + DRM_ERROR("failed to attach connector and encoder\n"); > + goto err_free_connector_sysfs; > + } > + > + return 0; > + > +err_free_connector_sysfs: > + drm_sysfs_connector_remove(connector); > +err_free_connector: > + drm_connector_cleanup(connector); > +err_free_encoder: > + drm_encoder_cleanup(encoder); > + return ret; > +} > + > +static void rockchip_edp_unbind(struct device *dev, struct device *master, > + void *data) > +{ > + struct rockchip_edp_device *edp = dev_get_drvdata(dev); > + struct drm_encoder *encoder; > + > + encoder = &edp->encoder; > + > + if (edp->panel) > + drm_panel_detach(edp->panel); > + > + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); > + encoder->funcs->destroy(encoder); > + drm_sysfs_connector_remove(&edp->connector); > + drm_connector_cleanup(&edp->connector); > + drm_encoder_cleanup(encoder); > +} > + > +static const struct component_ops rockchip_edp_component_ops = { > + .bind = rockchip_edp_bind, > + .unbind = rockchip_edp_unbind, > +}; > + > +static int rockchip_edp_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct drm_panel *panel; > + struct device_node *panel_node; > + struct rockchip_edp_device *edp; > + > + if (!dev->of_node) { > + dev_err(dev, "can't find eDP devices\n"); > + return -ENODEV; > + } > + > + panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0); > + if (!panel_node) { > + DRM_ERROR("failed to find diaplay panel\n"); > + return -ENODEV; > + } > + > + panel = of_drm_find_panel(panel_node); > + if (!panel) { > + DRM_ERROR("failed to find diaplay panel\n"); > + of_node_put(panel_node); > + return -EPROBE_DEFER; > + } > + > + of_node_put(panel_node); > + > + edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL); > + if (!edp) > + return -ENOMEM; > + edp->dev = dev; > + edp->panel = panel; > + platform_set_drvdata(pdev, edp); > + > + return component_add(dev, &rockchip_edp_component_ops); > +} > + > +static int rockchip_edp_remove(struct platform_device *pdev) > +{ > + component_del(&pdev->dev, &rockchip_edp_component_ops); > + > + return 0; > +} > + > +static struct platform_driver rockchip_edp_driver = { > + .probe = rockchip_edp_probe, > + .remove = rockchip_edp_remove, > + .driver = { > + .name = "rockchip-edp", > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(rockchip_edp_dt_ids), > + }, > +}; > + > +module_platform_driver(rockchip_edp_driver); > + > +MODULE_AUTHOR("Jeff chen <jeff.chen@rock-chips.com>"); > +MODULE_DESCRIPTION("ROCKCHIP EDP Driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.h b/drivers/gpu/drm/rockchip/rockchip_edp_core.h > new file mode 100644 > index 0000000..c13325f > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.h > @@ -0,0 +1,309 @@ > +/* > +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > +* Author: > +* Andy yan <andy.yan@rock-chips.com> > +* Jeff chen <jeff.chen@rock-chips.com> > +* > +* based on exynos_dp_core.h > +* > +* This program is free software; you can redistribute it and/or modify it > +* under the terms of the GNU General Public License as published by the > +* Free Software Foundation; either version 2 of the License, or (at your > +* option) any later version. > +*/ > + > +#ifndef _ROCKCHIP_EDP_CORE_H > +#define _ROCKCHIP_EDP_CORE_H > + > +#include <drm/drmP.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_dp_helper.h> > +#include <drm/drm_panel.h> > +#include "rockchip_drm_drv.h" > + > +#define DP_TIMEOUT_LOOP_CNT 100 > +#define MAX_CR_LOOP 5 > +#define MAX_EQ_LOOP 5 > + > +#define GRF_EDP_REF_CLK_SEL_INTER (1 << 4) > +#define GRF_EDP_HDCP_EN (1 << 15) > +#define GRF_EDP_BIST_EN (1 << 14) > +#define GRF_EDP_MEM_CTL_BY_EDP (1 << 13) > +#define GRF_EDP_SECURE_EN (1 << 3) > +#define EDP_SEL_VOP_LIT (1 << 5) > + > +enum link_lane_count_type { > + LANE_CNT1 = 1, > + LANE_CNT2 = 2, > + LANE_CNT4 = 4 > +}; > + > +enum link_training_state { > + LT_START, > + LT_CLK_RECOVERY, > + LT_EQ_TRAINING, > + FINISHED, > + FAILED > +}; > + > +enum voltage_swing_level { > + VOLTAGE_LEVEL_0, > + VOLTAGE_LEVEL_1, > + VOLTAGE_LEVEL_2, > + VOLTAGE_LEVEL_3, > +}; > + > +enum pre_emphasis_level { > + PRE_EMPHASIS_LEVEL_0, > + PRE_EMPHASIS_LEVEL_1, > + PRE_EMPHASIS_LEVEL_2, > + PRE_EMPHASIS_LEVEL_3, > +}; > + > +enum pattern_set { > + PRBS7, > + D10_2, > + TRAINING_PTN1, > + TRAINING_PTN2, > + DP_NONE > +}; > + > +enum color_space { > + CS_RGB, > + CS_YCBCR422, > + CS_YCBCR444 > +}; > + > +enum color_depth { > + COLOR_6, > + COLOR_8, > + COLOR_10, > + COLOR_12 > +}; > + > +enum color_coefficient { > + COLOR_YCBCR601, > + COLOR_YCBCR709 > +}; > + > +enum dynamic_range { > + VESA, > + CEA > +}; > + > +enum pll_status { > + DP_PLL_UNLOCKED, > + DP_PLL_LOCKED > +}; > + > +enum clock_recovery_m_value_type { > + CALCULATED_M, > + REGISTER_M > +}; > + > +enum video_timing_recognition_type { > + VIDEO_TIMING_FROM_CAPTURE, > + VIDEO_TIMING_FROM_REGISTER > +}; > + > +enum analog_power_block { > + AUX_BLOCK, > + CH0_BLOCK, > + CH1_BLOCK, > + CH2_BLOCK, > + CH3_BLOCK, > + ANALOG_TOTAL, > + POWER_ALL > +}; > + > +enum dp_irq_type { > + DP_IRQ_TYPE_HP_CABLE_IN, > + DP_IRQ_TYPE_HP_CABLE_OUT, > + DP_IRQ_TYPE_HP_CHANGE, > + DP_IRQ_TYPE_UNKNOWN, > +}; > + > +struct video_info { > + char *name; > + > + bool h_sync_polarity; > + bool v_sync_polarity; > + bool interlaced; > + > + enum color_space color_space; > + enum dynamic_range dynamic_range; > + enum color_coefficient ycbcr_coeff; > + enum color_depth color_depth; > + > + u8 link_rate; > + enum link_lane_count_type lane_count; > +}; > + > +struct link_train { > + int eq_loop; > + int cr_loop[4]; > + > + u8 link_rate; > + u8 lane_count; > + u8 training_lane[4]; > + > + enum link_training_state lt_state; > +}; > + > +/* > + * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds > + */ > +struct rockchip_edp_soc_data { > + int grf_soc_con6; > + int grf_soc_con12; > +}; > + > +struct rockchip_edp_device { > + struct device *dev; > + struct drm_device *drm_dev; > + struct drm_panel *panel; > + struct drm_connector connector; > + struct drm_encoder encoder; > + struct drm_display_mode mode; > + > + struct rockchip_edp_soc_data *soc_data; > + > + void __iomem *regs; > + struct regmap *grf; > + unsigned int irq; > + struct clk *clk_edp; > + struct clk *clk_24m_parent; > + struct clk *clk_24m; > + struct clk *pclk; > + struct reset_control *rst; > + struct link_train link_train; > + struct video_info video_info; > + bool clk_on; > + > + int dpms_mode; > +}; > + > +void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp, > + bool enable); > +void rockchip_edp_stop_video(struct rockchip_edp_device *edp); > +void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable); > +void rockchip_edp_init_refclk(struct rockchip_edp_device *edp); > +void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp); > +void rockchip_edp_reset(struct rockchip_edp_device *edp); > +void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp); > +u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp); > +void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp, > + bool enable); > +void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp); > +void rockchip_edp_init_hpd(struct rockchip_edp_device *edp); > +void rockchip_edp_reset_aux(struct rockchip_edp_device *edp); > +void rockchip_edp_init_aux(struct rockchip_edp_device *edp); > +int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp); > +void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp); > +int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp); > +int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp, > + unsigned int reg_addr, > + unsigned char data); > +int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp, > + unsigned int reg_addr, > + unsigned char *data); > +int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp, > + unsigned int reg_addr, > + unsigned int count, > + unsigned char data[]); > +int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp, > + unsigned int reg_addr, > + unsigned int count, > + unsigned char data[]); > +int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp, > + unsigned int device_addr, > + unsigned int reg_addr); > +int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp, > + unsigned int device_addr, > + unsigned int reg_addr, > + unsigned int *data); > +int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp, > + unsigned int device_addr, > + unsigned int reg_addr, > + unsigned int count, > + unsigned char edid[]); > +void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp, > + u32 bwtype); > +void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp, > + u32 *bwtype); > +void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, > + u32 count); > +void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, > + u32 *count); > +void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp, > + bool enable); > +void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp, > + enum pattern_set pattern); > +void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level); > +void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level); > +void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level); > +void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level); > +void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp, > + u32 training_lane); > +void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp, > + u32 training_lane); > +void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp, > + u32 training_lane); > +void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp, > + u32 training_lane); > +u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp); > +u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp); > +u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp); > +u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp); > +void rockchip_edp_reset_macro(struct rockchip_edp_device *edp); > +int rockchip_edp_init_video(struct rockchip_edp_device *edp); > + > +void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp, > + u32 color_depth, > + u32 color_space, > + u32 dynamic_range, > + u32 coeff); > +int > +rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp); > +void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp, > + enum clock_recovery_m_value_type type, > + u32 m_value, > + u32 n_value); > +void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp, > + u32 type); > +void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp, > + bool enable); > +void rockchip_edp_start_video(struct rockchip_edp_device *edp); > +int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp); > +void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp, > + struct video_info *video_info); > +void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp); > +void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp); > +void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp); > +int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp); > +int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp); > +enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp); > +void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp); > + > +/* I2C EDID Chip ID, Slave Address */ > +#define I2C_EDID_DEVICE_ADDR 0x50 > +#define I2C_E_EDID_DEVICE_ADDR 0x30 > + > +/* DPCD_ADDR_MAX_LANE_COUNT */ > +#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) > +#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) > + > +/* DPCD_ADDR_LANE_COUNT_SET */ > +#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f) > + > +/* DPCD_ADDR_TRAINING_LANE0_SET */ > +#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3) > +#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3) > +#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0) > +#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3) > + > +#endif /* _ROCKCHIP_EDP_CORE_H */ > diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.c b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c > new file mode 100644 > index 0000000..f6d641c > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c > @@ -0,0 +1,1202 @@ > +/* > +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > +* Author: > +* Andy yan <andy.yan@rock-chips.com> > +* Jeff chen <jeff.chen@rock-chips.com> > +* > +* based on exynos_dp_reg.c > +* > +* This program is free software; you can redistribute it and/or modify it > +* under the terms of the GNU General Public License as published by the > +* Free Software Foundation; either version 2 of the License, or (at your > +* option) any later version. > +*/ > + > +#include <linux/device.h> > +#include <linux/delay.h> > +#include <linux/io.h> > + > +#include "rockchip_edp_core.h" > +#include "rockchip_edp_reg.h" > + > +void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp, > + bool enable) > +{ > + u32 val; > + > + if (enable) { > + val = readl(edp->regs + VIDEO_CTL_1); > + val |= VIDEO_MUTE; > + writel(val, edp->regs + VIDEO_CTL_1); > + } else { > + val = readl(edp->regs + VIDEO_CTL_1); > + val &= ~VIDEO_MUTE; > + writel(val, edp->regs + VIDEO_CTL_1); > + } > +} > + > +void rockchip_edp_stop_video(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + VIDEO_CTL_1); > + val &= ~VIDEO_EN; > + writel(val, edp->regs + VIDEO_CTL_1); > +} > + > +void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable) > +{ > + u32 val; > + > + if (enable) > + val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 | > + LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3; > + else > + val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | > + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; > + > + writel(val, edp->regs + LANE_MAP); > +} > + > +void rockchip_edp_init_refclk(struct rockchip_edp_device *edp) > +{ > + writel(SEL_24M, edp->regs + ANALOG_CTL_2); > + writel(REF_CLK_24M, edp->regs + PLL_REG_1); > + > + writel(0x95, edp->regs + PLL_REG_2); > + writel(0x40, edp->regs + PLL_REG_3); > + writel(0x58, edp->regs + PLL_REG_4); > + writel(0x22, edp->regs + PLL_REG_5); > + writel(0x19, edp->regs + SSC_REG); > + writel(0x87, edp->regs + TX_REG_COMMON); > + writel(0x03, edp->regs + DP_AUX); > + writel(0x46, edp->regs + DP_BIAS); > + writel(0x55, edp->regs + DP_RESERVE2); > +} > + > +void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp) > +{ > + /* Set interrupt pin assertion polarity as high */ > + writel(INT_POL, edp->regs + INT_CTL); > + > + /* Clear pending valisers */ > + writel(0xff, edp->regs + COMMON_INT_STA_1); > + writel(0x4f, edp->regs + COMMON_INT_STA_2); > + writel(0xff, edp->regs + COMMON_INT_STA_3); > + writel(0x27, edp->regs + COMMON_INT_STA_4); > + > + writel(0x7f, edp->regs + DP_INT_STA); > + > + /* 0:mask,1: unmask */ > + writel(0x00, edp->regs + COMMON_INT_MASK_1); > + writel(0x00, edp->regs + COMMON_INT_MASK_2); > + writel(0x00, edp->regs + COMMON_INT_MASK_3); > + writel(0x00, edp->regs + COMMON_INT_MASK_4); > + writel(0x00, edp->regs + DP_INT_STA_MASK); > +} > + > +void rockchip_edp_reset(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + rockchip_edp_stop_video(edp); > + rockchip_edp_enable_video_mute(edp, 0); > + > + val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N | > + AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N; > + writel(val, edp->regs + FUNC_EN_1); > + > + val = SSC_FUNC_EN_N | AUX_FUNC_EN_N | > + SERDES_FIFO_FUNC_EN_N | > + LS_CLK_DOMAIN_FUNC_EN_N; > + writel(val, edp->regs + FUNC_EN_2); > + > + usleep_range(20, 30); > + > + rockchip_edp_lane_swap(edp, 0); > + > + writel(0x0, edp->regs + SYS_CTL_1); > + writel(0x40, edp->regs + SYS_CTL_2); > + writel(0x0, edp->regs + SYS_CTL_3); > + writel(0x0, edp->regs + SYS_CTL_4); > + > + writel(0x0, edp->regs + PKT_SEND_CTL); > + writel(0x0, edp->regs + HDCP_CTL); > + > + writel(0x5e, edp->regs + HPD_DEGLITCH_L); > + writel(0x1a, edp->regs + HPD_DEGLITCH_H); > + > + writel(0x10, edp->regs + LINK_DEBUG_CTL); > + > + writel(0x0, edp->regs + VIDEO_FIFO_THRD); > + writel(0x20, edp->regs + AUDIO_MARGIN); > + > + writel(0x4, edp->regs + M_VID_GEN_FILTER_TH); > + writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH); > + > + writel(0x0, edp->regs + SOC_GENERAL_CTL); > +} > + > +void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + /* 0: mask, 1: unmask */ > + val = 0; > + writel(val, edp->regs + COMMON_INT_MASK_1); > + > + writel(val, edp->regs + COMMON_INT_MASK_2); > + > + writel(val, edp->regs + COMMON_INT_MASK_3); > + > + writel(val, edp->regs + COMMON_INT_MASK_4); > + > + writel(val, edp->regs + DP_INT_STA_MASK); > +} > + > +u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + DEBUG_CTL); > + > + return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED; > +} > + > +void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp, > + bool enable) > +{ > + u32 val; > + > + if (enable) { > + val = PD_EXP_BG | PD_AUX | PD_PLL | > + PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0; > + writel(val, edp->regs + DP_PWRDN); > + usleep_range(10, 20); > + writel(0x0, edp->regs + DP_PWRDN); > + } else { > + val = PD_EXP_BG | PD_AUX | PD_PLL | > + PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0; > + writel(val, edp->regs + DP_PWRDN); > + } > +} > + > +void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp) > +{ > + u32 val; > + int wt = 0; > + > + rockchip_edp_analog_power_ctr(edp, 1); > + > + val = PLL_LOCK_CHG; > + writel(val, edp->regs + COMMON_INT_STA_1); > + > + val = readl(edp->regs + DEBUG_CTL); > + val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); > + writel(val, edp->regs + DEBUG_CTL); > + > + /* Power up PLL */ > + while (wt < 100) { > + if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) { > + dev_dbg(edp->dev, "edp pll locked\n"); > + break; > + } > + wt++; > + udelay(5); > + } > + > + /* Enable Serdes FIFO function and Link symbol clock domain module */ > + val = readl(edp->regs + FUNC_EN_2); > + val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N > + | AUX_FUNC_EN_N | SSC_FUNC_EN_N); > + writel(val, edp->regs + FUNC_EN_2); > +} > + > +void rockchip_edp_init_hpd(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = HOTPLUG_CHG | HPD_LOST | PLUG; > + writel(val, edp->regs + COMMON_INT_STA_4); > + > + val = INT_HPD; > + writel(val, edp->regs + DP_INT_STA); > + > + val = readl(edp->regs + SYS_CTL_3); > + val |= (F_HPD | HPD_CTRL); > + writel(val, edp->regs + SYS_CTL_3); > +} > + > +void rockchip_edp_reset_aux(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + /* Disable AUX channel module */ > + val = readl(edp->regs + FUNC_EN_2); > + val |= AUX_FUNC_EN_N; > + writel(val, edp->regs + FUNC_EN_2); > +} > + > +void rockchip_edp_init_aux(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + /* Clear inerrupts related to AUX channel */ > + val = RPLY_RECEIV | AUX_ERR; > + writel(val, edp->regs + DP_INT_STA); > + > + rockchip_edp_reset_aux(edp); > + > + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ > + val = DEFER_CTRL_EN | DEFER_COUNT(1); > + writel(val, edp->regs + AUX_CH_DEFER_CTL); > + > + /* Enable AUX channel module */ > + val = readl(edp->regs + FUNC_EN_2); > + val &= ~AUX_FUNC_EN_N; > + writel(val, edp->regs + FUNC_EN_2); > +} > + > +int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + SYS_CTL_3); > + if (val & HPD_STATUS) > + return 0; > + > + return -EINVAL; > +} > + > +void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + FUNC_EN_1); > + val &= ~SW_FUNC_EN_N; > + writel(val, edp->regs + FUNC_EN_1); > +} > + > +int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp) > +{ > + int val; > + int retval = 0; > + int timeout_loop = 0; > + int aux_timeout = 0; > + > + /* Enable AUX CH operation */ > + val = readl(edp->regs + AUX_CH_CTL_2); > + val |= AUX_EN; > + writel(val, edp->regs + AUX_CH_CTL_2); > + > + /* Is AUX CH operation enabled? */ > + val = readl(edp->regs + AUX_CH_CTL_2); > + while (val & AUX_EN) { > + aux_timeout++; > + if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) { > + dev_err(edp->dev, "AUX CH enable timeout!\n"); > + return -ETIMEDOUT; > + } > + val = readl(edp->regs + AUX_CH_CTL_2); > + usleep_range(1000, 2000); > + } > + > + /* Is AUX CH command redply received? */ > + val = readl(edp->regs + DP_INT_STA); > + while (!(val & RPLY_RECEIV)) { > + timeout_loop++; > + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { > + dev_err(edp->dev, "AUX CH command redply failed!\n"); > + return -ETIMEDOUT; > + } > + val = readl(edp->regs + DP_INT_STA); > + usleep_range(10, 20); > + } > + > + /* Clear interrupt source for AUX CH command redply */ > + writel(RPLY_RECEIV, edp->regs + DP_INT_STA); > + > + /* Clear interrupt source for AUX CH access error */ > + val = readl(edp->regs + DP_INT_STA); > + if (val & AUX_ERR) { > + writel(AUX_ERR, edp->regs + DP_INT_STA); > + return -EREMOTEIO; > + } > + > + /* Check AUX CH error access status */ > + val = readl(edp->regs + AUX_CH_STA); > + if ((val & AUX_STATUS_MASK) != 0) { > + dev_err(edp->dev, "AUX CH error happens: %d\n\n", > + val & AUX_STATUS_MASK); > + return -EREMOTEIO; > + } > + > + return retval; > +} > + > +int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp, > + unsigned int val_addr, > + unsigned char data) > +{ > + u32 val; > + int i; > + int retval; > + > + for (i = 0; i < 3; i++) { > + /* Clear AUX CH data buffer */ > + val = BUF_CLR; > + writel(val, edp->regs + BUFFER_DATA_CTL); > + > + /* Select DPCD device address */ > + val = AUX_ADDR_7_0(val_addr); > + writel(val, edp->regs + DP_AUX_ADDR_7_0); > + val = AUX_ADDR_15_8(val_addr); > + writel(val, edp->regs + DP_AUX_ADDR_15_8); > + val = AUX_ADDR_19_16(val_addr); > + writel(val, edp->regs + DP_AUX_ADDR_19_16); > + > + /* Write data buffer */ > + val = (unsigned int)data; > + writel(val, edp->regs + BUF_DATA_0); > + > + /* > + * Set DisplayPort transaction and write 1 byte > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval == 0) > + break; > + > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + } > + > + return retval; > +} > + > +int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp, > + unsigned int val_addr, > + unsigned char *data) > +{ > + u32 val; > + int i; > + int retval; > + > + for (i = 0; i < 10; i++) { > + /* Clear AUX CH data buffer */ > + val = BUF_CLR; > + writel(val, edp->regs + BUFFER_DATA_CTL); > + > + /* Select DPCD device address */ > + val = AUX_ADDR_7_0(val_addr); > + writel(val, edp->regs + DP_AUX_ADDR_7_0); > + val = AUX_ADDR_15_8(val_addr); > + writel(val, edp->regs + DP_AUX_ADDR_15_8); > + val = AUX_ADDR_19_16(val_addr); > + writel(val, edp->regs + DP_AUX_ADDR_19_16); > + > + /* > + * Set DisplayPort transaction and read 1 byte > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval == 0) > + break; > + > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + } > + > + /* Read data buffer */ > + val = readl(edp->regs + BUF_DATA_0); > + *data = (unsigned char)(val & 0xff); > + > + return retval; > +} > + > +int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp, > + unsigned int val_addr, > + unsigned int count, > + unsigned char data[]) > +{ > + u32 val; > + unsigned int start_offset; > + unsigned int cur_data_count; > + unsigned int cur_data_idx; > + int i; > + int retval = 0; > + > + /* Clear AUX CH data buffer */ > + val = BUF_CLR; > + writel(val, edp->regs + BUFFER_DATA_CTL); > + > + start_offset = 0; > + while (start_offset < count) { > + /* Buffer size of AUX CH is 16 * 4bytes */ > + if ((count - start_offset) > 16) > + cur_data_count = 16; > + else > + cur_data_count = count - start_offset; > + > + for (i = 0; i < 10; i++) { > + /* Select DPCD device address */ > + val = AUX_ADDR_7_0(val_addr + start_offset); > + writel(val, edp->regs + DP_AUX_ADDR_7_0); > + val = AUX_ADDR_15_8(val_addr + start_offset); > + writel(val, edp->regs + DP_AUX_ADDR_15_8); > + val = AUX_ADDR_19_16(val_addr + start_offset); > + writel(val, edp->regs + DP_AUX_ADDR_19_16); > + > + for (cur_data_idx = 0; cur_data_idx < cur_data_count; > + cur_data_idx++) { > + val = data[start_offset + cur_data_idx]; > + writel(val, edp->regs + BUF_DATA_0 > + + 4 * cur_data_idx); > + } > + > + /* > + * Set DisplayPort transaction and write > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_LENGTH(cur_data_count) | > + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval == 0) > + break; > + > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + } > + > + start_offset += cur_data_count; > + } > + > + return retval; > +} > + > +int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp, > + unsigned int val_addr, > + unsigned int count, > + unsigned char data[]) > +{ > + u32 val; > + unsigned int start_offset; > + unsigned int cur_data_count; > + unsigned int cur_data_idx; > + int i; > + int retval = 0; > + > + /* Clear AUX CH data buffer */ > + val = BUF_CLR; > + writel(val, edp->regs + BUFFER_DATA_CTL); > + > + start_offset = 0; > + while (start_offset < count) { > + /* Buffer size of AUX CH is 16 * 4bytes */ > + if ((count - start_offset) > 16) > + cur_data_count = 16; > + else > + cur_data_count = count - start_offset; > + > + /* AUX CH Request Transaction process */ > + for (i = 0; i < 10; i++) { > + /* Select DPCD device address */ > + val = AUX_ADDR_7_0(val_addr + start_offset); > + writel(val, edp->regs + DP_AUX_ADDR_7_0); > + val = AUX_ADDR_15_8(val_addr + start_offset); > + writel(val, edp->regs + DP_AUX_ADDR_15_8); > + val = AUX_ADDR_19_16(val_addr + start_offset); > + writel(val, edp->regs + DP_AUX_ADDR_19_16); > + > + /* > + * Set DisplayPort transaction and read > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_LENGTH(cur_data_count) | > + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval == 0) > + break; > + > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + } > + > + for (cur_data_idx = 0; cur_data_idx < cur_data_count; > + cur_data_idx++) { > + val = readl(edp->regs + BUF_DATA_0 > + + 4 * cur_data_idx); > + data[start_offset + cur_data_idx] = > + (unsigned char)val; > + } > + > + start_offset += cur_data_count; > + } > + > + return retval; > +} > + > +int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp, > + unsigned int device_addr, > + unsigned int val_addr) > +{ > + u32 val; > + int retval; > + > + /* Set EDID device address */ > + val = device_addr; > + writel(val, edp->regs + DP_AUX_ADDR_7_0); > + writel(0x0, edp->regs + DP_AUX_ADDR_15_8); > + writel(0x0, edp->regs + DP_AUX_ADDR_19_16); > + > + /* Set offset from base address of EDID device */ > + writel(val_addr, edp->regs + BUF_DATA_0); > + > + /* > + * Set I2C transaction and write address > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | > + AUX_TX_COMM_WRITE; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval != 0) > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + > + return retval; > +} > + > +int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp, > + unsigned int device_addr, > + unsigned int val_addr, > + unsigned int *data) > +{ > + u32 val; > + int i; > + int retval; > + > + for (i = 0; i < 10; i++) { > + /* Clear AUX CH data buffer */ > + val = BUF_CLR; > + writel(val, edp->regs + BUFFER_DATA_CTL); > + > + /* Select EDID device */ > + retval = rockchip_edp_select_i2c_device(edp, > + device_addr, > + val_addr); > + if (retval != 0) { > + dev_err(edp->dev, "Select EDID device fail!\n"); > + continue; > + } > + > + /* > + * Set I2C transaction and read data > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval == 0) > + break; > + > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + } > + > + /* Read data */ > + if (retval == 0) > + *data = readl(edp->regs + BUF_DATA_0); > + > + return retval; > +} > + > +int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp, > + unsigned int device_addr, > + unsigned int val_addr, > + unsigned int count, > + unsigned char edid[]) > +{ > + u32 val; > + unsigned int i, j; > + unsigned int cur_data_idx; > + unsigned int defer = 0; > + int retval = 0; > + > + for (i = 0; i < count; i += 16) { > + for (j = 0; j < 100; j++) { > + /* Clear AUX CH data buffer */ > + val = BUF_CLR; > + writel(val, edp->regs + BUFFER_DATA_CTL); > + > + /* Set normal AUX CH command */ > + val = readl(edp->regs + AUX_CH_CTL_2); > + val &= ~ADDR_ONLY; > + writel(val, edp->regs + AUX_CH_CTL_2); > + > + /* > + * If Rx sends defer, Tx sends only reads > + * request without sending addres > + */ > + if (!defer) > + retval = rockchip_edp_select_i2c_device( > + edp, device_addr, val_addr + i); > + else > + defer = 0; > + > + /* > + * Set I2C transaction and write data > + * If bit 3 is 1, DisplayPort transaction. > + * If Bit 3 is 0, I2C transaction. > + */ > + val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION | > + AUX_TX_COMM_READ; > + writel(val, edp->regs + AUX_CH_CTL_1); > + > + /* Start AUX transaction */ > + retval = rockchip_edp_start_aux_transaction(edp); > + if (retval == 0) > + break; > + > + dev_dbg(edp->dev, "Aux Transaction fail!\n"); > + > + /* Check if Rx sends defer */ > + val = readl(edp->regs + AUX_RX_COMM); > + if (val == AUX_RX_COMM_AUX_DEFER || > + val == AUX_RX_COMM_I2C_DEFER) { > + dev_err(edp->dev, "Defer: %d\n\n", val); > + defer = 1; > + } > + } > + > + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { > + val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx); > + edid[i + cur_data_idx] = (unsigned char)val; > + } > + } > + > + return retval; > +} > + > +void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp, > + u32 bwtype) > +{ > + u32 val; > + > + val = bwtype; > + if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62)) > + writel(val, edp->regs + LINK_BW_SET); > +} > + > +void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp, > + u32 *bwtype) > +{ > + u32 val; > + > + val = readl(edp->regs + LINK_BW_SET); > + *bwtype = val; > +} > + > +void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = HW_LT_EN; > + writel(val, edp->regs + HW_LT_CTL); > +} > + > +int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + DP_INT_STA); > + if (val&HW_LT_DONE) { > + writel(val, edp->regs + DP_INT_STA); > + return 0; > + } > + > + return 1; > +} > + > +int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + HW_LT_CTL); > + > + return (val & HW_LT_ERR_CODE_MASK) >> 4; > +} > + > +void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, u32 count) > +{ > + u32 val; > + > + val = count; > + writel(val, edp->regs + LANE_CNT_SET); > +} > + > +void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, u32 *count) > +{ > + u32 val; > + > + val = readl(edp->regs + LANE_CNT_SET); > + *count = val; > +} > + > +void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp, > + bool enable) > +{ > + u32 val; > + > + if (enable) { > + val = readl(edp->regs + SYS_CTL_4); > + val |= ENHANCED; > + writel(val, edp->regs + SYS_CTL_4); > + } else { > + val = readl(edp->regs + SYS_CTL_4); > + val &= ~ENHANCED; > + writel(val, edp->regs + SYS_CTL_4); > + } > +} > + > +void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp, > + enum pattern_set pattern) > +{ > + u32 val; > + > + switch (pattern) { > + case PRBS7: > + val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; > + writel(val, edp->regs + TRAINING_PTN_SET); > + break; > + case D10_2: > + val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; > + writel(val, edp->regs + TRAINING_PTN_SET); > + break; > + case TRAINING_PTN1: > + val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; > + writel(val, edp->regs + TRAINING_PTN_SET); > + break; > + case TRAINING_PTN2: > + val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; > + writel(val, edp->regs + TRAINING_PTN_SET); > + break; > + case DP_NONE: > + val = SCRAMBLING_ENABLE | > + LINK_QUAL_PATTERN_SET_DISABLE | > + SW_TRAINING_PATTERN_SET_DISABLE; > + writel(val, edp->regs + TRAINING_PTN_SET); > + break; > + default: > + break; > + } > +} > + > +void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level) > +{ > + u32 val; > + > + val = level << PRE_EMPHASIS_SET_SHIFT; > + writel(val, edp->regs + LN0_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level) > +{ > + u32 val; > + > + val = level << PRE_EMPHASIS_SET_SHIFT; > + writel(val, edp->regs + LN1_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level) > +{ > + u32 val; > + > + val = level << PRE_EMPHASIS_SET_SHIFT; > + writel(val, edp->regs + LN2_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp, > + u32 level) > +{ > + u32 val; > + > + val = level << PRE_EMPHASIS_SET_SHIFT; > + writel(val, edp->regs + LN3_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp, > + u32 training_lane) > +{ > + u32 val; > + > + val = training_lane; > + writel(val, edp->regs + LN0_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp, > + u32 training_lane) > +{ > + u32 val; > + > + val = training_lane; > + writel(val, edp->regs + LN1_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp, > + u32 training_lane) > +{ > + u32 val; > + > + val = training_lane; > + writel(val, edp->regs + LN2_LINK_TRAINING_CTL); > +} > + > +void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp, > + u32 training_lane) > +{ > + u32 val; > + > + val = training_lane; > + writel(val, edp->regs + LN3_LINK_TRAINING_CTL); > +} > + > +u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + LN0_LINK_TRAINING_CTL); > + return val; > +} > + > +u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + LN1_LINK_TRAINING_CTL); > + return val; > +} > + > +u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + LN2_LINK_TRAINING_CTL); > + return val; > +} > + > +u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + LN3_LINK_TRAINING_CTL); > + return val; > +} > + > +void rockchip_edp_reset_macro(struct rockchip_edp_device *edp) > +{ > +} > + > +int rockchip_edp_init_video(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; > + writel(val, edp->regs + COMMON_INT_STA_1); > + > + val = 0x0; > + writel(val, edp->regs + SYS_CTL_1); > + > + val = CHA_CRI(4) | CHA_CTRL; > + writel(val, edp->regs + SYS_CTL_2); > + > + val = VID_HRES_TH(2) | VID_VRES_TH(0); > + writel(val, edp->regs + VIDEO_CTL_8); > + > + return 0; > +} > + > +void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp, > + u32 color_dedpth, > + u32 color_space, > + u32 dynamic_range, > + u32 coeff) > +{ > + u32 val; > + > + /* Configure the input color dedpth, color space, dynamic range */ > + val = (dynamic_range << IN_D_RANGE_SHIFT) | > + (color_dedpth << IN_BPC_SHIFT) | > + (color_space << IN_COLOR_F_SHIFT); > + writel(val, edp->regs + VIDEO_CTL_2); > + > + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ > + val = readl(edp->regs + VIDEO_CTL_3); > + val &= ~IN_YC_COEFFI_MASK; > + if (coeff) > + val |= IN_YC_COEFFI_ITU709; > + else > + val |= IN_YC_COEFFI_ITU601; > + writel(val, edp->regs + VIDEO_CTL_3); > +} > + > +int rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + SYS_CTL_1); > + writel(val, edp->regs + SYS_CTL_1); > + > + val = readl(edp->regs + SYS_CTL_1); > + > + if (!(val & DET_STA)) { > + dev_dbg(edp->dev, "Input stream clock not detected.\n"); > + return -EINVAL; > + } > + > + val = readl(edp->regs + SYS_CTL_2); > + writel(val, edp->regs + SYS_CTL_2); > + > + val = readl(edp->regs + SYS_CTL_2); > + if (val & CHA_STA) { > + dev_dbg(edp->dev, "Input stream clk is changing\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp, > + enum clock_recovery_m_value_type type, > + u32 m_value, > + u32 n_value) > +{ > + u32 val; > + > + if (type == REGISTER_M) { > + val = readl(edp->regs + SYS_CTL_4); > + val |= FIX_M_VID; > + writel(val, edp->regs + SYS_CTL_4); > + val = m_value & 0xff; > + writel(val, edp->regs + M_VID_0); > + val = (m_value >> 8) & 0xff; > + writel(val, edp->regs + M_VID_1); > + val = (m_value >> 16) & 0xff; > + writel(val, edp->regs + M_VID_2); > + > + val = n_value & 0xff; > + writel(val, edp->regs + N_VID_0); > + val = (n_value >> 8) & 0xff; > + writel(val, edp->regs + N_VID_1); > + val = (n_value >> 16) & 0xff; > + writel(val, edp->regs + N_VID_2); > + } else { > + val = readl(edp->regs + SYS_CTL_4); > + val &= ~FIX_M_VID; > + writel(val, edp->regs + SYS_CTL_4); > + > + writel(0x00, edp->regs + N_VID_0); > + writel(0x80, edp->regs + N_VID_1); > + writel(0x00, edp->regs + N_VID_2); > + } > +} > + > +void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp, > + u32 type) > +{ > + u32 val; > + > + if (type == VIDEO_TIMING_FROM_CAPTURE) { > + val = readl(edp->regs + VIDEO_CTL_10); > + val &= ~F_SEL; > + writel(val, edp->regs + VIDEO_CTL_10); > + } else { > + val = readl(edp->regs + VIDEO_CTL_10); > + val |= F_SEL; > + writel(val, edp->regs + VIDEO_CTL_10); > + } > +} > + > +int rockchip_edp_bist_cfg(struct rockchip_edp_device *edp) > +{ > + struct video_info *video_info = &edp->video_info; > + struct drm_display_mode *mode = &edp->mode; > + u16 x_total, y_total, x_act; > + u32 val; > + > + x_total = mode->htotal; > + y_total = mode->vtotal; > + x_act = mode->hdisplay; > + > + rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); > + rockchip_edp_set_video_color_format(edp, video_info->color_depth, > + video_info->color_space, > + video_info->dynamic_range, > + video_info->ycbcr_coeff); > + > + val = y_total & 0xff; > + writel(val, edp->regs + TOTAL_LINE_CFG_L); > + val = (y_total >> 8); > + writel(val, edp->regs + TOTAL_LINE_CFG_H); > + val = (mode->vdisplay & 0xff); > + writel(val, edp->regs + ATV_LINE_CFG_L); > + val = (mode->vdisplay >> 8); > + writel(val, edp->regs + ATV_LINE_CFG_H); > + val = (mode->vsync_start - mode->vdisplay); > + writel(val, edp->regs + VF_PORCH_REG); > + val = (mode->vsync_end - mode->vsync_start); > + writel(val, edp->regs + VSYNC_CFG_REG); > + val = (mode->vtotal - mode->vsync_end); > + writel(val, edp->regs + VB_PORCH_REG); > + val = x_total & 0xff; > + writel(val, edp->regs + TOTAL_PIXELL_REG); > + val = x_total >> 8; > + writel(val, edp->regs + TOTAL_PIXELH_REG); > + val = (x_act & 0xff); > + writel(val, edp->regs + ATV_PIXELL_REG); > + val = (x_act >> 8); > + writel(val, edp->regs + ATV_PIXELH_REG); > + val = (mode->hsync_start - mode->hdisplay) & 0xff; > + writel(val, edp->regs + HF_PORCHL_REG); > + val = (mode->hsync_start - mode->hdisplay) >> 8; > + writel(val, edp->regs + HF_PORCHH_REG); > + val = (mode->hsync_end - mode->hsync_start) & 0xff; > + writel(val, edp->regs + HSYNC_CFGL_REG); > + val = (mode->hsync_end - mode->hsync_start) >> 8; > + writel(val, edp->regs + HSYNC_CFGH_REG); > + val = (mode->htotal - mode->hsync_end) & 0xff; > + writel(val, edp->regs + HB_PORCHL_REG); > + val = (mode->htotal - mode->hsync_end) >> 8; > + writel(val, edp->regs + HB_PORCHH_REG); > + > + val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR; > + writel(val, edp->regs + VIDEO_CTL_4); > + > + val = readl(edp->regs + VIDEO_CTL_10); > + val &= ~F_SEL; > + writel(val, edp->regs + VIDEO_CTL_10); > + return 0; > +} > + > +void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp, > + bool enable) > +{ > +} > + > +void rockchip_edp_start_video(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + VIDEO_CTL_1); > + val |= VIDEO_EN; > + writel(val, edp->regs + VIDEO_CTL_1); > +} > + > +int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + SYS_CTL_3); > + writel(val, edp->regs + SYS_CTL_3); > + > + val = readl(edp->regs + SYS_CTL_3); > + if (!(val & STRM_VALID)) { > + dev_dbg(edp->dev, "Input video stream is not detected.\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp, > + struct video_info *video_info) > +{ > + u32 val; > + > + val = readl(edp->regs + FUNC_EN_1); > + val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N); > + writel(val, edp->regs + FUNC_EN_1); > + > + val = readl(edp->regs + VIDEO_CTL_10); > + val &= ~INTERACE_SCAN_CFG; > + val |= (video_info->interlaced << 2); > + writel(val, edp->regs + VIDEO_CTL_10); > + > + val = readl(edp->regs + VIDEO_CTL_10); > + val &= ~VSYNC_POLARITY_CFG; > + val |= (video_info->v_sync_polarity << 1); > + writel(val, edp->regs + VIDEO_CTL_10); > + > + val = readl(edp->regs + VIDEO_CTL_10); > + val &= ~HSYNC_POLARITY_CFG; > + val |= (video_info->h_sync_polarity << 0); > + writel(val, edp->regs + VIDEO_CTL_10); > +} > + > +void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + TRAINING_PTN_SET); > + val &= ~SCRAMBLING_DISABLE; > + writel(val, edp->regs + TRAINING_PTN_SET); > +} > + > +void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = readl(edp->regs + TRAINING_PTN_SET); > + val |= SCRAMBLING_DISABLE; > + writel(val, edp->regs + TRAINING_PTN_SET); > +} > + > +enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + /* Parse hotplug interrupt status register */ > + val = readl(edp->regs + COMMON_INT_STA_4); > + if (val & PLUG) > + return DP_IRQ_TYPE_HP_CABLE_IN; > + > + if (val & HPD_LOST) > + return DP_IRQ_TYPE_HP_CABLE_OUT; > + > + if (val & HOTPLUG_CHG) > + return DP_IRQ_TYPE_HP_CHANGE; > + > + return DP_IRQ_TYPE_UNKNOWN; > +} > + > +void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp) > +{ > + u32 val; > + > + val = HOTPLUG_CHG | HPD_LOST | PLUG; > + writel(val, edp->regs + COMMON_INT_STA_4); > + > + val = INT_HPD; > + writel(val, edp->regs + DP_INT_STA); > +} > diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.h b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h > new file mode 100644 > index 0000000..b50dd47 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h > @@ -0,0 +1,345 @@ > +/* > +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > +* Author: > +* Andy yan <andy.yan@rock-chips.com> > +* Jeff chen <jeff.chen@rock-chips.com> > +* > +* based on exynos_dp_reg.h > +* > +* This program is free software; you can redistribute it and/or modify it > +* under the terms of the GNU General Public License as published by the > +* Free Software Foundation; either version 2 of the License, or (at your > +* option) any later version. > +*/ > + > +#ifndef _ROCKCHIP_EDP_REG_H > +#define _ROCKCHIP_EDP_REG_H > + > +#include <linux/bitops.h> > + > +#define TX_SW_RST 0x14 > +#define FUNC_EN_1 0x18 > +#define FUNC_EN_2 0x1C > +#define VIDEO_CTL_1 0x20 > +#define VIDEO_CTL_2 0x24 > +#define VIDEO_CTL_3 0x28 > +#define VIDEO_CTL_4 0x2c > +#define VIDEO_CTL_8 0x3C > +#define VIDEO_CTL_10 0x44 > +#define TOTAL_LINE_CFG_L 0x48 > +#define TOTAL_LINE_CFG_H 0x4c > +#define ATV_LINE_CFG_L 0x50 > +#define ATV_LINE_CFG_H 0x54 > +#define VF_PORCH_REG 0x58 > +#define VSYNC_CFG_REG 0x5c > +#define VB_PORCH_REG 0x60 > +#define TOTAL_PIXELL_REG 0x64 > +#define TOTAL_PIXELH_REG 0x68 > +#define ATV_PIXELL_REG 0x6c > +#define ATV_PIXELH_REG 0x70 > +#define HF_PORCHL_REG 0x74 > +#define HF_PORCHH_REG 0x78 > +#define HSYNC_CFGL_REG 0x7c > +#define HSYNC_CFGH_REG 0x80 > +#define HB_PORCHL_REG 0x84 > +#define HB_PORCHH_REG 0x88 > +#define PLL_REG_1 0xfc > + > +#define SSC_REG 0x104 > +#define TX_REG_COMMON 0x114 > +#define DP_AUX 0x120 > +#define DP_BIAS 0x124 > +#define DP_PWRDN 0x12c > +#define DP_RESERVE2 0x134 > + > +#define LANE_MAP 0x35C > +#define ANALOG_CTL_2 0x374 > +#define AUX_HW_RETRY_CTL 0x390 > +#define COMMON_INT_STA_1 0x3C4 > +#define COMMON_INT_STA_2 0x3C8 > +#define COMMON_INT_STA_3 0x3CC > +#define COMMON_INT_STA_4 0x3D0 > +#define DP_INT_STA 0x3DC > +#define COMMON_INT_MASK_1 0x3E0 > +#define COMMON_INT_MASK_2 0x3E4 > +#define COMMON_INT_MASK_3 0x3E8 > +#define COMMON_INT_MASK_4 0x3EC > +#define DP_INT_STA_MASK 0x3F8 > + > +#define SYS_CTL_1 0x600 > +#define SYS_CTL_2 0x604 > +#define SYS_CTL_3 0x608 > +#define SYS_CTL_4 0x60C > +#define PKT_SEND_CTL 0x640 > +#define HDCP_CTL 0x648 > +#define LINK_BW_SET 0x680 > +#define LANE_CNT_SET 0x684 > +#define TRAINING_PTN_SET 0x688 > +#define LN0_LINK_TRAINING_CTL 0x68C > +#define LN1_LINK_TRAINING_CTL 0x690 > +#define LN2_LINK_TRAINING_CTL 0x694 > +#define LN3_LINK_TRAINING_CTL 0x698 > +#define HW_LT_CTL 0x6a0 > +#define DEBUG_CTL 0x6C0 > +#define HPD_DEGLITCH_L 0x6C4 > +#define HPD_DEGLITCH_H 0x6C8 > +#define LINK_DEBUG_CTL 0x6E0 > +#define M_VID_0 0x700 > +#define M_VID_1 0x704 > +#define M_VID_2 0x708 > +#define N_VID_0 0x70C > +#define N_VID_1 0x710 > +#define N_VID_2 0x714 > +#define VIDEO_FIFO_THRD 0x730 > +#define AUDIO_MARGIN 0x73C > +#define M_VID_GEN_FILTER_TH 0x764 > +#define M_AUD_GEN_FILTER_TH 0x778 > +#define AUX_CH_STA 0x780 > +#define AUX_CH_DEFER_CTL 0x788 > +#define AUX_RX_COMM 0x78C > +#define BUFFER_DATA_CTL 0x790 > +#define AUX_CH_CTL_1 0x794 > +#define DP_AUX_ADDR_7_0 0x798 > +#define DP_AUX_ADDR_15_8 0x79C > +#define DP_AUX_ADDR_19_16 0x7A0 > +#define AUX_CH_CTL_2 0x7A4 > +#define BUF_DATA_0 0x7C0 > +#define SOC_GENERAL_CTL 0x800 > +#define PLL_REG_2 0x9e4 > +#define PLL_REG_3 0x9e8 > +#define PLL_REG_4 0x9ec > +#define PLL_REG_5 0xa00 > + > +/* ROCKCHIP_EDP_FUNC_EN_1 */ > +#define VID_CAP_FUNC_EN_N BIT(6) > +#define VID_FIFO_FUNC_EN_N BIT(5) > +#define AUD_FIFO_FUNC_EN_N BIT(4) > +#define AUD_FUNC_EN_N BIT(3) > +#define HDCP_FUNC_EN_N BIT(2) > +#define SW_FUNC_EN_N BIT(0) > + > +/* ROCKCHIP_EDP_FUNC_EN_2 */ > +#define SSC_FUNC_EN_N BIT(7) > +#define AUX_FUNC_EN_N BIT(2) > +#define SERDES_FIFO_FUNC_EN_N BIT(1) > +#define LS_CLK_DOMAIN_FUNC_EN_N BIT(0) > + > +/* ROCKCHIP_EDP_VIDEO_CTL_1 */ > +#define VIDEO_EN BIT(7) > +#define VIDEO_MUTE BIT(6) > + > +/* ROCKCHIP_EDP_VIDEO_CTL_1 */ > +#define IN_D_RANGE_MASK (0x1 << 7) > +#define IN_D_RANGE_SHIFT (7) > +#define IN_D_RANGE_CEA (0x1 << 7) > +#define IN_D_RANGE_VESA (0x0 << 7) > +#define IN_BPC_MASK (0x7 << 4) > +#define IN_BPC_SHIFT (4) > +#define IN_BPC_12_BITS (0x3 << 4) > +#define IN_BPC_10_BITS (0x2 << 4) > +#define IN_BPC_8_BITS (0x1 << 4) > +#define IN_BPC_6_BITS (0x0 << 4) > +#define IN_COLOR_F_MASK (0x3 << 0) > +#define IN_COLOR_F_SHIFT (0) > +#define IN_COLOR_F_YCBCR444 (0x2 << 0) > +#define IN_COLOR_F_YCBCR422 (0x1 << 0) > +#define IN_COLOR_F_RGB (0x0 << 0) > + > +/* ROCKCHIP_EDP_VIDEO_CTL_3 */ > +#define IN_YC_COEFFI_MASK (0x1 << 7) > +#define IN_YC_COEFFI_SHIFT (7) > +#define IN_YC_COEFFI_ITU709 (0x1 << 7) > +#define IN_YC_COEFFI_ITU601 (0x0 << 7) > +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) > +#define VID_CHK_UPDATE_TYPE_SHIFT (4) > +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) > +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) > + > +/* ROCKCHIP_EDP_VIDEO_CTL_4 */ > +#define BIST_EN (0x1 << 3) > +#define BIST_WH_64 (0x1 << 2) > +#define BIST_WH_32 (0x0 << 2) > +#define BIST_TYPE_COLR_BAR (0x0 << 0) > +#define BIST_TYPE_GRAY_BAR (0x1 << 0) > +#define BIST_TYPE_MOBILE_BAR (0x2 << 0) > + > +/* ROCKCHIP_EDP_VIDEO_CTL_8 */ > +#define VID_HRES_TH(x) (((x) & 0xf) << 4) > +#define VID_VRES_TH(x) (((x) & 0xf) << 0) > + > +/* ROCKCHIP_EDP_VIDEO_CTL_10 */ > +#define F_SEL (0x1 << 4) > +#define INTERACE_SCAN_CFG (0x1 << 2) > +#define VSYNC_POLARITY_CFG (0x1 << 1) > +#define HSYNC_POLARITY_CFG (0x1 << 0) > + > +/* ROCKCHIP_EDP_PLL_REG_1 */ > +#define REF_CLK_24M (0x1 << 1) > +#define REF_CLK_27M (0x0 << 1) > + > +/* ROCKCHIP_EDP_DP_PWRDN */ > +#define PD_INC_BG BIT(7) > +#define PD_EXP_BG BIT(6) > +#define PD_AUX BIT(5) > +#define PD_PLL BIT(4) > +#define PD_CH3 BIT(3) > +#define PD_CH2 BIT(2) > +#define PD_CH1 BIT(1) > +#define PD_CH0 BIT(0) > + > +/* ROCKCHIP_EDP_LANE_MAP */ > +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) > +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) > +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) > +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) > +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) > +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) > +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) > +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) > +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) > +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) > +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) > +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) > +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) > +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) > +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) > +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) > + > +/* ROCKCHIP_EDP_ANALOG_CTL_2 */ > +#define SEL_24M (0x1 << 3) > + > +/* ROCKCHIP_EDP_COMMON_INT_STA_1 */ > +#define VSYNC_DET BIT(7) > +#define PLL_LOCK_CHG BIT(6) > +#define SPDIF_ERR BIT(5) > +#define SPDIF_UNSTBL BIT(4) > +#define VID_FORMAT_CHG BIT(3) > +#define AUD_CLK_CHG BIT(2) > +#define VID_CLK_CHG BIT(1) > +#define SW_INT BIT(0) > + > +/* ROCKCHIP_EDP_COMMON_INT_STA_2 */ > +#define ENC_EN_CHG BIT(6) > +#define HW_BKSV_RDY BIT(3) > +#define HW_SHA_DONE BIT(2) > +#define HW_AUTH_STATE_CHG BIT(1) > +#define HW_AUTH_DONE BIT(0) > + > +/* ROCKCHIP_EDP_COMMON_INT_STA_3 */ > +#define AFIFO_UNDER BIT(7) > +#define AFIFO_OVER BIT(6) > +#define R0_CHK_FLAG BIT(5) > + > +/* ROCKCHIP_EDP_COMMON_INT_STA_4 */ > +#define PSR_ACTIVE BIT(7) > +#define PSR_INACTIVE BIT(6) > +#define SPDIF_BI_PHASE_ERR BIT(5) > +#define HOTPLUG_CHG BIT(2) > +#define HPD_LOST BIT(1) > +#define PLUG BIT(0) > + > +/* ROCKCHIP_EDP_INT_STA */ > +#define INT_HPD BIT(6) > +#define HW_LT_DONE BIT(5) > +#define SINK_LOST BIT(3) > +#define LINK_LOST BIT(2) > +#define RPLY_RECEIV BIT(1) > +#define AUX_ERR BIT(0) > + > +/* ROCKCHIP_EDP_INT_CTL */ > +#define INT_CTL 0x3FC > +#define SOFT_INT_CTRL BIT(2) > +#define INT_POL BIT(0) > + > +/* ROCKCHIP_EDP_SYS_CTL_1 */ > +#define DET_STA BIT(2) > +#define FORCE_DET BIT(1) > +#define DET_CTRL BIT(0) > + > +/* ROCKCHIP_EDP_SYS_CTL_2 */ > +#define CHA_CRI(x) (((x) & 0xf) << 4) > +#define CHA_STA BIT(2) > +#define FORCE_CHA BIT(1) > +#define CHA_CTRL BIT(0) > + > +/* ROCKCHIP_EDP_SYS_CTL_3 */ > +#define HPD_STATUS BIT(6) > +#define F_HPD BIT(5) > +#define HPD_CTRL BIT(4) > +#define HDCP_RDY BIT(3) > +#define STRM_VALID BIT(2) > +#define F_VALID BIT(1) > +#define VALID_CTRL BIT(0) > + > +/* ROCKCHIP_EDP_SYS_CTL_4 */ > +#define FIX_M_AUD BIT(4) > +#define ENHANCED BIT(3) > +#define FIX_M_VID BIT(2) > +#define M_VID_UPDATE_CTRL BIT(0) > + > +/* ROCKCHIP_EDP_TRAINING_PTN_SET */ > +#define SCRAMBLING_DISABLE (0x1 << 5) > +#define SCRAMBLING_ENABLE (0x0 << 5) > +#define LINK_QUAL_PATTERN_SET_MASK (0x7 << 2) > +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) > +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) > +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) > +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) > +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) > +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) > +#define SW_TRAINING_PATTERN_SET_DISABLE (0x0 << 0) > + > +/* ROCKCHIP_EDP_HW_LT_CTL */ > +#define HW_LT_ERR_CODE_MASK 0x70 > +#define HW_LT_EN BIT(0) > + > +/* ROCKCHIP_EDP_LN0_LINK_TRAINING_CTL */ > +#define PRE_EMPHASIS_SET_MASK (0x3 << 3) > +#define PRE_EMPHASIS_SET_SHIFT (3) > + > +/* ROCKCHIP_EDP_DEBUG_CTL */ > +#define PLL_LOCK BIT(4) > +#define F_PLL_LOCK BIT(3) > +#define PLL_LOCK_CTRL BIT(2) > +#define POLL_EN BIT(1) > +#define PN_INV BIT(0) > + > +/* ROCKCHIP_EDP_AUX_CH_STA */ > +#define AUX_BUSY (0x1 << 4) > +#define AUX_STATUS_MASK (0xf << 0) > + > +/* ROCKCHIP_EDP_AUX_CH_DEFER_CTL */ > +#define DEFER_CTRL_EN (0x1 << 7) > +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) > + > +/* ROCKCHIP_EDP_AUX_RX_COMM */ > +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) > +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) > + > +/* ROCKCHIP_EDP_BUFFER_DATA_CTL */ > +#define BUF_CLR (0x1 << 7) > +#define BUF_DATA_COUNT(x) (((x) & 0xf) << 0) > + > +/* ROCKCHIP_EDP_AUX_CH_CTL_1 */ > +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) > +#define AUX_TX_COMM_MASK (0xf << 0) > +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) > +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) > +#define AUX_TX_COMM_MOT (0x1 << 2) > +#define AUX_TX_COMM_WRITE (0x0 << 0) > +#define AUX_TX_COMM_READ (0x1 << 0) > + > +/* OCKCHIP_EDP_AUX_ADDR_7_0 */ > +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) > + > +/* ROCKCHIP_EDP_AUX_ADDR_15_8 */ > +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) > + > +/* ROCKCHIP_EDP_AUX_ADDR_19_16 */ > +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) > + > +/* ROCKCHIP_EDP_AUX_CH_CTL_2 */ > +#define ADDR_ONLY BIT(1) > +#define AUX_EN BIT(0) > + > +#endif /* _ROCKCHIP_EDP_REG_H */ > -- > 1.7.9.5 > >
On Tuesday, September 23, 2014 03:20 AM, Rob Clark wrote: > On Mon, Sep 22, 2014 at 7:02 AM, Mark yao <mark.yao@rock-chips.com> wrote: >> This adds support for Rockchip soc edp found on rk3288 >> >> Signed-off-by: Mark Yao <mark.yao@rock-chips.com> >> Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com> >> --- >> Changes in v2: >> - fix code sytle >> - use some define from drm_dp_helper.h >> - use panel-simple driver for primary display. >> - remove unnecessary clock clk_24m_parent. >> >> Changes in v3: None >> >> Changes in v4: None >> >> drivers/gpu/drm/rockchip/Kconfig | 9 + >> drivers/gpu/drm/rockchip/Makefile | 2 + >> drivers/gpu/drm/rockchip/rockchip_edp_core.c | 853 ++++++++++++++++++ >> drivers/gpu/drm/rockchip/rockchip_edp_core.h | 309 +++++++ >> drivers/gpu/drm/rockchip/rockchip_edp_reg.c | 1202 ++++++++++++++++++++++++++ >> drivers/gpu/drm/rockchip/rockchip_edp_reg.h | 345 ++++++++ >> 6 files changed, 2720 insertions(+) >> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c >> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h >> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c >> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h >> >> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig >> index 7146c80..04b1f8c 100644 >> --- a/drivers/gpu/drm/rockchip/Kconfig >> +++ b/drivers/gpu/drm/rockchip/Kconfig >> @@ -17,3 +17,12 @@ config DRM_ROCKCHIP >> management to userspace. This driver does not provides >> 2D or 3D acceleration; acceleration is performed by other >> IP found on the SoC. >> + >> +config ROCKCHIP_EDP >> + bool "Rockchip edp support" >> + depends on DRM_ROCKCHIP >> + help >> + Choose this option if you have a Rockchip eDP. >> + Rockchip rk3288 SoC has eDP TX Controller can be used. >> + If you have an Embedded DisplayPort Panel, say Y to enable its >> + driver. >> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile >> index 6e6d468..a0fc3a1 100644 >> --- a/drivers/gpu/drm/rockchip/Makefile >> +++ b/drivers/gpu/drm/rockchip/Makefile >> @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip >> rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ >> rockchip_drm_gem.o rockchip_drm_vop.o >> >> +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o rockchip_edp_reg.o >> + >> obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o >> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >> new file mode 100644 >> index 0000000..5450d1fa >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >> @@ -0,0 +1,853 @@ >> +/* >> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> +* Author: >> +* Andy yan <andy.yan@rock-chips.com> >> +* Jeff chen <jeff.chen@rock-chips.com> >> +* >> +* based on exynos_dp_core.c >> +* > hmm, did you look at all at drm_dp_helpers? The exynos code probably > pre-dates the helpers, so might not be the best example to work off > of.. > > If there is actually a valid reason not to use the dp-helpers, then > you should mention the reasons, at least in the commit msg if not the > code > > BR, > -R Thanks Rob,Because RK3288 eDP controller IP design is similar to exynos.They from same IP vendors but have some difference. So we choosed exynos_dp as example to work off of.exynos_dp only used some defines from drm_dp_helper.h like DPCD. > >> +* This program is free software; you can redistribute it and/or modify it >> +* under the terms of the GNU General Public License as published by the >> +* Free Software Foundation; either version 2 of the License, or (at your >> +* option) any later version. >> +*/ >> + >> +#include <drm/drmP.h> >> +#include <drm/drm_crtc_helper.h> >> +#include <drm/drm_panel.h> >> +#include <drm/drm_of.h> >> + >> +#include <linux/component.h> >> +#include <linux/clk.h> >> +#include <linux/mfd/syscon.h> >> +#include <linux/regmap.h> >> +#include <linux/reset.h> >> + >> +#include <video/of_videomode.h> >> +#include <video/videomode.h> >> + >> +#include "rockchip_edp_core.h" >> + >> +#define connector_to_edp(c) \ >> + container_of(c, struct rockchip_edp_device, connector) >> + >> +#define encoder_to_edp(c) \ >> + container_of(c, struct rockchip_edp_device, encoder) >> + >> +static struct rockchip_edp_soc_data soc_data[2] = { >> + /* rk3288 */ >> + {.grf_soc_con6 = 0x025c, >> + .grf_soc_con12 = 0x0274}, >> + /* no edp switching needed */ >> + {.grf_soc_con6 = -1, >> + .grf_soc_con12 = -1}, >> +}; >> + >> +static const struct of_device_id rockchip_edp_dt_ids[] = { >> + {.compatible = "rockchip,rk3288-edp", >> + .data = (void *)&soc_data[0] }, >> + {} >> +}; >> + >> +MODULE_DEVICE_TABLE(of, rockchip_edp_dt_ids); >> + >> +static int rockchip_edp_clk_enable(struct rockchip_edp_device *edp) >> +{ >> + int ret = 0; >> + >> + if (!edp->clk_on) { >> + ret = clk_prepare_enable(edp->pclk); >> + if (ret < 0) { >> + dev_err(edp->dev, "cannot enable edp pclk %d\n", ret); >> + goto err_pclk; >> + } >> + >> + ret = clk_prepare_enable(edp->clk_edp); >> + if (ret < 0) { >> + dev_err(edp->dev, "cannot enable clk_edp %d\n", ret); >> + goto err_clk_edp; >> + } >> + >> + ret = clk_set_rate(edp->clk_24m, 24000000); >> + if (ret < 0) { >> + dev_err(edp->dev, "cannot set edp clk_24m %d\n", >> + ret); >> + goto err_clk_24m; >> + } >> + >> + ret = clk_prepare_enable(edp->clk_24m); >> + if (ret < 0) { >> + dev_err(edp->dev, "cannot enable edp clk_24m %d\n", >> + ret); >> + goto err_clk_24m; >> + } >> + >> + edp->clk_on = true; >> + } >> + >> + return 0; >> + >> +err_clk_24m: >> + clk_disable_unprepare(edp->clk_edp); >> +err_clk_edp: >> + clk_disable_unprepare(edp->pclk); >> +err_pclk: >> + edp->clk_on = false; >> + >> + return ret; >> +} >> + >> +static int rockchip_edp_clk_disable(struct rockchip_edp_device *edp) >> +{ >> + if (edp->clk_on) { >> + clk_disable_unprepare(edp->pclk); >> + clk_disable_unprepare(edp->clk_edp); >> + clk_disable_unprepare(edp->clk_24m); >> + edp->clk_on = false; >> + } >> + >> + return 0; >> +} >> + >> +static int rockchip_edp_pre_init(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + int ret; >> + >> + val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16); >> + ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val); >> + if (ret != 0) { >> + dev_err(edp->dev, "Could not write to GRF: %d\n", ret); >> + return ret; >> + } >> + >> + reset_control_assert(edp->rst); >> + usleep_range(10, 20); >> + reset_control_deassert(edp->rst); >> + >> + return 0; >> +} >> + >> +static int rockchip_edp_init_edp(struct rockchip_edp_device *edp) >> +{ >> + rockchip_edp_reset(edp); >> + rockchip_edp_init_refclk(edp); >> + rockchip_edp_init_interrupt(edp); >> + rockchip_edp_enable_sw_function(edp); >> + rockchip_edp_init_analog_func(edp); >> + rockchip_edp_init_hpd(edp); >> + rockchip_edp_init_aux(edp); >> + >> + return 0; >> +} >> + >> +static int rockchip_edp_get_max_rx_bandwidth( >> + struct rockchip_edp_device *edp, >> + u8 *bandwidth) >> +{ >> + u8 data; >> + int retval; >> + >> + /* >> + * For DP rev.1.1, Maximum link rate of Main Link lanes >> + * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps >> + */ >> + retval = rockchip_edp_read_byte_from_dpcd( >> + edp, DP_MAX_LINK_RATE, &data); >> + if (retval < 0) >> + *bandwidth = 0; >> + else >> + *bandwidth = data; >> + >> + return retval; >> +} >> + >> +static int rockchip_edp_get_max_rx_lane_count(struct rockchip_edp_device *edp, >> + u8 *lane_count) >> +{ >> + u8 data; >> + int retval; >> + >> + /* >> + * For DP rev.1.1, Maximum number of Main Link lanes >> + * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes >> + */ >> + retval = rockchip_edp_read_byte_from_dpcd( >> + edp, DP_MAX_LANE_COUNT, &data); >> + if (retval < 0) >> + *lane_count = 0; >> + else >> + *lane_count = DPCD_MAX_LANE_COUNT(data); >> + >> + return retval; >> +} >> + >> +static int rockchip_edp_init_training(struct rockchip_edp_device *edp) >> +{ >> + int retval; >> + >> + /* >> + * MACRO_RST must be applied after the PLL_LOCK to avoid >> + * the DP inter pair skew issue for at least 10 us >> + */ >> + rockchip_edp_reset_macro(edp); >> + >> + retval = rockchip_edp_get_max_rx_bandwidth( >> + edp, &edp->link_train.link_rate); >> + retval = rockchip_edp_get_max_rx_lane_count( >> + edp, &edp->link_train.lane_count); >> + dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n", >> + edp->link_train.link_rate * 27 / 100, >> + edp->link_train.link_rate * 27 % 100, >> + edp->link_train.lane_count); >> + >> + if ((edp->link_train.link_rate != DP_LINK_BW_1_62) && >> + (edp->link_train.link_rate != DP_LINK_BW_2_7)) { >> + dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n" >> + "use default link rate:%d.%dGps\n", >> + edp->link_train.link_rate, >> + edp->video_info.link_rate * 27 / 100, >> + edp->video_info.link_rate * 27 % 100); >> + edp->link_train.link_rate = edp->video_info.link_rate; >> + } >> + >> + if (edp->link_train.lane_count == 0) { >> + dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n" >> + "use default lanes:%d\n", >> + edp->link_train.lane_count, >> + edp->video_info.lane_count); >> + edp->link_train.lane_count = edp->video_info.lane_count; >> + } >> + >> + rockchip_edp_analog_power_ctr(edp, 1); >> + >> + return 0; >> +} >> + >> +static int rockchip_edp_hw_link_training(struct rockchip_edp_device *edp) >> +{ >> + u32 cnt = 50; >> + u32 val; >> + >> + /* Set link rate and count as you want to establish*/ >> + rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate); >> + rockchip_edp_set_lane_count(edp, edp->link_train.lane_count); >> + rockchip_edp_hw_link_training_en(edp); >> + val = rockchip_edp_wait_hw_lt_done(edp); >> + while (val) { >> + if (cnt-- <= 0) { >> + dev_err(edp->dev, "hw lt timeout"); >> + return -ETIMEDOUT; >> + } >> + mdelay(1); >> + val = rockchip_edp_wait_hw_lt_done(edp); >> + } >> + >> + val = rockchip_edp_get_hw_lt_status(edp); >> + if (val) >> + dev_err(edp->dev, "hw lt err:%d\n", val); >> + >> + return val; >> +} >> + >> +static int rockchip_edp_set_link_train(struct rockchip_edp_device *edp) >> +{ >> + int retval; >> + >> + rockchip_edp_init_training(edp); >> + >> + retval = rockchip_edp_hw_link_training(edp); >> + if (retval < 0) >> + dev_err(edp->dev, "DP hw LT failed!\n"); >> + >> + return retval; >> +} >> + >> +static int rockchip_edp_config_video(struct rockchip_edp_device *edp, >> + struct video_info *video_info) >> +{ >> + int retval = 0; >> + int timeout_loop = 0; >> + int done_count = 0; >> + >> + rockchip_edp_config_video_slave_mode(edp, video_info); >> + >> + rockchip_edp_set_video_color_format(edp, video_info->color_depth, >> + video_info->color_space, >> + video_info->dynamic_range, >> + video_info->ycbcr_coeff); >> + >> + if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) { >> + dev_err(edp->dev, "PLL is not locked yet.\n"); >> + return -EINVAL; >> + } >> + >> + for (;;) { >> + timeout_loop++; >> + if (rockchip_edp_is_slave_video_stream_clock_on(edp) == 0) >> + break; >> + >> + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { >> + dev_err(edp->dev, "Timeout of video streamclk ok\n"); >> + return -ETIMEDOUT; >> + } >> + >> + udelay(1); >> + } >> + >> + /* Set to use the register calculated M/N video */ >> + rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); >> + >> + /* Disable video mute */ >> + rockchip_edp_enable_video_mute(edp, 0); >> + >> + /* Configure video slave mode */ >> + rockchip_edp_enable_video_master(edp, 0); >> + >> + /* Enable video */ >> + rockchip_edp_start_video(edp); >> + >> + timeout_loop = 0; >> + >> + for (;;) { >> + timeout_loop++; >> + if (rockchip_edp_is_video_stream_on(edp) == 0) { >> + done_count++; >> + if (done_count > 10) >> + break; >> + } else if (done_count) { >> + done_count = 0; >> + } >> + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { >> + dev_err(edp->dev, "Timeout of video streamclk ok\n"); >> + return -ETIMEDOUT; >> + } >> + >> + mdelay(1); >> + } >> + >> + if (retval != 0) >> + dev_err(edp->dev, "Video stream is not detected!\n"); >> + >> + return retval; >> +} >> + >> +static irqreturn_t rockchip_edp_isr(int irq, void *arg) >> +{ >> + struct rockchip_edp_device *edp = arg; >> + enum dp_irq_type irq_type; >> + >> + irq_type = rockchip_edp_get_irq_type(edp); >> + switch (irq_type) { >> + case DP_IRQ_TYPE_HP_CABLE_IN: >> + dev_dbg(edp->dev, "Received irq - cable in\n"); >> + rockchip_edp_clear_hotplug_interrupts(edp); >> + break; >> + case DP_IRQ_TYPE_HP_CABLE_OUT: >> + dev_dbg(edp->dev, "Received irq - cable out\n"); >> + rockchip_edp_clear_hotplug_interrupts(edp); >> + break; >> + case DP_IRQ_TYPE_HP_CHANGE: >> + /* >> + * We get these change notifications once in a while, but there >> + * is nothing we can do with them. Just ignore it for now and >> + * only handle cable changes. >> + */ >> + dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n"); >> + rockchip_edp_clear_hotplug_interrupts(edp); >> + break; >> + default: >> + dev_err(edp->dev, "Received irq - unknown type[%x]!\n", >> + irq_type); >> + rockchip_edp_clear_hotplug_interrupts(edp); >> + break; >> + } >> + >> + return IRQ_HANDLED; >> +} >> + >> +static void rockchip_edp_commit(struct drm_encoder *encoder) >> +{ >> + struct rockchip_edp_device *edp = encoder_to_edp(encoder); >> + int ret; >> + >> + ret = rockchip_edp_set_link_train(edp); >> + if (ret) >> + dev_err(edp->dev, "link train failed!\n"); >> + else >> + dev_dbg(edp->dev, "link training success.\n"); >> + >> + rockchip_edp_set_lane_count(edp, edp->link_train.lane_count); >> + rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate); >> + rockchip_edp_init_video(edp); >> + >> + ret = rockchip_edp_config_video(edp, &edp->video_info); >> + if (ret) >> + dev_err(edp->dev, "unable to config video\n"); >> +} >> + >> +static void rockchip_edp_poweron(struct drm_encoder *encoder) >> +{ >> + struct rockchip_edp_device *edp = encoder_to_edp(encoder); >> + int ret; >> + >> + if (edp->dpms_mode == DRM_MODE_DPMS_ON) >> + return; >> + >> + if (edp->panel) >> + edp->panel->funcs->enable(edp->panel); >> + >> + ret = rockchip_edp_clk_enable(edp); >> + if (ret < 0) { >> + dev_err(edp->dev, "cannot enable edp clk %d\n", ret); >> + return; >> + } >> + >> + ret = rockchip_edp_pre_init(edp); >> + if (ret < 0) { >> + dev_err(edp->dev, "edp pre init fail %d\n", ret); >> + return; >> + } >> + >> + ret = rockchip_edp_init_edp(edp); >> + if (ret < 0) { >> + dev_err(edp->dev, "edp init fail %d\n", ret); >> + return; >> + } >> + >> + enable_irq(edp->irq); >> + rockchip_edp_commit(encoder); >> +} >> + >> +static void rockchip_edp_poweroff(struct drm_encoder *encoder) >> +{ >> + struct rockchip_edp_device *edp = encoder_to_edp(encoder); >> + >> + if (edp->dpms_mode == DRM_MODE_DPMS_OFF) >> + return; >> + >> + disable_irq(edp->irq); >> + rockchip_edp_reset(edp); >> + rockchip_edp_analog_power_ctr(edp, 0); >> + rockchip_edp_clk_disable(edp); >> + if (edp->panel) >> + edp->panel->funcs->disable(edp->panel); >> +} >> + >> +static enum drm_connector_status >> +rockchip_connector_detect(struct drm_connector *connector, bool force) >> +{ >> + return connector_status_connected; >> +} >> + >> +static void rockchip_connector_destroy(struct drm_connector *connector) >> +{ >> + drm_sysfs_connector_remove(connector); >> + drm_connector_cleanup(connector); >> +} >> + >> +static struct drm_connector_funcs rockchip_connector_funcs = { >> + .dpms = drm_helper_connector_dpms, >> + .detect = rockchip_connector_detect, >> + .fill_modes = drm_helper_probe_single_connector_modes, >> + .destroy = rockchip_connector_destroy, >> +}; >> + >> +static int rockchip_connector_get_modes(struct drm_connector *connector) >> +{ >> + struct rockchip_edp_device *edp = connector_to_edp(connector); >> + struct drm_panel *panel = edp->panel; >> + >> + return panel->funcs->get_modes(panel); >> +} >> + >> +static struct drm_encoder * >> + rockchip_connector_best_encoder(struct drm_connector *connector) >> +{ >> + struct rockchip_edp_device *edp = connector_to_edp(connector); >> + >> + return &edp->encoder; >> +} >> + >> +static enum drm_mode_status rockchip_connector_mode_valid( >> + struct drm_connector *connector, >> + struct drm_display_mode *mode) >> +{ >> + /* TODO(rk): verify that the mode is really valid */ >> + return MODE_OK; >> +} >> + >> +static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = { >> + .get_modes = rockchip_connector_get_modes, >> + .mode_valid = rockchip_connector_mode_valid, >> + .best_encoder = rockchip_connector_best_encoder, >> +}; >> + >> +static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode) >> +{ >> + struct rockchip_edp_device *edp = encoder_to_edp(encoder); >> + >> + if (edp->dpms_mode == mode) >> + return; >> + >> + switch (mode) { >> + case DRM_MODE_DPMS_ON: >> + rockchip_edp_poweron(encoder); >> + break; >> + case DRM_MODE_DPMS_STANDBY: >> + case DRM_MODE_DPMS_SUSPEND: >> + case DRM_MODE_DPMS_OFF: >> + rockchip_edp_poweroff(encoder); >> + break; >> + default: >> + break; >> + } >> + >> + edp->dpms_mode = mode; >> +} >> + >> +static bool >> +rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder, >> + const struct drm_display_mode *mode, >> + struct drm_display_mode *adjusted_mode) >> +{ >> + if (!adjusted_mode->private) { >> + struct rockchip_display_mode *priv_mode; >> + >> + priv_mode = kzalloc(sizeof(*priv_mode), GFP_KERNEL); >> + priv_mode->out_type = ROCKCHIP_DISPLAY_TYPE_EDP; >> + adjusted_mode->private = (int *)priv_mode; >> + } >> + >> + return true; >> +} >> + >> +static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder, >> + struct drm_display_mode *mode, >> + struct drm_display_mode *adjusted) >> +{ >> + struct rockchip_edp_device *edp = encoder_to_edp(encoder); >> + u32 val; >> + int ret; >> + >> + ret = rockchip_drm_encoder_get_mux_id(edp->dev->of_node, encoder); >> + if (ret < 0) >> + return; >> + >> + if (ret == ROCKCHIP_CRTC_VOPL) >> + val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16); >> + else >> + val = EDP_SEL_VOP_LIT << 16; >> + >> + dev_info(edp->dev, "vop %s output to edp\n", >> + (ret == ROCKCHIP_CRTC_VOPL) ? "LIT" : "BIG"); >> + ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val); >> + if (ret != 0) { >> + dev_err(edp->dev, "Could not write to GRF: %d\n", ret); >> + return; >> + } >> + >> + memcpy(&edp->mode, adjusted, sizeof(*mode)); >> +} >> + >> +static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder) >> +{ >> +} >> + >> +static void rockchip_drm_encoder_commit(struct drm_encoder *encoder) >> +{ >> + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON); >> +} >> + >> +static void rockchip_drm_encoder_disable(struct drm_encoder *encoder) >> +{ >> + struct drm_plane *plane; >> + struct drm_device *dev = encoder->dev; >> + >> + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); >> + >> + /* all planes connected to this encoder should be also disabled. */ >> + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { >> + if (plane->crtc && (plane->crtc == encoder->crtc)) >> + plane->funcs->disable_plane(plane); >> + } >> +} >> + >> +static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = { >> + .dpms = rockchip_drm_encoder_dpms, >> + .mode_fixup = rockchip_drm_encoder_mode_fixup, >> + .mode_set = rockchip_drm_encoder_mode_set, >> + .prepare = rockchip_drm_encoder_prepare, >> + .commit = rockchip_drm_encoder_commit, >> + .disable = rockchip_drm_encoder_disable, >> +}; >> + >> +static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder) >> +{ >> + drm_encoder_cleanup(encoder); >> +} >> + >> +static struct drm_encoder_funcs rockchip_encoder_funcs = { >> + .destroy = rockchip_drm_encoder_destroy, >> +}; >> + >> +static int rockchip_edp_init(struct rockchip_edp_device *edp) >> +{ >> + struct device *dev = edp->dev; >> + struct device_node *np = dev->of_node; >> + struct platform_device *pdev = to_platform_device(dev); >> + struct resource *res; >> + const struct of_device_id *match; >> + int ret; >> + >> + if (!np) { >> + dev_err(dev, "Missing device tree node.\n"); >> + return -EINVAL; >> + } >> + >> + match = of_match_node(rockchip_edp_dt_ids, np); >> + edp->soc_data = (struct rockchip_edp_soc_data *)match->data; >> + /* >> + * The control bit is located in the GRF register space. >> + */ >> + if (edp->soc_data->grf_soc_con6 >= 0) { >> + edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); >> + if (IS_ERR(edp->grf)) { >> + dev_err(dev, >> + "rk3288-edp needs rockchip,grf property\n"); >> + return PTR_ERR(edp->grf); >> + } >> + } >> + >> + edp->video_info.h_sync_polarity = 0; >> + edp->video_info.v_sync_polarity = 0; >> + edp->video_info.interlaced = 0; >> + edp->video_info.color_space = CS_RGB; >> + edp->video_info.dynamic_range = VESA; >> + edp->video_info.ycbcr_coeff = COLOR_YCBCR601; >> + edp->video_info.color_depth = COLOR_8; >> + >> + edp->video_info.link_rate = DP_LINK_BW_1_62; >> + edp->video_info.lane_count = LANE_CNT4; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + edp->regs = devm_ioremap_resource(dev, res); >> + if (IS_ERR(edp->regs)) { >> + dev_err(dev, "ioremap reg failed\n"); >> + return PTR_ERR(edp->regs); >> + } >> + >> + edp->clk_edp = devm_clk_get(dev, "clk_edp"); >> + if (IS_ERR(edp->clk_edp)) { >> + dev_err(dev, "cannot get clk_edp\n"); >> + return PTR_ERR(edp->clk_edp); >> + } >> + >> + edp->clk_24m = devm_clk_get(dev, "clk_edp_24m"); >> + if (IS_ERR(edp->clk_24m)) { >> + dev_err(dev, "cannot get clk_edp_24m\n"); >> + return PTR_ERR(edp->clk_24m); >> + } >> + >> + edp->pclk = devm_clk_get(dev, "pclk_edp"); >> + if (IS_ERR(edp->pclk)) { >> + dev_err(dev, "cannot get pclk\n"); >> + return PTR_ERR(edp->pclk); >> + } >> + >> + edp->rst = devm_reset_control_get(dev, "edp"); >> + if (IS_ERR(edp->rst)) { >> + dev_err(dev, "failed to get reset\n"); >> + return PTR_ERR(edp->rst); >> + } >> + >> + ret = rockchip_edp_clk_enable(edp); >> + if (ret < 0) { >> + dev_err(edp->dev, "cannot enable edp clk %d\n", ret); >> + return ret; >> + } >> + >> + ret = rockchip_edp_pre_init(edp); >> + if (ret < 0) { >> + dev_err(edp->dev, "failed to pre init %d\n", ret); >> + return ret; >> + } >> + >> + edp->irq = platform_get_irq(pdev, 0); >> + if (edp->irq < 0) { >> + dev_err(dev, "cannot find IRQ\n"); >> + return edp->irq; >> + } >> + >> + ret = devm_request_irq(dev, edp->irq, rockchip_edp_isr, 0, >> + dev_name(dev), edp); >> + if (ret) { >> + dev_err(dev, "cannot claim IRQ %d\n", edp->irq); >> + return ret; >> + } >> + >> + disable_irq_nosync(edp->irq); >> + >> + edp->dpms_mode = DRM_MODE_DPMS_OFF; >> + >> + dev_set_name(edp->dev, "rockchip-edp"); >> + >> + return 0; >> +} >> + >> +static int rockchip_edp_bind(struct device *dev, struct device *master, >> + void *data) >> +{ >> + struct rockchip_edp_device *edp = dev_get_drvdata(dev); >> + struct drm_encoder *encoder; >> + struct drm_connector *connector; >> + struct drm_device *drm_dev = data; >> + int ret; >> + >> + ret = rockchip_edp_init(edp); >> + if (ret < 0) >> + return ret; >> + >> + edp->drm_dev = drm_dev; >> + >> + encoder = &edp->encoder; >> + >> + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, >> + dev->of_node); >> + DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); >> + >> + ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs, >> + DRM_MODE_ENCODER_LVDS); >> + if (ret) { >> + DRM_ERROR("failed to initialize encoder with drm\n"); >> + return ret; >> + } >> + >> + drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs); >> + >> + connector = &edp->connector; >> + connector->polled = DRM_CONNECTOR_POLL_HPD; >> + connector->dpms = DRM_MODE_DPMS_OFF; >> + >> + ret = drm_connector_init(drm_dev, connector, >> + &rockchip_connector_funcs, >> + DRM_MODE_CONNECTOR_eDP); >> + if (ret) { >> + DRM_ERROR("failed to initialize connector with drm\n"); >> + goto err_free_encoder; >> + } >> + >> + drm_connector_helper_add(connector, >> + &rockchip_connector_helper_funcs); >> + >> + ret = drm_sysfs_connector_add(connector); >> + if (ret) { >> + DRM_ERROR("failed to add drm_sysfs\n"); >> + goto err_free_connector; >> + } >> + >> + ret = drm_mode_connector_attach_encoder(connector, encoder); >> + if (ret) { >> + DRM_ERROR("failed to attach connector and encoder\n"); >> + goto err_free_connector_sysfs; >> + } >> + >> + ret = drm_panel_attach(edp->panel, connector); >> + if (ret) { >> + DRM_ERROR("failed to attach connector and encoder\n"); >> + goto err_free_connector_sysfs; >> + } >> + >> + return 0; >> + >> +err_free_connector_sysfs: >> + drm_sysfs_connector_remove(connector); >> +err_free_connector: >> + drm_connector_cleanup(connector); >> +err_free_encoder: >> + drm_encoder_cleanup(encoder); >> + return ret; >> +} >> + >> +static void rockchip_edp_unbind(struct device *dev, struct device *master, >> + void *data) >> +{ >> + struct rockchip_edp_device *edp = dev_get_drvdata(dev); >> + struct drm_encoder *encoder; >> + >> + encoder = &edp->encoder; >> + >> + if (edp->panel) >> + drm_panel_detach(edp->panel); >> + >> + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); >> + encoder->funcs->destroy(encoder); >> + drm_sysfs_connector_remove(&edp->connector); >> + drm_connector_cleanup(&edp->connector); >> + drm_encoder_cleanup(encoder); >> +} >> + >> +static const struct component_ops rockchip_edp_component_ops = { >> + .bind = rockchip_edp_bind, >> + .unbind = rockchip_edp_unbind, >> +}; >> + >> +static int rockchip_edp_probe(struct platform_device *pdev) >> +{ >> + struct device *dev = &pdev->dev; >> + struct drm_panel *panel; >> + struct device_node *panel_node; >> + struct rockchip_edp_device *edp; >> + >> + if (!dev->of_node) { >> + dev_err(dev, "can't find eDP devices\n"); >> + return -ENODEV; >> + } >> + >> + panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0); >> + if (!panel_node) { >> + DRM_ERROR("failed to find diaplay panel\n"); >> + return -ENODEV; >> + } >> + >> + panel = of_drm_find_panel(panel_node); >> + if (!panel) { >> + DRM_ERROR("failed to find diaplay panel\n"); >> + of_node_put(panel_node); >> + return -EPROBE_DEFER; >> + } >> + >> + of_node_put(panel_node); >> + >> + edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL); >> + if (!edp) >> + return -ENOMEM; >> + edp->dev = dev; >> + edp->panel = panel; >> + platform_set_drvdata(pdev, edp); >> + >> + return component_add(dev, &rockchip_edp_component_ops); >> +} >> + >> +static int rockchip_edp_remove(struct platform_device *pdev) >> +{ >> + component_del(&pdev->dev, &rockchip_edp_component_ops); >> + >> + return 0; >> +} >> + >> +static struct platform_driver rockchip_edp_driver = { >> + .probe = rockchip_edp_probe, >> + .remove = rockchip_edp_remove, >> + .driver = { >> + .name = "rockchip-edp", >> + .owner = THIS_MODULE, >> + .of_match_table = of_match_ptr(rockchip_edp_dt_ids), >> + }, >> +}; >> + >> +module_platform_driver(rockchip_edp_driver); >> + >> +MODULE_AUTHOR("Jeff chen <jeff.chen@rock-chips.com>"); >> +MODULE_DESCRIPTION("ROCKCHIP EDP Driver"); >> +MODULE_LICENSE("GPL v2"); >> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.h b/drivers/gpu/drm/rockchip/rockchip_edp_core.h >> new file mode 100644 >> index 0000000..c13325f >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.h >> @@ -0,0 +1,309 @@ >> +/* >> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> +* Author: >> +* Andy yan <andy.yan@rock-chips.com> >> +* Jeff chen <jeff.chen@rock-chips.com> >> +* >> +* based on exynos_dp_core.h >> +* >> +* This program is free software; you can redistribute it and/or modify it >> +* under the terms of the GNU General Public License as published by the >> +* Free Software Foundation; either version 2 of the License, or (at your >> +* option) any later version. >> +*/ >> + >> +#ifndef _ROCKCHIP_EDP_CORE_H >> +#define _ROCKCHIP_EDP_CORE_H >> + >> +#include <drm/drmP.h> >> +#include <drm/drm_crtc_helper.h> >> +#include <drm/drm_dp_helper.h> >> +#include <drm/drm_panel.h> >> +#include "rockchip_drm_drv.h" >> + >> +#define DP_TIMEOUT_LOOP_CNT 100 >> +#define MAX_CR_LOOP 5 >> +#define MAX_EQ_LOOP 5 >> + >> +#define GRF_EDP_REF_CLK_SEL_INTER (1 << 4) >> +#define GRF_EDP_HDCP_EN (1 << 15) >> +#define GRF_EDP_BIST_EN (1 << 14) >> +#define GRF_EDP_MEM_CTL_BY_EDP (1 << 13) >> +#define GRF_EDP_SECURE_EN (1 << 3) >> +#define EDP_SEL_VOP_LIT (1 << 5) >> + >> +enum link_lane_count_type { >> + LANE_CNT1 = 1, >> + LANE_CNT2 = 2, >> + LANE_CNT4 = 4 >> +}; >> + >> +enum link_training_state { >> + LT_START, >> + LT_CLK_RECOVERY, >> + LT_EQ_TRAINING, >> + FINISHED, >> + FAILED >> +}; >> + >> +enum voltage_swing_level { >> + VOLTAGE_LEVEL_0, >> + VOLTAGE_LEVEL_1, >> + VOLTAGE_LEVEL_2, >> + VOLTAGE_LEVEL_3, >> +}; >> + >> +enum pre_emphasis_level { >> + PRE_EMPHASIS_LEVEL_0, >> + PRE_EMPHASIS_LEVEL_1, >> + PRE_EMPHASIS_LEVEL_2, >> + PRE_EMPHASIS_LEVEL_3, >> +}; >> + >> +enum pattern_set { >> + PRBS7, >> + D10_2, >> + TRAINING_PTN1, >> + TRAINING_PTN2, >> + DP_NONE >> +}; >> + >> +enum color_space { >> + CS_RGB, >> + CS_YCBCR422, >> + CS_YCBCR444 >> +}; >> + >> +enum color_depth { >> + COLOR_6, >> + COLOR_8, >> + COLOR_10, >> + COLOR_12 >> +}; >> + >> +enum color_coefficient { >> + COLOR_YCBCR601, >> + COLOR_YCBCR709 >> +}; >> + >> +enum dynamic_range { >> + VESA, >> + CEA >> +}; >> + >> +enum pll_status { >> + DP_PLL_UNLOCKED, >> + DP_PLL_LOCKED >> +}; >> + >> +enum clock_recovery_m_value_type { >> + CALCULATED_M, >> + REGISTER_M >> +}; >> + >> +enum video_timing_recognition_type { >> + VIDEO_TIMING_FROM_CAPTURE, >> + VIDEO_TIMING_FROM_REGISTER >> +}; >> + >> +enum analog_power_block { >> + AUX_BLOCK, >> + CH0_BLOCK, >> + CH1_BLOCK, >> + CH2_BLOCK, >> + CH3_BLOCK, >> + ANALOG_TOTAL, >> + POWER_ALL >> +}; >> + >> +enum dp_irq_type { >> + DP_IRQ_TYPE_HP_CABLE_IN, >> + DP_IRQ_TYPE_HP_CABLE_OUT, >> + DP_IRQ_TYPE_HP_CHANGE, >> + DP_IRQ_TYPE_UNKNOWN, >> +}; >> + >> +struct video_info { >> + char *name; >> + >> + bool h_sync_polarity; >> + bool v_sync_polarity; >> + bool interlaced; >> + >> + enum color_space color_space; >> + enum dynamic_range dynamic_range; >> + enum color_coefficient ycbcr_coeff; >> + enum color_depth color_depth; >> + >> + u8 link_rate; >> + enum link_lane_count_type lane_count; >> +}; >> + >> +struct link_train { >> + int eq_loop; >> + int cr_loop[4]; >> + >> + u8 link_rate; >> + u8 lane_count; >> + u8 training_lane[4]; >> + >> + enum link_training_state lt_state; >> +}; >> + >> +/* >> + * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds >> + */ >> +struct rockchip_edp_soc_data { >> + int grf_soc_con6; >> + int grf_soc_con12; >> +}; >> + >> +struct rockchip_edp_device { >> + struct device *dev; >> + struct drm_device *drm_dev; >> + struct drm_panel *panel; >> + struct drm_connector connector; >> + struct drm_encoder encoder; >> + struct drm_display_mode mode; >> + >> + struct rockchip_edp_soc_data *soc_data; >> + >> + void __iomem *regs; >> + struct regmap *grf; >> + unsigned int irq; >> + struct clk *clk_edp; >> + struct clk *clk_24m_parent; >> + struct clk *clk_24m; >> + struct clk *pclk; >> + struct reset_control *rst; >> + struct link_train link_train; >> + struct video_info video_info; >> + bool clk_on; >> + >> + int dpms_mode; >> +}; >> + >> +void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp, >> + bool enable); >> +void rockchip_edp_stop_video(struct rockchip_edp_device *edp); >> +void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable); >> +void rockchip_edp_init_refclk(struct rockchip_edp_device *edp); >> +void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp); >> +void rockchip_edp_reset(struct rockchip_edp_device *edp); >> +void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp); >> +u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp); >> +void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp, >> + bool enable); >> +void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp); >> +void rockchip_edp_init_hpd(struct rockchip_edp_device *edp); >> +void rockchip_edp_reset_aux(struct rockchip_edp_device *edp); >> +void rockchip_edp_init_aux(struct rockchip_edp_device *edp); >> +int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp); >> +void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp); >> +int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp); >> +int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp, >> + unsigned int reg_addr, >> + unsigned char data); >> +int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp, >> + unsigned int reg_addr, >> + unsigned char *data); >> +int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp, >> + unsigned int reg_addr, >> + unsigned int count, >> + unsigned char data[]); >> +int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp, >> + unsigned int reg_addr, >> + unsigned int count, >> + unsigned char data[]); >> +int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp, >> + unsigned int device_addr, >> + unsigned int reg_addr); >> +int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp, >> + unsigned int device_addr, >> + unsigned int reg_addr, >> + unsigned int *data); >> +int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp, >> + unsigned int device_addr, >> + unsigned int reg_addr, >> + unsigned int count, >> + unsigned char edid[]); >> +void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp, >> + u32 bwtype); >> +void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp, >> + u32 *bwtype); >> +void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, >> + u32 count); >> +void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, >> + u32 *count); >> +void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp, >> + bool enable); >> +void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp, >> + enum pattern_set pattern); >> +void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level); >> +void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level); >> +void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level); >> +void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level); >> +void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane); >> +void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane); >> +void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane); >> +void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane); >> +u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp); >> +u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp); >> +u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp); >> +u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp); >> +void rockchip_edp_reset_macro(struct rockchip_edp_device *edp); >> +int rockchip_edp_init_video(struct rockchip_edp_device *edp); >> + >> +void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp, >> + u32 color_depth, >> + u32 color_space, >> + u32 dynamic_range, >> + u32 coeff); >> +int >> +rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp); >> +void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp, >> + enum clock_recovery_m_value_type type, >> + u32 m_value, >> + u32 n_value); >> +void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp, >> + u32 type); >> +void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp, >> + bool enable); >> +void rockchip_edp_start_video(struct rockchip_edp_device *edp); >> +int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp); >> +void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp, >> + struct video_info *video_info); >> +void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp); >> +void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp); >> +void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp); >> +int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp); >> +int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp); >> +enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp); >> +void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp); >> + >> +/* I2C EDID Chip ID, Slave Address */ >> +#define I2C_EDID_DEVICE_ADDR 0x50 >> +#define I2C_E_EDID_DEVICE_ADDR 0x30 >> + >> +/* DPCD_ADDR_MAX_LANE_COUNT */ >> +#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) >> +#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) >> + >> +/* DPCD_ADDR_LANE_COUNT_SET */ >> +#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f) >> + >> +/* DPCD_ADDR_TRAINING_LANE0_SET */ >> +#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3) >> +#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3) >> +#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0) >> +#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3) >> + >> +#endif /* _ROCKCHIP_EDP_CORE_H */ >> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.c b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c >> new file mode 100644 >> index 0000000..f6d641c >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c >> @@ -0,0 +1,1202 @@ >> +/* >> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> +* Author: >> +* Andy yan <andy.yan@rock-chips.com> >> +* Jeff chen <jeff.chen@rock-chips.com> >> +* >> +* based on exynos_dp_reg.c >> +* >> +* This program is free software; you can redistribute it and/or modify it >> +* under the terms of the GNU General Public License as published by the >> +* Free Software Foundation; either version 2 of the License, or (at your >> +* option) any later version. >> +*/ >> + >> +#include <linux/device.h> >> +#include <linux/delay.h> >> +#include <linux/io.h> >> + >> +#include "rockchip_edp_core.h" >> +#include "rockchip_edp_reg.h" >> + >> +void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp, >> + bool enable) >> +{ >> + u32 val; >> + >> + if (enable) { >> + val = readl(edp->regs + VIDEO_CTL_1); >> + val |= VIDEO_MUTE; >> + writel(val, edp->regs + VIDEO_CTL_1); >> + } else { >> + val = readl(edp->regs + VIDEO_CTL_1); >> + val &= ~VIDEO_MUTE; >> + writel(val, edp->regs + VIDEO_CTL_1); >> + } >> +} >> + >> +void rockchip_edp_stop_video(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + VIDEO_CTL_1); >> + val &= ~VIDEO_EN; >> + writel(val, edp->regs + VIDEO_CTL_1); >> +} >> + >> +void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable) >> +{ >> + u32 val; >> + >> + if (enable) >> + val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 | >> + LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3; >> + else >> + val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | >> + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; >> + >> + writel(val, edp->regs + LANE_MAP); >> +} >> + >> +void rockchip_edp_init_refclk(struct rockchip_edp_device *edp) >> +{ >> + writel(SEL_24M, edp->regs + ANALOG_CTL_2); >> + writel(REF_CLK_24M, edp->regs + PLL_REG_1); >> + >> + writel(0x95, edp->regs + PLL_REG_2); >> + writel(0x40, edp->regs + PLL_REG_3); >> + writel(0x58, edp->regs + PLL_REG_4); >> + writel(0x22, edp->regs + PLL_REG_5); >> + writel(0x19, edp->regs + SSC_REG); >> + writel(0x87, edp->regs + TX_REG_COMMON); >> + writel(0x03, edp->regs + DP_AUX); >> + writel(0x46, edp->regs + DP_BIAS); >> + writel(0x55, edp->regs + DP_RESERVE2); >> +} >> + >> +void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp) >> +{ >> + /* Set interrupt pin assertion polarity as high */ >> + writel(INT_POL, edp->regs + INT_CTL); >> + >> + /* Clear pending valisers */ >> + writel(0xff, edp->regs + COMMON_INT_STA_1); >> + writel(0x4f, edp->regs + COMMON_INT_STA_2); >> + writel(0xff, edp->regs + COMMON_INT_STA_3); >> + writel(0x27, edp->regs + COMMON_INT_STA_4); >> + >> + writel(0x7f, edp->regs + DP_INT_STA); >> + >> + /* 0:mask,1: unmask */ >> + writel(0x00, edp->regs + COMMON_INT_MASK_1); >> + writel(0x00, edp->regs + COMMON_INT_MASK_2); >> + writel(0x00, edp->regs + COMMON_INT_MASK_3); >> + writel(0x00, edp->regs + COMMON_INT_MASK_4); >> + writel(0x00, edp->regs + DP_INT_STA_MASK); >> +} >> + >> +void rockchip_edp_reset(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + rockchip_edp_stop_video(edp); >> + rockchip_edp_enable_video_mute(edp, 0); >> + >> + val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N | >> + AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N; >> + writel(val, edp->regs + FUNC_EN_1); >> + >> + val = SSC_FUNC_EN_N | AUX_FUNC_EN_N | >> + SERDES_FIFO_FUNC_EN_N | >> + LS_CLK_DOMAIN_FUNC_EN_N; >> + writel(val, edp->regs + FUNC_EN_2); >> + >> + usleep_range(20, 30); >> + >> + rockchip_edp_lane_swap(edp, 0); >> + >> + writel(0x0, edp->regs + SYS_CTL_1); >> + writel(0x40, edp->regs + SYS_CTL_2); >> + writel(0x0, edp->regs + SYS_CTL_3); >> + writel(0x0, edp->regs + SYS_CTL_4); >> + >> + writel(0x0, edp->regs + PKT_SEND_CTL); >> + writel(0x0, edp->regs + HDCP_CTL); >> + >> + writel(0x5e, edp->regs + HPD_DEGLITCH_L); >> + writel(0x1a, edp->regs + HPD_DEGLITCH_H); >> + >> + writel(0x10, edp->regs + LINK_DEBUG_CTL); >> + >> + writel(0x0, edp->regs + VIDEO_FIFO_THRD); >> + writel(0x20, edp->regs + AUDIO_MARGIN); >> + >> + writel(0x4, edp->regs + M_VID_GEN_FILTER_TH); >> + writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH); >> + >> + writel(0x0, edp->regs + SOC_GENERAL_CTL); >> +} >> + >> +void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + /* 0: mask, 1: unmask */ >> + val = 0; >> + writel(val, edp->regs + COMMON_INT_MASK_1); >> + >> + writel(val, edp->regs + COMMON_INT_MASK_2); >> + >> + writel(val, edp->regs + COMMON_INT_MASK_3); >> + >> + writel(val, edp->regs + COMMON_INT_MASK_4); >> + >> + writel(val, edp->regs + DP_INT_STA_MASK); >> +} >> + >> +u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + DEBUG_CTL); >> + >> + return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED; >> +} >> + >> +void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp, >> + bool enable) >> +{ >> + u32 val; >> + >> + if (enable) { >> + val = PD_EXP_BG | PD_AUX | PD_PLL | >> + PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0; >> + writel(val, edp->regs + DP_PWRDN); >> + usleep_range(10, 20); >> + writel(0x0, edp->regs + DP_PWRDN); >> + } else { >> + val = PD_EXP_BG | PD_AUX | PD_PLL | >> + PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0; >> + writel(val, edp->regs + DP_PWRDN); >> + } >> +} >> + >> +void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + int wt = 0; >> + >> + rockchip_edp_analog_power_ctr(edp, 1); >> + >> + val = PLL_LOCK_CHG; >> + writel(val, edp->regs + COMMON_INT_STA_1); >> + >> + val = readl(edp->regs + DEBUG_CTL); >> + val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); >> + writel(val, edp->regs + DEBUG_CTL); >> + >> + /* Power up PLL */ >> + while (wt < 100) { >> + if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) { >> + dev_dbg(edp->dev, "edp pll locked\n"); >> + break; >> + } >> + wt++; >> + udelay(5); >> + } >> + >> + /* Enable Serdes FIFO function and Link symbol clock domain module */ >> + val = readl(edp->regs + FUNC_EN_2); >> + val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N >> + | AUX_FUNC_EN_N | SSC_FUNC_EN_N); >> + writel(val, edp->regs + FUNC_EN_2); >> +} >> + >> +void rockchip_edp_init_hpd(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = HOTPLUG_CHG | HPD_LOST | PLUG; >> + writel(val, edp->regs + COMMON_INT_STA_4); >> + >> + val = INT_HPD; >> + writel(val, edp->regs + DP_INT_STA); >> + >> + val = readl(edp->regs + SYS_CTL_3); >> + val |= (F_HPD | HPD_CTRL); >> + writel(val, edp->regs + SYS_CTL_3); >> +} >> + >> +void rockchip_edp_reset_aux(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + /* Disable AUX channel module */ >> + val = readl(edp->regs + FUNC_EN_2); >> + val |= AUX_FUNC_EN_N; >> + writel(val, edp->regs + FUNC_EN_2); >> +} >> + >> +void rockchip_edp_init_aux(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + /* Clear inerrupts related to AUX channel */ >> + val = RPLY_RECEIV | AUX_ERR; >> + writel(val, edp->regs + DP_INT_STA); >> + >> + rockchip_edp_reset_aux(edp); >> + >> + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ >> + val = DEFER_CTRL_EN | DEFER_COUNT(1); >> + writel(val, edp->regs + AUX_CH_DEFER_CTL); >> + >> + /* Enable AUX channel module */ >> + val = readl(edp->regs + FUNC_EN_2); >> + val &= ~AUX_FUNC_EN_N; >> + writel(val, edp->regs + FUNC_EN_2); >> +} >> + >> +int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + SYS_CTL_3); >> + if (val & HPD_STATUS) >> + return 0; >> + >> + return -EINVAL; >> +} >> + >> +void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + FUNC_EN_1); >> + val &= ~SW_FUNC_EN_N; >> + writel(val, edp->regs + FUNC_EN_1); >> +} >> + >> +int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp) >> +{ >> + int val; >> + int retval = 0; >> + int timeout_loop = 0; >> + int aux_timeout = 0; >> + >> + /* Enable AUX CH operation */ >> + val = readl(edp->regs + AUX_CH_CTL_2); >> + val |= AUX_EN; >> + writel(val, edp->regs + AUX_CH_CTL_2); >> + >> + /* Is AUX CH operation enabled? */ >> + val = readl(edp->regs + AUX_CH_CTL_2); >> + while (val & AUX_EN) { >> + aux_timeout++; >> + if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) { >> + dev_err(edp->dev, "AUX CH enable timeout!\n"); >> + return -ETIMEDOUT; >> + } >> + val = readl(edp->regs + AUX_CH_CTL_2); >> + usleep_range(1000, 2000); >> + } >> + >> + /* Is AUX CH command redply received? */ >> + val = readl(edp->regs + DP_INT_STA); >> + while (!(val & RPLY_RECEIV)) { >> + timeout_loop++; >> + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { >> + dev_err(edp->dev, "AUX CH command redply failed!\n"); >> + return -ETIMEDOUT; >> + } >> + val = readl(edp->regs + DP_INT_STA); >> + usleep_range(10, 20); >> + } >> + >> + /* Clear interrupt source for AUX CH command redply */ >> + writel(RPLY_RECEIV, edp->regs + DP_INT_STA); >> + >> + /* Clear interrupt source for AUX CH access error */ >> + val = readl(edp->regs + DP_INT_STA); >> + if (val & AUX_ERR) { >> + writel(AUX_ERR, edp->regs + DP_INT_STA); >> + return -EREMOTEIO; >> + } >> + >> + /* Check AUX CH error access status */ >> + val = readl(edp->regs + AUX_CH_STA); >> + if ((val & AUX_STATUS_MASK) != 0) { >> + dev_err(edp->dev, "AUX CH error happens: %d\n\n", >> + val & AUX_STATUS_MASK); >> + return -EREMOTEIO; >> + } >> + >> + return retval; >> +} >> + >> +int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp, >> + unsigned int val_addr, >> + unsigned char data) >> +{ >> + u32 val; >> + int i; >> + int retval; >> + >> + for (i = 0; i < 3; i++) { >> + /* Clear AUX CH data buffer */ >> + val = BUF_CLR; >> + writel(val, edp->regs + BUFFER_DATA_CTL); >> + >> + /* Select DPCD device address */ >> + val = AUX_ADDR_7_0(val_addr); >> + writel(val, edp->regs + DP_AUX_ADDR_7_0); >> + val = AUX_ADDR_15_8(val_addr); >> + writel(val, edp->regs + DP_AUX_ADDR_15_8); >> + val = AUX_ADDR_19_16(val_addr); >> + writel(val, edp->regs + DP_AUX_ADDR_19_16); >> + >> + /* Write data buffer */ >> + val = (unsigned int)data; >> + writel(val, edp->regs + BUF_DATA_0); >> + >> + /* >> + * Set DisplayPort transaction and write 1 byte >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval == 0) >> + break; >> + >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + } >> + >> + return retval; >> +} >> + >> +int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp, >> + unsigned int val_addr, >> + unsigned char *data) >> +{ >> + u32 val; >> + int i; >> + int retval; >> + >> + for (i = 0; i < 10; i++) { >> + /* Clear AUX CH data buffer */ >> + val = BUF_CLR; >> + writel(val, edp->regs + BUFFER_DATA_CTL); >> + >> + /* Select DPCD device address */ >> + val = AUX_ADDR_7_0(val_addr); >> + writel(val, edp->regs + DP_AUX_ADDR_7_0); >> + val = AUX_ADDR_15_8(val_addr); >> + writel(val, edp->regs + DP_AUX_ADDR_15_8); >> + val = AUX_ADDR_19_16(val_addr); >> + writel(val, edp->regs + DP_AUX_ADDR_19_16); >> + >> + /* >> + * Set DisplayPort transaction and read 1 byte >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval == 0) >> + break; >> + >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + } >> + >> + /* Read data buffer */ >> + val = readl(edp->regs + BUF_DATA_0); >> + *data = (unsigned char)(val & 0xff); >> + >> + return retval; >> +} >> + >> +int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp, >> + unsigned int val_addr, >> + unsigned int count, >> + unsigned char data[]) >> +{ >> + u32 val; >> + unsigned int start_offset; >> + unsigned int cur_data_count; >> + unsigned int cur_data_idx; >> + int i; >> + int retval = 0; >> + >> + /* Clear AUX CH data buffer */ >> + val = BUF_CLR; >> + writel(val, edp->regs + BUFFER_DATA_CTL); >> + >> + start_offset = 0; >> + while (start_offset < count) { >> + /* Buffer size of AUX CH is 16 * 4bytes */ >> + if ((count - start_offset) > 16) >> + cur_data_count = 16; >> + else >> + cur_data_count = count - start_offset; >> + >> + for (i = 0; i < 10; i++) { >> + /* Select DPCD device address */ >> + val = AUX_ADDR_7_0(val_addr + start_offset); >> + writel(val, edp->regs + DP_AUX_ADDR_7_0); >> + val = AUX_ADDR_15_8(val_addr + start_offset); >> + writel(val, edp->regs + DP_AUX_ADDR_15_8); >> + val = AUX_ADDR_19_16(val_addr + start_offset); >> + writel(val, edp->regs + DP_AUX_ADDR_19_16); >> + >> + for (cur_data_idx = 0; cur_data_idx < cur_data_count; >> + cur_data_idx++) { >> + val = data[start_offset + cur_data_idx]; >> + writel(val, edp->regs + BUF_DATA_0 >> + + 4 * cur_data_idx); >> + } >> + >> + /* >> + * Set DisplayPort transaction and write >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_LENGTH(cur_data_count) | >> + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval == 0) >> + break; >> + >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + } >> + >> + start_offset += cur_data_count; >> + } >> + >> + return retval; >> +} >> + >> +int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp, >> + unsigned int val_addr, >> + unsigned int count, >> + unsigned char data[]) >> +{ >> + u32 val; >> + unsigned int start_offset; >> + unsigned int cur_data_count; >> + unsigned int cur_data_idx; >> + int i; >> + int retval = 0; >> + >> + /* Clear AUX CH data buffer */ >> + val = BUF_CLR; >> + writel(val, edp->regs + BUFFER_DATA_CTL); >> + >> + start_offset = 0; >> + while (start_offset < count) { >> + /* Buffer size of AUX CH is 16 * 4bytes */ >> + if ((count - start_offset) > 16) >> + cur_data_count = 16; >> + else >> + cur_data_count = count - start_offset; >> + >> + /* AUX CH Request Transaction process */ >> + for (i = 0; i < 10; i++) { >> + /* Select DPCD device address */ >> + val = AUX_ADDR_7_0(val_addr + start_offset); >> + writel(val, edp->regs + DP_AUX_ADDR_7_0); >> + val = AUX_ADDR_15_8(val_addr + start_offset); >> + writel(val, edp->regs + DP_AUX_ADDR_15_8); >> + val = AUX_ADDR_19_16(val_addr + start_offset); >> + writel(val, edp->regs + DP_AUX_ADDR_19_16); >> + >> + /* >> + * Set DisplayPort transaction and read >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_LENGTH(cur_data_count) | >> + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval == 0) >> + break; >> + >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + } >> + >> + for (cur_data_idx = 0; cur_data_idx < cur_data_count; >> + cur_data_idx++) { >> + val = readl(edp->regs + BUF_DATA_0 >> + + 4 * cur_data_idx); >> + data[start_offset + cur_data_idx] = >> + (unsigned char)val; >> + } >> + >> + start_offset += cur_data_count; >> + } >> + >> + return retval; >> +} >> + >> +int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp, >> + unsigned int device_addr, >> + unsigned int val_addr) >> +{ >> + u32 val; >> + int retval; >> + >> + /* Set EDID device address */ >> + val = device_addr; >> + writel(val, edp->regs + DP_AUX_ADDR_7_0); >> + writel(0x0, edp->regs + DP_AUX_ADDR_15_8); >> + writel(0x0, edp->regs + DP_AUX_ADDR_19_16); >> + >> + /* Set offset from base address of EDID device */ >> + writel(val_addr, edp->regs + BUF_DATA_0); >> + >> + /* >> + * Set I2C transaction and write address >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | >> + AUX_TX_COMM_WRITE; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval != 0) >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + >> + return retval; >> +} >> + >> +int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp, >> + unsigned int device_addr, >> + unsigned int val_addr, >> + unsigned int *data) >> +{ >> + u32 val; >> + int i; >> + int retval; >> + >> + for (i = 0; i < 10; i++) { >> + /* Clear AUX CH data buffer */ >> + val = BUF_CLR; >> + writel(val, edp->regs + BUFFER_DATA_CTL); >> + >> + /* Select EDID device */ >> + retval = rockchip_edp_select_i2c_device(edp, >> + device_addr, >> + val_addr); >> + if (retval != 0) { >> + dev_err(edp->dev, "Select EDID device fail!\n"); >> + continue; >> + } >> + >> + /* >> + * Set I2C transaction and read data >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval == 0) >> + break; >> + >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + } >> + >> + /* Read data */ >> + if (retval == 0) >> + *data = readl(edp->regs + BUF_DATA_0); >> + >> + return retval; >> +} >> + >> +int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp, >> + unsigned int device_addr, >> + unsigned int val_addr, >> + unsigned int count, >> + unsigned char edid[]) >> +{ >> + u32 val; >> + unsigned int i, j; >> + unsigned int cur_data_idx; >> + unsigned int defer = 0; >> + int retval = 0; >> + >> + for (i = 0; i < count; i += 16) { >> + for (j = 0; j < 100; j++) { >> + /* Clear AUX CH data buffer */ >> + val = BUF_CLR; >> + writel(val, edp->regs + BUFFER_DATA_CTL); >> + >> + /* Set normal AUX CH command */ >> + val = readl(edp->regs + AUX_CH_CTL_2); >> + val &= ~ADDR_ONLY; >> + writel(val, edp->regs + AUX_CH_CTL_2); >> + >> + /* >> + * If Rx sends defer, Tx sends only reads >> + * request without sending addres >> + */ >> + if (!defer) >> + retval = rockchip_edp_select_i2c_device( >> + edp, device_addr, val_addr + i); >> + else >> + defer = 0; >> + >> + /* >> + * Set I2C transaction and write data >> + * If bit 3 is 1, DisplayPort transaction. >> + * If Bit 3 is 0, I2C transaction. >> + */ >> + val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION | >> + AUX_TX_COMM_READ; >> + writel(val, edp->regs + AUX_CH_CTL_1); >> + >> + /* Start AUX transaction */ >> + retval = rockchip_edp_start_aux_transaction(edp); >> + if (retval == 0) >> + break; >> + >> + dev_dbg(edp->dev, "Aux Transaction fail!\n"); >> + >> + /* Check if Rx sends defer */ >> + val = readl(edp->regs + AUX_RX_COMM); >> + if (val == AUX_RX_COMM_AUX_DEFER || >> + val == AUX_RX_COMM_I2C_DEFER) { >> + dev_err(edp->dev, "Defer: %d\n\n", val); >> + defer = 1; >> + } >> + } >> + >> + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { >> + val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx); >> + edid[i + cur_data_idx] = (unsigned char)val; >> + } >> + } >> + >> + return retval; >> +} >> + >> +void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp, >> + u32 bwtype) >> +{ >> + u32 val; >> + >> + val = bwtype; >> + if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62)) >> + writel(val, edp->regs + LINK_BW_SET); >> +} >> + >> +void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp, >> + u32 *bwtype) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + LINK_BW_SET); >> + *bwtype = val; >> +} >> + >> +void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = HW_LT_EN; >> + writel(val, edp->regs + HW_LT_CTL); >> +} >> + >> +int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + DP_INT_STA); >> + if (val&HW_LT_DONE) { >> + writel(val, edp->regs + DP_INT_STA); >> + return 0; >> + } >> + >> + return 1; >> +} >> + >> +int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + HW_LT_CTL); >> + >> + return (val & HW_LT_ERR_CODE_MASK) >> 4; >> +} >> + >> +void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, u32 count) >> +{ >> + u32 val; >> + >> + val = count; >> + writel(val, edp->regs + LANE_CNT_SET); >> +} >> + >> +void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, u32 *count) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + LANE_CNT_SET); >> + *count = val; >> +} >> + >> +void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp, >> + bool enable) >> +{ >> + u32 val; >> + >> + if (enable) { >> + val = readl(edp->regs + SYS_CTL_4); >> + val |= ENHANCED; >> + writel(val, edp->regs + SYS_CTL_4); >> + } else { >> + val = readl(edp->regs + SYS_CTL_4); >> + val &= ~ENHANCED; >> + writel(val, edp->regs + SYS_CTL_4); >> + } >> +} >> + >> +void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp, >> + enum pattern_set pattern) >> +{ >> + u32 val; >> + >> + switch (pattern) { >> + case PRBS7: >> + val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> + break; >> + case D10_2: >> + val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> + break; >> + case TRAINING_PTN1: >> + val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> + break; >> + case TRAINING_PTN2: >> + val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> + break; >> + case DP_NONE: >> + val = SCRAMBLING_ENABLE | >> + LINK_QUAL_PATTERN_SET_DISABLE | >> + SW_TRAINING_PATTERN_SET_DISABLE; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> + break; >> + default: >> + break; >> + } >> +} >> + >> +void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level) >> +{ >> + u32 val; >> + >> + val = level << PRE_EMPHASIS_SET_SHIFT; >> + writel(val, edp->regs + LN0_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level) >> +{ >> + u32 val; >> + >> + val = level << PRE_EMPHASIS_SET_SHIFT; >> + writel(val, edp->regs + LN1_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level) >> +{ >> + u32 val; >> + >> + val = level << PRE_EMPHASIS_SET_SHIFT; >> + writel(val, edp->regs + LN2_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp, >> + u32 level) >> +{ >> + u32 val; >> + >> + val = level << PRE_EMPHASIS_SET_SHIFT; >> + writel(val, edp->regs + LN3_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane) >> +{ >> + u32 val; >> + >> + val = training_lane; >> + writel(val, edp->regs + LN0_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane) >> +{ >> + u32 val; >> + >> + val = training_lane; >> + writel(val, edp->regs + LN1_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane) >> +{ >> + u32 val; >> + >> + val = training_lane; >> + writel(val, edp->regs + LN2_LINK_TRAINING_CTL); >> +} >> + >> +void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp, >> + u32 training_lane) >> +{ >> + u32 val; >> + >> + val = training_lane; >> + writel(val, edp->regs + LN3_LINK_TRAINING_CTL); >> +} >> + >> +u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + LN0_LINK_TRAINING_CTL); >> + return val; >> +} >> + >> +u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + LN1_LINK_TRAINING_CTL); >> + return val; >> +} >> + >> +u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + LN2_LINK_TRAINING_CTL); >> + return val; >> +} >> + >> +u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + LN3_LINK_TRAINING_CTL); >> + return val; >> +} >> + >> +void rockchip_edp_reset_macro(struct rockchip_edp_device *edp) >> +{ >> +} >> + >> +int rockchip_edp_init_video(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; >> + writel(val, edp->regs + COMMON_INT_STA_1); >> + >> + val = 0x0; >> + writel(val, edp->regs + SYS_CTL_1); >> + >> + val = CHA_CRI(4) | CHA_CTRL; >> + writel(val, edp->regs + SYS_CTL_2); >> + >> + val = VID_HRES_TH(2) | VID_VRES_TH(0); >> + writel(val, edp->regs + VIDEO_CTL_8); >> + >> + return 0; >> +} >> + >> +void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp, >> + u32 color_dedpth, >> + u32 color_space, >> + u32 dynamic_range, >> + u32 coeff) >> +{ >> + u32 val; >> + >> + /* Configure the input color dedpth, color space, dynamic range */ >> + val = (dynamic_range << IN_D_RANGE_SHIFT) | >> + (color_dedpth << IN_BPC_SHIFT) | >> + (color_space << IN_COLOR_F_SHIFT); >> + writel(val, edp->regs + VIDEO_CTL_2); >> + >> + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ >> + val = readl(edp->regs + VIDEO_CTL_3); >> + val &= ~IN_YC_COEFFI_MASK; >> + if (coeff) >> + val |= IN_YC_COEFFI_ITU709; >> + else >> + val |= IN_YC_COEFFI_ITU601; >> + writel(val, edp->regs + VIDEO_CTL_3); >> +} >> + >> +int rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + SYS_CTL_1); >> + writel(val, edp->regs + SYS_CTL_1); >> + >> + val = readl(edp->regs + SYS_CTL_1); >> + >> + if (!(val & DET_STA)) { >> + dev_dbg(edp->dev, "Input stream clock not detected.\n"); >> + return -EINVAL; >> + } >> + >> + val = readl(edp->regs + SYS_CTL_2); >> + writel(val, edp->regs + SYS_CTL_2); >> + >> + val = readl(edp->regs + SYS_CTL_2); >> + if (val & CHA_STA) { >> + dev_dbg(edp->dev, "Input stream clk is changing\n"); >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp, >> + enum clock_recovery_m_value_type type, >> + u32 m_value, >> + u32 n_value) >> +{ >> + u32 val; >> + >> + if (type == REGISTER_M) { >> + val = readl(edp->regs + SYS_CTL_4); >> + val |= FIX_M_VID; >> + writel(val, edp->regs + SYS_CTL_4); >> + val = m_value & 0xff; >> + writel(val, edp->regs + M_VID_0); >> + val = (m_value >> 8) & 0xff; >> + writel(val, edp->regs + M_VID_1); >> + val = (m_value >> 16) & 0xff; >> + writel(val, edp->regs + M_VID_2); >> + >> + val = n_value & 0xff; >> + writel(val, edp->regs + N_VID_0); >> + val = (n_value >> 8) & 0xff; >> + writel(val, edp->regs + N_VID_1); >> + val = (n_value >> 16) & 0xff; >> + writel(val, edp->regs + N_VID_2); >> + } else { >> + val = readl(edp->regs + SYS_CTL_4); >> + val &= ~FIX_M_VID; >> + writel(val, edp->regs + SYS_CTL_4); >> + >> + writel(0x00, edp->regs + N_VID_0); >> + writel(0x80, edp->regs + N_VID_1); >> + writel(0x00, edp->regs + N_VID_2); >> + } >> +} >> + >> +void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp, >> + u32 type) >> +{ >> + u32 val; >> + >> + if (type == VIDEO_TIMING_FROM_CAPTURE) { >> + val = readl(edp->regs + VIDEO_CTL_10); >> + val &= ~F_SEL; >> + writel(val, edp->regs + VIDEO_CTL_10); >> + } else { >> + val = readl(edp->regs + VIDEO_CTL_10); >> + val |= F_SEL; >> + writel(val, edp->regs + VIDEO_CTL_10); >> + } >> +} >> + >> +int rockchip_edp_bist_cfg(struct rockchip_edp_device *edp) >> +{ >> + struct video_info *video_info = &edp->video_info; >> + struct drm_display_mode *mode = &edp->mode; >> + u16 x_total, y_total, x_act; >> + u32 val; >> + >> + x_total = mode->htotal; >> + y_total = mode->vtotal; >> + x_act = mode->hdisplay; >> + >> + rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); >> + rockchip_edp_set_video_color_format(edp, video_info->color_depth, >> + video_info->color_space, >> + video_info->dynamic_range, >> + video_info->ycbcr_coeff); >> + >> + val = y_total & 0xff; >> + writel(val, edp->regs + TOTAL_LINE_CFG_L); >> + val = (y_total >> 8); >> + writel(val, edp->regs + TOTAL_LINE_CFG_H); >> + val = (mode->vdisplay & 0xff); >> + writel(val, edp->regs + ATV_LINE_CFG_L); >> + val = (mode->vdisplay >> 8); >> + writel(val, edp->regs + ATV_LINE_CFG_H); >> + val = (mode->vsync_start - mode->vdisplay); >> + writel(val, edp->regs + VF_PORCH_REG); >> + val = (mode->vsync_end - mode->vsync_start); >> + writel(val, edp->regs + VSYNC_CFG_REG); >> + val = (mode->vtotal - mode->vsync_end); >> + writel(val, edp->regs + VB_PORCH_REG); >> + val = x_total & 0xff; >> + writel(val, edp->regs + TOTAL_PIXELL_REG); >> + val = x_total >> 8; >> + writel(val, edp->regs + TOTAL_PIXELH_REG); >> + val = (x_act & 0xff); >> + writel(val, edp->regs + ATV_PIXELL_REG); >> + val = (x_act >> 8); >> + writel(val, edp->regs + ATV_PIXELH_REG); >> + val = (mode->hsync_start - mode->hdisplay) & 0xff; >> + writel(val, edp->regs + HF_PORCHL_REG); >> + val = (mode->hsync_start - mode->hdisplay) >> 8; >> + writel(val, edp->regs + HF_PORCHH_REG); >> + val = (mode->hsync_end - mode->hsync_start) & 0xff; >> + writel(val, edp->regs + HSYNC_CFGL_REG); >> + val = (mode->hsync_end - mode->hsync_start) >> 8; >> + writel(val, edp->regs + HSYNC_CFGH_REG); >> + val = (mode->htotal - mode->hsync_end) & 0xff; >> + writel(val, edp->regs + HB_PORCHL_REG); >> + val = (mode->htotal - mode->hsync_end) >> 8; >> + writel(val, edp->regs + HB_PORCHH_REG); >> + >> + val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR; >> + writel(val, edp->regs + VIDEO_CTL_4); >> + >> + val = readl(edp->regs + VIDEO_CTL_10); >> + val &= ~F_SEL; >> + writel(val, edp->regs + VIDEO_CTL_10); >> + return 0; >> +} >> + >> +void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp, >> + bool enable) >> +{ >> +} >> + >> +void rockchip_edp_start_video(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + VIDEO_CTL_1); >> + val |= VIDEO_EN; >> + writel(val, edp->regs + VIDEO_CTL_1); >> +} >> + >> +int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + SYS_CTL_3); >> + writel(val, edp->regs + SYS_CTL_3); >> + >> + val = readl(edp->regs + SYS_CTL_3); >> + if (!(val & STRM_VALID)) { >> + dev_dbg(edp->dev, "Input video stream is not detected.\n"); >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp, >> + struct video_info *video_info) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + FUNC_EN_1); >> + val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N); >> + writel(val, edp->regs + FUNC_EN_1); >> + >> + val = readl(edp->regs + VIDEO_CTL_10); >> + val &= ~INTERACE_SCAN_CFG; >> + val |= (video_info->interlaced << 2); >> + writel(val, edp->regs + VIDEO_CTL_10); >> + >> + val = readl(edp->regs + VIDEO_CTL_10); >> + val &= ~VSYNC_POLARITY_CFG; >> + val |= (video_info->v_sync_polarity << 1); >> + writel(val, edp->regs + VIDEO_CTL_10); >> + >> + val = readl(edp->regs + VIDEO_CTL_10); >> + val &= ~HSYNC_POLARITY_CFG; >> + val |= (video_info->h_sync_polarity << 0); >> + writel(val, edp->regs + VIDEO_CTL_10); >> +} >> + >> +void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + TRAINING_PTN_SET); >> + val &= ~SCRAMBLING_DISABLE; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> +} >> + >> +void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = readl(edp->regs + TRAINING_PTN_SET); >> + val |= SCRAMBLING_DISABLE; >> + writel(val, edp->regs + TRAINING_PTN_SET); >> +} >> + >> +enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + /* Parse hotplug interrupt status register */ >> + val = readl(edp->regs + COMMON_INT_STA_4); >> + if (val & PLUG) >> + return DP_IRQ_TYPE_HP_CABLE_IN; >> + >> + if (val & HPD_LOST) >> + return DP_IRQ_TYPE_HP_CABLE_OUT; >> + >> + if (val & HOTPLUG_CHG) >> + return DP_IRQ_TYPE_HP_CHANGE; >> + >> + return DP_IRQ_TYPE_UNKNOWN; >> +} >> + >> +void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp) >> +{ >> + u32 val; >> + >> + val = HOTPLUG_CHG | HPD_LOST | PLUG; >> + writel(val, edp->regs + COMMON_INT_STA_4); >> + >> + val = INT_HPD; >> + writel(val, edp->regs + DP_INT_STA); >> +} >> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.h b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h >> new file mode 100644 >> index 0000000..b50dd47 >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h >> @@ -0,0 +1,345 @@ >> +/* >> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> +* Author: >> +* Andy yan <andy.yan@rock-chips.com> >> +* Jeff chen <jeff.chen@rock-chips.com> >> +* >> +* based on exynos_dp_reg.h >> +* >> +* This program is free software; you can redistribute it and/or modify it >> +* under the terms of the GNU General Public License as published by the >> +* Free Software Foundation; either version 2 of the License, or (at your >> +* option) any later version. >> +*/ >> + >> +#ifndef _ROCKCHIP_EDP_REG_H >> +#define _ROCKCHIP_EDP_REG_H >> + >> +#include <linux/bitops.h> >> + >> +#define TX_SW_RST 0x14 >> +#define FUNC_EN_1 0x18 >> +#define FUNC_EN_2 0x1C >> +#define VIDEO_CTL_1 0x20 >> +#define VIDEO_CTL_2 0x24 >> +#define VIDEO_CTL_3 0x28 >> +#define VIDEO_CTL_4 0x2c >> +#define VIDEO_CTL_8 0x3C >> +#define VIDEO_CTL_10 0x44 >> +#define TOTAL_LINE_CFG_L 0x48 >> +#define TOTAL_LINE_CFG_H 0x4c >> +#define ATV_LINE_CFG_L 0x50 >> +#define ATV_LINE_CFG_H 0x54 >> +#define VF_PORCH_REG 0x58 >> +#define VSYNC_CFG_REG 0x5c >> +#define VB_PORCH_REG 0x60 >> +#define TOTAL_PIXELL_REG 0x64 >> +#define TOTAL_PIXELH_REG 0x68 >> +#define ATV_PIXELL_REG 0x6c >> +#define ATV_PIXELH_REG 0x70 >> +#define HF_PORCHL_REG 0x74 >> +#define HF_PORCHH_REG 0x78 >> +#define HSYNC_CFGL_REG 0x7c >> +#define HSYNC_CFGH_REG 0x80 >> +#define HB_PORCHL_REG 0x84 >> +#define HB_PORCHH_REG 0x88 >> +#define PLL_REG_1 0xfc >> + >> +#define SSC_REG 0x104 >> +#define TX_REG_COMMON 0x114 >> +#define DP_AUX 0x120 >> +#define DP_BIAS 0x124 >> +#define DP_PWRDN 0x12c >> +#define DP_RESERVE2 0x134 >> + >> +#define LANE_MAP 0x35C >> +#define ANALOG_CTL_2 0x374 >> +#define AUX_HW_RETRY_CTL 0x390 >> +#define COMMON_INT_STA_1 0x3C4 >> +#define COMMON_INT_STA_2 0x3C8 >> +#define COMMON_INT_STA_3 0x3CC >> +#define COMMON_INT_STA_4 0x3D0 >> +#define DP_INT_STA 0x3DC >> +#define COMMON_INT_MASK_1 0x3E0 >> +#define COMMON_INT_MASK_2 0x3E4 >> +#define COMMON_INT_MASK_3 0x3E8 >> +#define COMMON_INT_MASK_4 0x3EC >> +#define DP_INT_STA_MASK 0x3F8 >> + >> +#define SYS_CTL_1 0x600 >> +#define SYS_CTL_2 0x604 >> +#define SYS_CTL_3 0x608 >> +#define SYS_CTL_4 0x60C >> +#define PKT_SEND_CTL 0x640 >> +#define HDCP_CTL 0x648 >> +#define LINK_BW_SET 0x680 >> +#define LANE_CNT_SET 0x684 >> +#define TRAINING_PTN_SET 0x688 >> +#define LN0_LINK_TRAINING_CTL 0x68C >> +#define LN1_LINK_TRAINING_CTL 0x690 >> +#define LN2_LINK_TRAINING_CTL 0x694 >> +#define LN3_LINK_TRAINING_CTL 0x698 >> +#define HW_LT_CTL 0x6a0 >> +#define DEBUG_CTL 0x6C0 >> +#define HPD_DEGLITCH_L 0x6C4 >> +#define HPD_DEGLITCH_H 0x6C8 >> +#define LINK_DEBUG_CTL 0x6E0 >> +#define M_VID_0 0x700 >> +#define M_VID_1 0x704 >> +#define M_VID_2 0x708 >> +#define N_VID_0 0x70C >> +#define N_VID_1 0x710 >> +#define N_VID_2 0x714 >> +#define VIDEO_FIFO_THRD 0x730 >> +#define AUDIO_MARGIN 0x73C >> +#define M_VID_GEN_FILTER_TH 0x764 >> +#define M_AUD_GEN_FILTER_TH 0x778 >> +#define AUX_CH_STA 0x780 >> +#define AUX_CH_DEFER_CTL 0x788 >> +#define AUX_RX_COMM 0x78C >> +#define BUFFER_DATA_CTL 0x790 >> +#define AUX_CH_CTL_1 0x794 >> +#define DP_AUX_ADDR_7_0 0x798 >> +#define DP_AUX_ADDR_15_8 0x79C >> +#define DP_AUX_ADDR_19_16 0x7A0 >> +#define AUX_CH_CTL_2 0x7A4 >> +#define BUF_DATA_0 0x7C0 >> +#define SOC_GENERAL_CTL 0x800 >> +#define PLL_REG_2 0x9e4 >> +#define PLL_REG_3 0x9e8 >> +#define PLL_REG_4 0x9ec >> +#define PLL_REG_5 0xa00 >> + >> +/* ROCKCHIP_EDP_FUNC_EN_1 */ >> +#define VID_CAP_FUNC_EN_N BIT(6) >> +#define VID_FIFO_FUNC_EN_N BIT(5) >> +#define AUD_FIFO_FUNC_EN_N BIT(4) >> +#define AUD_FUNC_EN_N BIT(3) >> +#define HDCP_FUNC_EN_N BIT(2) >> +#define SW_FUNC_EN_N BIT(0) >> + >> +/* ROCKCHIP_EDP_FUNC_EN_2 */ >> +#define SSC_FUNC_EN_N BIT(7) >> +#define AUX_FUNC_EN_N BIT(2) >> +#define SERDES_FIFO_FUNC_EN_N BIT(1) >> +#define LS_CLK_DOMAIN_FUNC_EN_N BIT(0) >> + >> +/* ROCKCHIP_EDP_VIDEO_CTL_1 */ >> +#define VIDEO_EN BIT(7) >> +#define VIDEO_MUTE BIT(6) >> + >> +/* ROCKCHIP_EDP_VIDEO_CTL_1 */ >> +#define IN_D_RANGE_MASK (0x1 << 7) >> +#define IN_D_RANGE_SHIFT (7) >> +#define IN_D_RANGE_CEA (0x1 << 7) >> +#define IN_D_RANGE_VESA (0x0 << 7) >> +#define IN_BPC_MASK (0x7 << 4) >> +#define IN_BPC_SHIFT (4) >> +#define IN_BPC_12_BITS (0x3 << 4) >> +#define IN_BPC_10_BITS (0x2 << 4) >> +#define IN_BPC_8_BITS (0x1 << 4) >> +#define IN_BPC_6_BITS (0x0 << 4) >> +#define IN_COLOR_F_MASK (0x3 << 0) >> +#define IN_COLOR_F_SHIFT (0) >> +#define IN_COLOR_F_YCBCR444 (0x2 << 0) >> +#define IN_COLOR_F_YCBCR422 (0x1 << 0) >> +#define IN_COLOR_F_RGB (0x0 << 0) >> + >> +/* ROCKCHIP_EDP_VIDEO_CTL_3 */ >> +#define IN_YC_COEFFI_MASK (0x1 << 7) >> +#define IN_YC_COEFFI_SHIFT (7) >> +#define IN_YC_COEFFI_ITU709 (0x1 << 7) >> +#define IN_YC_COEFFI_ITU601 (0x0 << 7) >> +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) >> +#define VID_CHK_UPDATE_TYPE_SHIFT (4) >> +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) >> +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) >> + >> +/* ROCKCHIP_EDP_VIDEO_CTL_4 */ >> +#define BIST_EN (0x1 << 3) >> +#define BIST_WH_64 (0x1 << 2) >> +#define BIST_WH_32 (0x0 << 2) >> +#define BIST_TYPE_COLR_BAR (0x0 << 0) >> +#define BIST_TYPE_GRAY_BAR (0x1 << 0) >> +#define BIST_TYPE_MOBILE_BAR (0x2 << 0) >> + >> +/* ROCKCHIP_EDP_VIDEO_CTL_8 */ >> +#define VID_HRES_TH(x) (((x) & 0xf) << 4) >> +#define VID_VRES_TH(x) (((x) & 0xf) << 0) >> + >> +/* ROCKCHIP_EDP_VIDEO_CTL_10 */ >> +#define F_SEL (0x1 << 4) >> +#define INTERACE_SCAN_CFG (0x1 << 2) >> +#define VSYNC_POLARITY_CFG (0x1 << 1) >> +#define HSYNC_POLARITY_CFG (0x1 << 0) >> + >> +/* ROCKCHIP_EDP_PLL_REG_1 */ >> +#define REF_CLK_24M (0x1 << 1) >> +#define REF_CLK_27M (0x0 << 1) >> + >> +/* ROCKCHIP_EDP_DP_PWRDN */ >> +#define PD_INC_BG BIT(7) >> +#define PD_EXP_BG BIT(6) >> +#define PD_AUX BIT(5) >> +#define PD_PLL BIT(4) >> +#define PD_CH3 BIT(3) >> +#define PD_CH2 BIT(2) >> +#define PD_CH1 BIT(1) >> +#define PD_CH0 BIT(0) >> + >> +/* ROCKCHIP_EDP_LANE_MAP */ >> +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) >> +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) >> +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) >> +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) >> +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) >> +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) >> +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) >> +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) >> +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) >> +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) >> +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) >> +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) >> +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) >> +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) >> +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) >> +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) >> + >> +/* ROCKCHIP_EDP_ANALOG_CTL_2 */ >> +#define SEL_24M (0x1 << 3) >> + >> +/* ROCKCHIP_EDP_COMMON_INT_STA_1 */ >> +#define VSYNC_DET BIT(7) >> +#define PLL_LOCK_CHG BIT(6) >> +#define SPDIF_ERR BIT(5) >> +#define SPDIF_UNSTBL BIT(4) >> +#define VID_FORMAT_CHG BIT(3) >> +#define AUD_CLK_CHG BIT(2) >> +#define VID_CLK_CHG BIT(1) >> +#define SW_INT BIT(0) >> + >> +/* ROCKCHIP_EDP_COMMON_INT_STA_2 */ >> +#define ENC_EN_CHG BIT(6) >> +#define HW_BKSV_RDY BIT(3) >> +#define HW_SHA_DONE BIT(2) >> +#define HW_AUTH_STATE_CHG BIT(1) >> +#define HW_AUTH_DONE BIT(0) >> + >> +/* ROCKCHIP_EDP_COMMON_INT_STA_3 */ >> +#define AFIFO_UNDER BIT(7) >> +#define AFIFO_OVER BIT(6) >> +#define R0_CHK_FLAG BIT(5) >> + >> +/* ROCKCHIP_EDP_COMMON_INT_STA_4 */ >> +#define PSR_ACTIVE BIT(7) >> +#define PSR_INACTIVE BIT(6) >> +#define SPDIF_BI_PHASE_ERR BIT(5) >> +#define HOTPLUG_CHG BIT(2) >> +#define HPD_LOST BIT(1) >> +#define PLUG BIT(0) >> + >> +/* ROCKCHIP_EDP_INT_STA */ >> +#define INT_HPD BIT(6) >> +#define HW_LT_DONE BIT(5) >> +#define SINK_LOST BIT(3) >> +#define LINK_LOST BIT(2) >> +#define RPLY_RECEIV BIT(1) >> +#define AUX_ERR BIT(0) >> + >> +/* ROCKCHIP_EDP_INT_CTL */ >> +#define INT_CTL 0x3FC >> +#define SOFT_INT_CTRL BIT(2) >> +#define INT_POL BIT(0) >> + >> +/* ROCKCHIP_EDP_SYS_CTL_1 */ >> +#define DET_STA BIT(2) >> +#define FORCE_DET BIT(1) >> +#define DET_CTRL BIT(0) >> + >> +/* ROCKCHIP_EDP_SYS_CTL_2 */ >> +#define CHA_CRI(x) (((x) & 0xf) << 4) >> +#define CHA_STA BIT(2) >> +#define FORCE_CHA BIT(1) >> +#define CHA_CTRL BIT(0) >> + >> +/* ROCKCHIP_EDP_SYS_CTL_3 */ >> +#define HPD_STATUS BIT(6) >> +#define F_HPD BIT(5) >> +#define HPD_CTRL BIT(4) >> +#define HDCP_RDY BIT(3) >> +#define STRM_VALID BIT(2) >> +#define F_VALID BIT(1) >> +#define VALID_CTRL BIT(0) >> + >> +/* ROCKCHIP_EDP_SYS_CTL_4 */ >> +#define FIX_M_AUD BIT(4) >> +#define ENHANCED BIT(3) >> +#define FIX_M_VID BIT(2) >> +#define M_VID_UPDATE_CTRL BIT(0) >> + >> +/* ROCKCHIP_EDP_TRAINING_PTN_SET */ >> +#define SCRAMBLING_DISABLE (0x1 << 5) >> +#define SCRAMBLING_ENABLE (0x0 << 5) >> +#define LINK_QUAL_PATTERN_SET_MASK (0x7 << 2) >> +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) >> +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) >> +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) >> +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) >> +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) >> +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) >> +#define SW_TRAINING_PATTERN_SET_DISABLE (0x0 << 0) >> + >> +/* ROCKCHIP_EDP_HW_LT_CTL */ >> +#define HW_LT_ERR_CODE_MASK 0x70 >> +#define HW_LT_EN BIT(0) >> + >> +/* ROCKCHIP_EDP_LN0_LINK_TRAINING_CTL */ >> +#define PRE_EMPHASIS_SET_MASK (0x3 << 3) >> +#define PRE_EMPHASIS_SET_SHIFT (3) >> + >> +/* ROCKCHIP_EDP_DEBUG_CTL */ >> +#define PLL_LOCK BIT(4) >> +#define F_PLL_LOCK BIT(3) >> +#define PLL_LOCK_CTRL BIT(2) >> +#define POLL_EN BIT(1) >> +#define PN_INV BIT(0) >> + >> +/* ROCKCHIP_EDP_AUX_CH_STA */ >> +#define AUX_BUSY (0x1 << 4) >> +#define AUX_STATUS_MASK (0xf << 0) >> + >> +/* ROCKCHIP_EDP_AUX_CH_DEFER_CTL */ >> +#define DEFER_CTRL_EN (0x1 << 7) >> +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) >> + >> +/* ROCKCHIP_EDP_AUX_RX_COMM */ >> +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) >> +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) >> + >> +/* ROCKCHIP_EDP_BUFFER_DATA_CTL */ >> +#define BUF_CLR (0x1 << 7) >> +#define BUF_DATA_COUNT(x) (((x) & 0xf) << 0) >> + >> +/* ROCKCHIP_EDP_AUX_CH_CTL_1 */ >> +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) >> +#define AUX_TX_COMM_MASK (0xf << 0) >> +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) >> +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) >> +#define AUX_TX_COMM_MOT (0x1 << 2) >> +#define AUX_TX_COMM_WRITE (0x0 << 0) >> +#define AUX_TX_COMM_READ (0x1 << 0) >> + >> +/* OCKCHIP_EDP_AUX_ADDR_7_0 */ >> +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) >> + >> +/* ROCKCHIP_EDP_AUX_ADDR_15_8 */ >> +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) >> + >> +/* ROCKCHIP_EDP_AUX_ADDR_19_16 */ >> +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) >> + >> +/* ROCKCHIP_EDP_AUX_CH_CTL_2 */ >> +#define ADDR_ONLY BIT(1) >> +#define AUX_EN BIT(0) >> + >> +#endif /* _ROCKCHIP_EDP_REG_H */ >> -- >> 1.7.9.5 >> >> > >
On Tue, Sep 23, 2014 at 4:47 AM, cym <cym@rock-chips.com> wrote: > > On Tuesday, September 23, 2014 03:20 AM, Rob Clark wrote: >> >> On Mon, Sep 22, 2014 at 7:02 AM, Mark yao <mark.yao@rock-chips.com> wrote: >>> >>> This adds support for Rockchip soc edp found on rk3288 >>> >>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com> >>> Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com> >>> --- >>> Changes in v2: >>> - fix code sytle >>> - use some define from drm_dp_helper.h >>> - use panel-simple driver for primary display. >>> - remove unnecessary clock clk_24m_parent. >>> >>> Changes in v3: None >>> >>> Changes in v4: None >>> >>> drivers/gpu/drm/rockchip/Kconfig | 9 + >>> drivers/gpu/drm/rockchip/Makefile | 2 + >>> drivers/gpu/drm/rockchip/rockchip_edp_core.c | 853 ++++++++++++++++++ >>> drivers/gpu/drm/rockchip/rockchip_edp_core.h | 309 +++++++ >>> drivers/gpu/drm/rockchip/rockchip_edp_reg.c | 1202 >>> ++++++++++++++++++++++++++ >>> drivers/gpu/drm/rockchip/rockchip_edp_reg.h | 345 ++++++++ >>> 6 files changed, 2720 insertions(+) >>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c >>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h >>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c >>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h >>> >>> diff --git a/drivers/gpu/drm/rockchip/Kconfig >>> b/drivers/gpu/drm/rockchip/Kconfig >>> index 7146c80..04b1f8c 100644 >>> --- a/drivers/gpu/drm/rockchip/Kconfig >>> +++ b/drivers/gpu/drm/rockchip/Kconfig >>> @@ -17,3 +17,12 @@ config DRM_ROCKCHIP >>> management to userspace. This driver does not provides >>> 2D or 3D acceleration; acceleration is performed by other >>> IP found on the SoC. >>> + >>> +config ROCKCHIP_EDP >>> + bool "Rockchip edp support" >>> + depends on DRM_ROCKCHIP >>> + help >>> + Choose this option if you have a Rockchip eDP. >>> + Rockchip rk3288 SoC has eDP TX Controller can be used. >>> + If you have an Embedded DisplayPort Panel, say Y to enable its >>> + driver. >>> diff --git a/drivers/gpu/drm/rockchip/Makefile >>> b/drivers/gpu/drm/rockchip/Makefile >>> index 6e6d468..a0fc3a1 100644 >>> --- a/drivers/gpu/drm/rockchip/Makefile >>> +++ b/drivers/gpu/drm/rockchip/Makefile >>> @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip >>> rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o >>> rockchip_drm_fbdev.o \ >>> rockchip_drm_gem.o rockchip_drm_vop.o >>> >>> +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o >>> rockchip_edp_reg.o >>> + >>> obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o >>> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>> b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>> new file mode 100644 >>> index 0000000..5450d1fa >>> --- /dev/null >>> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>> @@ -0,0 +1,853 @@ >>> +/* >>> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >>> +* Author: >>> +* Andy yan <andy.yan@rock-chips.com> >>> +* Jeff chen <jeff.chen@rock-chips.com> >>> +* >>> +* based on exynos_dp_core.c >>> +* >> >> hmm, did you look at all at drm_dp_helpers? The exynos code probably >> pre-dates the helpers, so might not be the best example to work off >> of.. >> >> If there is actually a valid reason not to use the dp-helpers, then >> you should mention the reasons, at least in the commit msg if not the >> code >> >> BR, >> -R > > Thanks Rob,Because RK3288 eDP controller IP design is similar to exynos.They > from same IP vendors but have some difference. > So we choosed exynos_dp as example to work off of.exynos_dp only used some > defines from drm_dp_helper.h like DPCD. > Hmm, it sounds like it perhaps should be refactored out into a drm_bridge so more of it can be shared between rockchip and exynos. Either way, it should be using the drm_dp_helpers.. That "the code I copied did it wrong" isn't a terribly good reason for new drivers to do it wrong. So NAK for the eDP part until you use the helpers. BR, -R
On Tue, Sep 23, 2014 at 9:56 AM, Rob Clark <robdclark@gmail.com> wrote: > On Tue, Sep 23, 2014 at 4:47 AM, cym <cym@rock-chips.com> wrote: >> >> On Tuesday, September 23, 2014 03:20 AM, Rob Clark wrote: >>> >>> On Mon, Sep 22, 2014 at 7:02 AM, Mark yao <mark.yao@rock-chips.com> wrote: >>>> >>>> This adds support for Rockchip soc edp found on rk3288 >>>> >>>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com> >>>> Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com> >>>> --- >>>> Changes in v2: >>>> - fix code sytle >>>> - use some define from drm_dp_helper.h >>>> - use panel-simple driver for primary display. >>>> - remove unnecessary clock clk_24m_parent. >>>> >>>> Changes in v3: None >>>> >>>> Changes in v4: None >>>> >>>> drivers/gpu/drm/rockchip/Kconfig | 9 + >>>> drivers/gpu/drm/rockchip/Makefile | 2 + >>>> drivers/gpu/drm/rockchip/rockchip_edp_core.c | 853 ++++++++++++++++++ >>>> drivers/gpu/drm/rockchip/rockchip_edp_core.h | 309 +++++++ >>>> drivers/gpu/drm/rockchip/rockchip_edp_reg.c | 1202 >>>> ++++++++++++++++++++++++++ >>>> drivers/gpu/drm/rockchip/rockchip_edp_reg.h | 345 ++++++++ >>>> 6 files changed, 2720 insertions(+) >>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h >>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c >>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h >>>> >>>> diff --git a/drivers/gpu/drm/rockchip/Kconfig >>>> b/drivers/gpu/drm/rockchip/Kconfig >>>> index 7146c80..04b1f8c 100644 >>>> --- a/drivers/gpu/drm/rockchip/Kconfig >>>> +++ b/drivers/gpu/drm/rockchip/Kconfig >>>> @@ -17,3 +17,12 @@ config DRM_ROCKCHIP >>>> management to userspace. This driver does not provides >>>> 2D or 3D acceleration; acceleration is performed by other >>>> IP found on the SoC. >>>> + >>>> +config ROCKCHIP_EDP >>>> + bool "Rockchip edp support" >>>> + depends on DRM_ROCKCHIP >>>> + help >>>> + Choose this option if you have a Rockchip eDP. >>>> + Rockchip rk3288 SoC has eDP TX Controller can be used. >>>> + If you have an Embedded DisplayPort Panel, say Y to enable its >>>> + driver. >>>> diff --git a/drivers/gpu/drm/rockchip/Makefile >>>> b/drivers/gpu/drm/rockchip/Makefile >>>> index 6e6d468..a0fc3a1 100644 >>>> --- a/drivers/gpu/drm/rockchip/Makefile >>>> +++ b/drivers/gpu/drm/rockchip/Makefile >>>> @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip >>>> rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o >>>> rockchip_drm_fbdev.o \ >>>> rockchip_drm_gem.o rockchip_drm_vop.o >>>> >>>> +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o >>>> rockchip_edp_reg.o >>>> + >>>> obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o >>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>> b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>> new file mode 100644 >>>> index 0000000..5450d1fa >>>> --- /dev/null >>>> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>> @@ -0,0 +1,853 @@ >>>> +/* >>>> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >>>> +* Author: >>>> +* Andy yan <andy.yan@rock-chips.com> >>>> +* Jeff chen <jeff.chen@rock-chips.com> >>>> +* >>>> +* based on exynos_dp_core.c >>>> +* >>> >>> hmm, did you look at all at drm_dp_helpers? The exynos code probably >>> pre-dates the helpers, so might not be the best example to work off >>> of.. >>> >>> If there is actually a valid reason not to use the dp-helpers, then >>> you should mention the reasons, at least in the commit msg if not the >>> code >>> >>> BR, >>> -R >> >> Thanks Rob,Because RK3288 eDP controller IP design is similar to exynos.They >> from same IP vendors but have some difference. >> So we choosed exynos_dp as example to work off of.exynos_dp only used some >> defines from drm_dp_helper.h like DPCD. >> > > > Hmm, it sounds like it perhaps should be refactored out into a > drm_bridge so more of it can be shared between rockchip and exynos. > > Either way, it should be using the drm_dp_helpers.. That "the code I > copied did it wrong" isn't a terribly good reason for new drivers to > do it wrong. > > So NAK for the eDP part until you use the helpers. and btw, if it wasn't clear, go ahead and at least repost the core part of the driver.. the first patch just needed a few small tweaks to get my r-b even if it takes longer to sort out something sane for the DP part.. BR, -R
On 2014/9/24 7:35, Rob Clark wrote: > On Tue, Sep 23, 2014 at 9:56 AM, Rob Clark <robdclark@gmail.com> wrote: >> On Tue, Sep 23, 2014 at 4:47 AM, cym <cym@rock-chips.com> wrote: >>> On Tuesday, September 23, 2014 03:20 AM, Rob Clark wrote: >>>> On Mon, Sep 22, 2014 at 7:02 AM, Mark yao <mark.yao@rock-chips.com> wrote: >>>>> This adds support for Rockchip soc edp found on rk3288 >>>>> >>>>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com> >>>>> Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com> >>>>> --- >>>>> Changes in v2: >>>>> - fix code sytle >>>>> - use some define from drm_dp_helper.h >>>>> - use panel-simple driver for primary display. >>>>> - remove unnecessary clock clk_24m_parent. >>>>> >>>>> Changes in v3: None >>>>> >>>>> Changes in v4: None >>>>> >>>>> drivers/gpu/drm/rockchip/Kconfig | 9 + >>>>> drivers/gpu/drm/rockchip/Makefile | 2 + >>>>> drivers/gpu/drm/rockchip/rockchip_edp_core.c | 853 ++++++++++++++++++ >>>>> drivers/gpu/drm/rockchip/rockchip_edp_core.h | 309 +++++++ >>>>> drivers/gpu/drm/rockchip/rockchip_edp_reg.c | 1202 >>>>> ++++++++++++++++++++++++++ >>>>> drivers/gpu/drm/rockchip/rockchip_edp_reg.h | 345 ++++++++ >>>>> 6 files changed, 2720 insertions(+) >>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h >>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c >>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h >>>>> >>>>> diff --git a/drivers/gpu/drm/rockchip/Kconfig >>>>> b/drivers/gpu/drm/rockchip/Kconfig >>>>> index 7146c80..04b1f8c 100644 >>>>> --- a/drivers/gpu/drm/rockchip/Kconfig >>>>> +++ b/drivers/gpu/drm/rockchip/Kconfig >>>>> @@ -17,3 +17,12 @@ config DRM_ROCKCHIP >>>>> management to userspace. This driver does not provides >>>>> 2D or 3D acceleration; acceleration is performed by other >>>>> IP found on the SoC. >>>>> + >>>>> +config ROCKCHIP_EDP >>>>> + bool "Rockchip edp support" >>>>> + depends on DRM_ROCKCHIP >>>>> + help >>>>> + Choose this option if you have a Rockchip eDP. >>>>> + Rockchip rk3288 SoC has eDP TX Controller can be used. >>>>> + If you have an Embedded DisplayPort Panel, say Y to enable its >>>>> + driver. >>>>> diff --git a/drivers/gpu/drm/rockchip/Makefile >>>>> b/drivers/gpu/drm/rockchip/Makefile >>>>> index 6e6d468..a0fc3a1 100644 >>>>> --- a/drivers/gpu/drm/rockchip/Makefile >>>>> +++ b/drivers/gpu/drm/rockchip/Makefile >>>>> @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip >>>>> rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o >>>>> rockchip_drm_fbdev.o \ >>>>> rockchip_drm_gem.o rockchip_drm_vop.o >>>>> >>>>> +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o >>>>> rockchip_edp_reg.o >>>>> + >>>>> obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o >>>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>>> b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>>> new file mode 100644 >>>>> index 0000000..5450d1fa >>>>> --- /dev/null >>>>> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c >>>>> @@ -0,0 +1,853 @@ >>>>> +/* >>>>> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >>>>> +* Author: >>>>> +* Andy yan <andy.yan@rock-chips.com> >>>>> +* Jeff chen <jeff.chen@rock-chips.com> >>>>> +* >>>>> +* based on exynos_dp_core.c >>>>> +* >>>> hmm, did you look at all at drm_dp_helpers? The exynos code probably >>>> pre-dates the helpers, so might not be the best example to work off >>>> of.. >>>> >>>> If there is actually a valid reason not to use the dp-helpers, then >>>> you should mention the reasons, at least in the commit msg if not the >>>> code >>>> >>>> BR, >>>> -R >>> Thanks Rob,Because RK3288 eDP controller IP design is similar to exynos.They >>> from same IP vendors but have some difference. >>> So we choosed exynos_dp as example to work off of.exynos_dp only used some >>> defines from drm_dp_helper.h like DPCD. >>> >> >> Hmm, it sounds like it perhaps should be refactored out into a >> drm_bridge so more of it can be shared between rockchip and exynos. >> >> Either way, it should be using the drm_dp_helpers.. That "the code I >> copied did it wrong" isn't a terribly good reason for new drivers to >> do it wrong. >> >> So NAK for the eDP part until you use the helpers. > and btw, if it wasn't clear, go ahead and at least repost the core > part of the driver.. the first patch just needed a few small tweaks to > get my r-b even if it takes longer to sort out something sane for the > DP part.. > > BR, > -R thanks,I will modify the core part of the driver. BR, -Jeff > _______________________________________________ > Linux-rockchip mailing list > Linux-rockchip@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-rockchip > > >
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 7146c80..04b1f8c 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -17,3 +17,12 @@ config DRM_ROCKCHIP management to userspace. This driver does not provides 2D or 3D acceleration; acceleration is performed by other IP found on the SoC. + +config ROCKCHIP_EDP + bool "Rockchip edp support" + depends on DRM_ROCKCHIP + help + Choose this option if you have a Rockchip eDP. + Rockchip rk3288 SoC has eDP TX Controller can be used. + If you have an Embedded DisplayPort Panel, say Y to enable its + driver. diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 6e6d468..a0fc3a1 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ rockchip_drm_gem.o rockchip_drm_vop.o +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o rockchip_edp_reg.o + obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c b/drivers/gpu/drm/rockchip/rockchip_edp_core.c new file mode 100644 index 0000000..5450d1fa --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c @@ -0,0 +1,853 @@ +/* +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd +* Author: +* Andy yan <andy.yan@rock-chips.com> +* Jeff chen <jeff.chen@rock-chips.com> +* +* based on exynos_dp_core.c +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_panel.h> +#include <drm/drm_of.h> + +#include <linux/component.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <video/of_videomode.h> +#include <video/videomode.h> + +#include "rockchip_edp_core.h" + +#define connector_to_edp(c) \ + container_of(c, struct rockchip_edp_device, connector) + +#define encoder_to_edp(c) \ + container_of(c, struct rockchip_edp_device, encoder) + +static struct rockchip_edp_soc_data soc_data[2] = { + /* rk3288 */ + {.grf_soc_con6 = 0x025c, + .grf_soc_con12 = 0x0274}, + /* no edp switching needed */ + {.grf_soc_con6 = -1, + .grf_soc_con12 = -1}, +}; + +static const struct of_device_id rockchip_edp_dt_ids[] = { + {.compatible = "rockchip,rk3288-edp", + .data = (void *)&soc_data[0] }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_edp_dt_ids); + +static int rockchip_edp_clk_enable(struct rockchip_edp_device *edp) +{ + int ret = 0; + + if (!edp->clk_on) { + ret = clk_prepare_enable(edp->pclk); + if (ret < 0) { + dev_err(edp->dev, "cannot enable edp pclk %d\n", ret); + goto err_pclk; + } + + ret = clk_prepare_enable(edp->clk_edp); + if (ret < 0) { + dev_err(edp->dev, "cannot enable clk_edp %d\n", ret); + goto err_clk_edp; + } + + ret = clk_set_rate(edp->clk_24m, 24000000); + if (ret < 0) { + dev_err(edp->dev, "cannot set edp clk_24m %d\n", + ret); + goto err_clk_24m; + } + + ret = clk_prepare_enable(edp->clk_24m); + if (ret < 0) { + dev_err(edp->dev, "cannot enable edp clk_24m %d\n", + ret); + goto err_clk_24m; + } + + edp->clk_on = true; + } + + return 0; + +err_clk_24m: + clk_disable_unprepare(edp->clk_edp); +err_clk_edp: + clk_disable_unprepare(edp->pclk); +err_pclk: + edp->clk_on = false; + + return ret; +} + +static int rockchip_edp_clk_disable(struct rockchip_edp_device *edp) +{ + if (edp->clk_on) { + clk_disable_unprepare(edp->pclk); + clk_disable_unprepare(edp->clk_edp); + clk_disable_unprepare(edp->clk_24m); + edp->clk_on = false; + } + + return 0; +} + +static int rockchip_edp_pre_init(struct rockchip_edp_device *edp) +{ + u32 val; + int ret; + + val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16); + ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val); + if (ret != 0) { + dev_err(edp->dev, "Could not write to GRF: %d\n", ret); + return ret; + } + + reset_control_assert(edp->rst); + usleep_range(10, 20); + reset_control_deassert(edp->rst); + + return 0; +} + +static int rockchip_edp_init_edp(struct rockchip_edp_device *edp) +{ + rockchip_edp_reset(edp); + rockchip_edp_init_refclk(edp); + rockchip_edp_init_interrupt(edp); + rockchip_edp_enable_sw_function(edp); + rockchip_edp_init_analog_func(edp); + rockchip_edp_init_hpd(edp); + rockchip_edp_init_aux(edp); + + return 0; +} + +static int rockchip_edp_get_max_rx_bandwidth( + struct rockchip_edp_device *edp, + u8 *bandwidth) +{ + u8 data; + int retval; + + /* + * For DP rev.1.1, Maximum link rate of Main Link lanes + * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps + */ + retval = rockchip_edp_read_byte_from_dpcd( + edp, DP_MAX_LINK_RATE, &data); + if (retval < 0) + *bandwidth = 0; + else + *bandwidth = data; + + return retval; +} + +static int rockchip_edp_get_max_rx_lane_count(struct rockchip_edp_device *edp, + u8 *lane_count) +{ + u8 data; + int retval; + + /* + * For DP rev.1.1, Maximum number of Main Link lanes + * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes + */ + retval = rockchip_edp_read_byte_from_dpcd( + edp, DP_MAX_LANE_COUNT, &data); + if (retval < 0) + *lane_count = 0; + else + *lane_count = DPCD_MAX_LANE_COUNT(data); + + return retval; +} + +static int rockchip_edp_init_training(struct rockchip_edp_device *edp) +{ + int retval; + + /* + * MACRO_RST must be applied after the PLL_LOCK to avoid + * the DP inter pair skew issue for at least 10 us + */ + rockchip_edp_reset_macro(edp); + + retval = rockchip_edp_get_max_rx_bandwidth( + edp, &edp->link_train.link_rate); + retval = rockchip_edp_get_max_rx_lane_count( + edp, &edp->link_train.lane_count); + dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n", + edp->link_train.link_rate * 27 / 100, + edp->link_train.link_rate * 27 % 100, + edp->link_train.lane_count); + + if ((edp->link_train.link_rate != DP_LINK_BW_1_62) && + (edp->link_train.link_rate != DP_LINK_BW_2_7)) { + dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n" + "use default link rate:%d.%dGps\n", + edp->link_train.link_rate, + edp->video_info.link_rate * 27 / 100, + edp->video_info.link_rate * 27 % 100); + edp->link_train.link_rate = edp->video_info.link_rate; + } + + if (edp->link_train.lane_count == 0) { + dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n" + "use default lanes:%d\n", + edp->link_train.lane_count, + edp->video_info.lane_count); + edp->link_train.lane_count = edp->video_info.lane_count; + } + + rockchip_edp_analog_power_ctr(edp, 1); + + return 0; +} + +static int rockchip_edp_hw_link_training(struct rockchip_edp_device *edp) +{ + u32 cnt = 50; + u32 val; + + /* Set link rate and count as you want to establish*/ + rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate); + rockchip_edp_set_lane_count(edp, edp->link_train.lane_count); + rockchip_edp_hw_link_training_en(edp); + val = rockchip_edp_wait_hw_lt_done(edp); + while (val) { + if (cnt-- <= 0) { + dev_err(edp->dev, "hw lt timeout"); + return -ETIMEDOUT; + } + mdelay(1); + val = rockchip_edp_wait_hw_lt_done(edp); + } + + val = rockchip_edp_get_hw_lt_status(edp); + if (val) + dev_err(edp->dev, "hw lt err:%d\n", val); + + return val; +} + +static int rockchip_edp_set_link_train(struct rockchip_edp_device *edp) +{ + int retval; + + rockchip_edp_init_training(edp); + + retval = rockchip_edp_hw_link_training(edp); + if (retval < 0) + dev_err(edp->dev, "DP hw LT failed!\n"); + + return retval; +} + +static int rockchip_edp_config_video(struct rockchip_edp_device *edp, + struct video_info *video_info) +{ + int retval = 0; + int timeout_loop = 0; + int done_count = 0; + + rockchip_edp_config_video_slave_mode(edp, video_info); + + rockchip_edp_set_video_color_format(edp, video_info->color_depth, + video_info->color_space, + video_info->dynamic_range, + video_info->ycbcr_coeff); + + if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) { + dev_err(edp->dev, "PLL is not locked yet.\n"); + return -EINVAL; + } + + for (;;) { + timeout_loop++; + if (rockchip_edp_is_slave_video_stream_clock_on(edp) == 0) + break; + + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { + dev_err(edp->dev, "Timeout of video streamclk ok\n"); + return -ETIMEDOUT; + } + + udelay(1); + } + + /* Set to use the register calculated M/N video */ + rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); + + /* Disable video mute */ + rockchip_edp_enable_video_mute(edp, 0); + + /* Configure video slave mode */ + rockchip_edp_enable_video_master(edp, 0); + + /* Enable video */ + rockchip_edp_start_video(edp); + + timeout_loop = 0; + + for (;;) { + timeout_loop++; + if (rockchip_edp_is_video_stream_on(edp) == 0) { + done_count++; + if (done_count > 10) + break; + } else if (done_count) { + done_count = 0; + } + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { + dev_err(edp->dev, "Timeout of video streamclk ok\n"); + return -ETIMEDOUT; + } + + mdelay(1); + } + + if (retval != 0) + dev_err(edp->dev, "Video stream is not detected!\n"); + + return retval; +} + +static irqreturn_t rockchip_edp_isr(int irq, void *arg) +{ + struct rockchip_edp_device *edp = arg; + enum dp_irq_type irq_type; + + irq_type = rockchip_edp_get_irq_type(edp); + switch (irq_type) { + case DP_IRQ_TYPE_HP_CABLE_IN: + dev_dbg(edp->dev, "Received irq - cable in\n"); + rockchip_edp_clear_hotplug_interrupts(edp); + break; + case DP_IRQ_TYPE_HP_CABLE_OUT: + dev_dbg(edp->dev, "Received irq - cable out\n"); + rockchip_edp_clear_hotplug_interrupts(edp); + break; + case DP_IRQ_TYPE_HP_CHANGE: + /* + * We get these change notifications once in a while, but there + * is nothing we can do with them. Just ignore it for now and + * only handle cable changes. + */ + dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n"); + rockchip_edp_clear_hotplug_interrupts(edp); + break; + default: + dev_err(edp->dev, "Received irq - unknown type[%x]!\n", + irq_type); + rockchip_edp_clear_hotplug_interrupts(edp); + break; + } + + return IRQ_HANDLED; +} + +static void rockchip_edp_commit(struct drm_encoder *encoder) +{ + struct rockchip_edp_device *edp = encoder_to_edp(encoder); + int ret; + + ret = rockchip_edp_set_link_train(edp); + if (ret) + dev_err(edp->dev, "link train failed!\n"); + else + dev_dbg(edp->dev, "link training success.\n"); + + rockchip_edp_set_lane_count(edp, edp->link_train.lane_count); + rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate); + rockchip_edp_init_video(edp); + + ret = rockchip_edp_config_video(edp, &edp->video_info); + if (ret) + dev_err(edp->dev, "unable to config video\n"); +} + +static void rockchip_edp_poweron(struct drm_encoder *encoder) +{ + struct rockchip_edp_device *edp = encoder_to_edp(encoder); + int ret; + + if (edp->dpms_mode == DRM_MODE_DPMS_ON) + return; + + if (edp->panel) + edp->panel->funcs->enable(edp->panel); + + ret = rockchip_edp_clk_enable(edp); + if (ret < 0) { + dev_err(edp->dev, "cannot enable edp clk %d\n", ret); + return; + } + + ret = rockchip_edp_pre_init(edp); + if (ret < 0) { + dev_err(edp->dev, "edp pre init fail %d\n", ret); + return; + } + + ret = rockchip_edp_init_edp(edp); + if (ret < 0) { + dev_err(edp->dev, "edp init fail %d\n", ret); + return; + } + + enable_irq(edp->irq); + rockchip_edp_commit(encoder); +} + +static void rockchip_edp_poweroff(struct drm_encoder *encoder) +{ + struct rockchip_edp_device *edp = encoder_to_edp(encoder); + + if (edp->dpms_mode == DRM_MODE_DPMS_OFF) + return; + + disable_irq(edp->irq); + rockchip_edp_reset(edp); + rockchip_edp_analog_power_ctr(edp, 0); + rockchip_edp_clk_disable(edp); + if (edp->panel) + edp->panel->funcs->disable(edp->panel); +} + +static enum drm_connector_status +rockchip_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void rockchip_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static struct drm_connector_funcs rockchip_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = rockchip_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = rockchip_connector_destroy, +}; + +static int rockchip_connector_get_modes(struct drm_connector *connector) +{ + struct rockchip_edp_device *edp = connector_to_edp(connector); + struct drm_panel *panel = edp->panel; + + return panel->funcs->get_modes(panel); +} + +static struct drm_encoder * + rockchip_connector_best_encoder(struct drm_connector *connector) +{ + struct rockchip_edp_device *edp = connector_to_edp(connector); + + return &edp->encoder; +} + +static enum drm_mode_status rockchip_connector_mode_valid( + struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* TODO(rk): verify that the mode is really valid */ + return MODE_OK; +} + +static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = { + .get_modes = rockchip_connector_get_modes, + .mode_valid = rockchip_connector_mode_valid, + .best_encoder = rockchip_connector_best_encoder, +}; + +static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct rockchip_edp_device *edp = encoder_to_edp(encoder); + + if (edp->dpms_mode == mode) + return; + + switch (mode) { + case DRM_MODE_DPMS_ON: + rockchip_edp_poweron(encoder); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + rockchip_edp_poweroff(encoder); + break; + default: + break; + } + + edp->dpms_mode = mode; +} + +static bool +rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (!adjusted_mode->private) { + struct rockchip_display_mode *priv_mode; + + priv_mode = kzalloc(sizeof(*priv_mode), GFP_KERNEL); + priv_mode->out_type = ROCKCHIP_DISPLAY_TYPE_EDP; + adjusted_mode->private = (int *)priv_mode; + } + + return true; +} + +static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct rockchip_edp_device *edp = encoder_to_edp(encoder); + u32 val; + int ret; + + ret = rockchip_drm_encoder_get_mux_id(edp->dev->of_node, encoder); + if (ret < 0) + return; + + if (ret == ROCKCHIP_CRTC_VOPL) + val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16); + else + val = EDP_SEL_VOP_LIT << 16; + + dev_info(edp->dev, "vop %s output to edp\n", + (ret == ROCKCHIP_CRTC_VOPL) ? "LIT" : "BIG"); + ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val); + if (ret != 0) { + dev_err(edp->dev, "Could not write to GRF: %d\n", ret); + return; + } + + memcpy(&edp->mode, adjusted, sizeof(*mode)); +} + +static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void rockchip_drm_encoder_commit(struct drm_encoder *encoder) +{ + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static void rockchip_drm_encoder_disable(struct drm_encoder *encoder) +{ + struct drm_plane *plane; + struct drm_device *dev = encoder->dev; + + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); + + /* all planes connected to this encoder should be also disabled. */ + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + if (plane->crtc && (plane->crtc == encoder->crtc)) + plane->funcs->disable_plane(plane); + } +} + +static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = { + .dpms = rockchip_drm_encoder_dpms, + .mode_fixup = rockchip_drm_encoder_mode_fixup, + .mode_set = rockchip_drm_encoder_mode_set, + .prepare = rockchip_drm_encoder_prepare, + .commit = rockchip_drm_encoder_commit, + .disable = rockchip_drm_encoder_disable, +}; + +static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static struct drm_encoder_funcs rockchip_encoder_funcs = { + .destroy = rockchip_drm_encoder_destroy, +}; + +static int rockchip_edp_init(struct rockchip_edp_device *edp) +{ + struct device *dev = edp->dev; + struct device_node *np = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + const struct of_device_id *match; + int ret; + + if (!np) { + dev_err(dev, "Missing device tree node.\n"); + return -EINVAL; + } + + match = of_match_node(rockchip_edp_dt_ids, np); + edp->soc_data = (struct rockchip_edp_soc_data *)match->data; + /* + * The control bit is located in the GRF register space. + */ + if (edp->soc_data->grf_soc_con6 >= 0) { + edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(edp->grf)) { + dev_err(dev, + "rk3288-edp needs rockchip,grf property\n"); + return PTR_ERR(edp->grf); + } + } + + edp->video_info.h_sync_polarity = 0; + edp->video_info.v_sync_polarity = 0; + edp->video_info.interlaced = 0; + edp->video_info.color_space = CS_RGB; + edp->video_info.dynamic_range = VESA; + edp->video_info.ycbcr_coeff = COLOR_YCBCR601; + edp->video_info.color_depth = COLOR_8; + + edp->video_info.link_rate = DP_LINK_BW_1_62; + edp->video_info.lane_count = LANE_CNT4; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + edp->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(edp->regs)) { + dev_err(dev, "ioremap reg failed\n"); + return PTR_ERR(edp->regs); + } + + edp->clk_edp = devm_clk_get(dev, "clk_edp"); + if (IS_ERR(edp->clk_edp)) { + dev_err(dev, "cannot get clk_edp\n"); + return PTR_ERR(edp->clk_edp); + } + + edp->clk_24m = devm_clk_get(dev, "clk_edp_24m"); + if (IS_ERR(edp->clk_24m)) { + dev_err(dev, "cannot get clk_edp_24m\n"); + return PTR_ERR(edp->clk_24m); + } + + edp->pclk = devm_clk_get(dev, "pclk_edp"); + if (IS_ERR(edp->pclk)) { + dev_err(dev, "cannot get pclk\n"); + return PTR_ERR(edp->pclk); + } + + edp->rst = devm_reset_control_get(dev, "edp"); + if (IS_ERR(edp->rst)) { + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(edp->rst); + } + + ret = rockchip_edp_clk_enable(edp); + if (ret < 0) { + dev_err(edp->dev, "cannot enable edp clk %d\n", ret); + return ret; + } + + ret = rockchip_edp_pre_init(edp); + if (ret < 0) { + dev_err(edp->dev, "failed to pre init %d\n", ret); + return ret; + } + + edp->irq = platform_get_irq(pdev, 0); + if (edp->irq < 0) { + dev_err(dev, "cannot find IRQ\n"); + return edp->irq; + } + + ret = devm_request_irq(dev, edp->irq, rockchip_edp_isr, 0, + dev_name(dev), edp); + if (ret) { + dev_err(dev, "cannot claim IRQ %d\n", edp->irq); + return ret; + } + + disable_irq_nosync(edp->irq); + + edp->dpms_mode = DRM_MODE_DPMS_OFF; + + dev_set_name(edp->dev, "rockchip-edp"); + + return 0; +} + +static int rockchip_edp_bind(struct device *dev, struct device *master, + void *data) +{ + struct rockchip_edp_device *edp = dev_get_drvdata(dev); + struct drm_encoder *encoder; + struct drm_connector *connector; + struct drm_device *drm_dev = data; + int ret; + + ret = rockchip_edp_init(edp); + if (ret < 0) + return ret; + + edp->drm_dev = drm_dev; + + encoder = &edp->encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, + dev->of_node); + DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + + ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs, + DRM_MODE_ENCODER_LVDS); + if (ret) { + DRM_ERROR("failed to initialize encoder with drm\n"); + return ret; + } + + drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs); + + connector = &edp->connector; + connector->polled = DRM_CONNECTOR_POLL_HPD; + connector->dpms = DRM_MODE_DPMS_OFF; + + ret = drm_connector_init(drm_dev, connector, + &rockchip_connector_funcs, + DRM_MODE_CONNECTOR_eDP); + if (ret) { + DRM_ERROR("failed to initialize connector with drm\n"); + goto err_free_encoder; + } + + drm_connector_helper_add(connector, + &rockchip_connector_helper_funcs); + + ret = drm_sysfs_connector_add(connector); + if (ret) { + DRM_ERROR("failed to add drm_sysfs\n"); + goto err_free_connector; + } + + ret = drm_mode_connector_attach_encoder(connector, encoder); + if (ret) { + DRM_ERROR("failed to attach connector and encoder\n"); + goto err_free_connector_sysfs; + } + + ret = drm_panel_attach(edp->panel, connector); + if (ret) { + DRM_ERROR("failed to attach connector and encoder\n"); + goto err_free_connector_sysfs; + } + + return 0; + +err_free_connector_sysfs: + drm_sysfs_connector_remove(connector); +err_free_connector: + drm_connector_cleanup(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); + return ret; +} + +static void rockchip_edp_unbind(struct device *dev, struct device *master, + void *data) +{ + struct rockchip_edp_device *edp = dev_get_drvdata(dev); + struct drm_encoder *encoder; + + encoder = &edp->encoder; + + if (edp->panel) + drm_panel_detach(edp->panel); + + rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); + encoder->funcs->destroy(encoder); + drm_sysfs_connector_remove(&edp->connector); + drm_connector_cleanup(&edp->connector); + drm_encoder_cleanup(encoder); +} + +static const struct component_ops rockchip_edp_component_ops = { + .bind = rockchip_edp_bind, + .unbind = rockchip_edp_unbind, +}; + +static int rockchip_edp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct drm_panel *panel; + struct device_node *panel_node; + struct rockchip_edp_device *edp; + + if (!dev->of_node) { + dev_err(dev, "can't find eDP devices\n"); + return -ENODEV; + } + + panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0); + if (!panel_node) { + DRM_ERROR("failed to find diaplay panel\n"); + return -ENODEV; + } + + panel = of_drm_find_panel(panel_node); + if (!panel) { + DRM_ERROR("failed to find diaplay panel\n"); + of_node_put(panel_node); + return -EPROBE_DEFER; + } + + of_node_put(panel_node); + + edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL); + if (!edp) + return -ENOMEM; + edp->dev = dev; + edp->panel = panel; + platform_set_drvdata(pdev, edp); + + return component_add(dev, &rockchip_edp_component_ops); +} + +static int rockchip_edp_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &rockchip_edp_component_ops); + + return 0; +} + +static struct platform_driver rockchip_edp_driver = { + .probe = rockchip_edp_probe, + .remove = rockchip_edp_remove, + .driver = { + .name = "rockchip-edp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rockchip_edp_dt_ids), + }, +}; + +module_platform_driver(rockchip_edp_driver); + +MODULE_AUTHOR("Jeff chen <jeff.chen@rock-chips.com>"); +MODULE_DESCRIPTION("ROCKCHIP EDP Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.h b/drivers/gpu/drm/rockchip/rockchip_edp_core.h new file mode 100644 index 0000000..c13325f --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.h @@ -0,0 +1,309 @@ +/* +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd +* Author: +* Andy yan <andy.yan@rock-chips.com> +* Jeff chen <jeff.chen@rock-chips.com> +* +* based on exynos_dp_core.h +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#ifndef _ROCKCHIP_EDP_CORE_H +#define _ROCKCHIP_EDP_CORE_H + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_panel.h> +#include "rockchip_drm_drv.h" + +#define DP_TIMEOUT_LOOP_CNT 100 +#define MAX_CR_LOOP 5 +#define MAX_EQ_LOOP 5 + +#define GRF_EDP_REF_CLK_SEL_INTER (1 << 4) +#define GRF_EDP_HDCP_EN (1 << 15) +#define GRF_EDP_BIST_EN (1 << 14) +#define GRF_EDP_MEM_CTL_BY_EDP (1 << 13) +#define GRF_EDP_SECURE_EN (1 << 3) +#define EDP_SEL_VOP_LIT (1 << 5) + +enum link_lane_count_type { + LANE_CNT1 = 1, + LANE_CNT2 = 2, + LANE_CNT4 = 4 +}; + +enum link_training_state { + LT_START, + LT_CLK_RECOVERY, + LT_EQ_TRAINING, + FINISHED, + FAILED +}; + +enum voltage_swing_level { + VOLTAGE_LEVEL_0, + VOLTAGE_LEVEL_1, + VOLTAGE_LEVEL_2, + VOLTAGE_LEVEL_3, +}; + +enum pre_emphasis_level { + PRE_EMPHASIS_LEVEL_0, + PRE_EMPHASIS_LEVEL_1, + PRE_EMPHASIS_LEVEL_2, + PRE_EMPHASIS_LEVEL_3, +}; + +enum pattern_set { + PRBS7, + D10_2, + TRAINING_PTN1, + TRAINING_PTN2, + DP_NONE +}; + +enum color_space { + CS_RGB, + CS_YCBCR422, + CS_YCBCR444 +}; + +enum color_depth { + COLOR_6, + COLOR_8, + COLOR_10, + COLOR_12 +}; + +enum color_coefficient { + COLOR_YCBCR601, + COLOR_YCBCR709 +}; + +enum dynamic_range { + VESA, + CEA +}; + +enum pll_status { + DP_PLL_UNLOCKED, + DP_PLL_LOCKED +}; + +enum clock_recovery_m_value_type { + CALCULATED_M, + REGISTER_M +}; + +enum video_timing_recognition_type { + VIDEO_TIMING_FROM_CAPTURE, + VIDEO_TIMING_FROM_REGISTER +}; + +enum analog_power_block { + AUX_BLOCK, + CH0_BLOCK, + CH1_BLOCK, + CH2_BLOCK, + CH3_BLOCK, + ANALOG_TOTAL, + POWER_ALL +}; + +enum dp_irq_type { + DP_IRQ_TYPE_HP_CABLE_IN, + DP_IRQ_TYPE_HP_CABLE_OUT, + DP_IRQ_TYPE_HP_CHANGE, + DP_IRQ_TYPE_UNKNOWN, +}; + +struct video_info { + char *name; + + bool h_sync_polarity; + bool v_sync_polarity; + bool interlaced; + + enum color_space color_space; + enum dynamic_range dynamic_range; + enum color_coefficient ycbcr_coeff; + enum color_depth color_depth; + + u8 link_rate; + enum link_lane_count_type lane_count; +}; + +struct link_train { + int eq_loop; + int cr_loop[4]; + + u8 link_rate; + u8 lane_count; + u8 training_lane[4]; + + enum link_training_state lt_state; +}; + +/* + * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds + */ +struct rockchip_edp_soc_data { + int grf_soc_con6; + int grf_soc_con12; +}; + +struct rockchip_edp_device { + struct device *dev; + struct drm_device *drm_dev; + struct drm_panel *panel; + struct drm_connector connector; + struct drm_encoder encoder; + struct drm_display_mode mode; + + struct rockchip_edp_soc_data *soc_data; + + void __iomem *regs; + struct regmap *grf; + unsigned int irq; + struct clk *clk_edp; + struct clk *clk_24m_parent; + struct clk *clk_24m; + struct clk *pclk; + struct reset_control *rst; + struct link_train link_train; + struct video_info video_info; + bool clk_on; + + int dpms_mode; +}; + +void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp, + bool enable); +void rockchip_edp_stop_video(struct rockchip_edp_device *edp); +void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable); +void rockchip_edp_init_refclk(struct rockchip_edp_device *edp); +void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp); +void rockchip_edp_reset(struct rockchip_edp_device *edp); +void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp); +u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp); +void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp, + bool enable); +void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp); +void rockchip_edp_init_hpd(struct rockchip_edp_device *edp); +void rockchip_edp_reset_aux(struct rockchip_edp_device *edp); +void rockchip_edp_init_aux(struct rockchip_edp_device *edp); +int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp); +void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp); +int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp); +int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp, + unsigned int reg_addr, + unsigned char data); +int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp, + unsigned int reg_addr, + unsigned char *data); +int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp, + unsigned int device_addr, + unsigned int reg_addr); +int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int *data); +int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int count, + unsigned char edid[]); +void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp, + u32 bwtype); +void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp, + u32 *bwtype); +void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, + u32 count); +void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, + u32 *count); +void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp, + bool enable); +void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp, + enum pattern_set pattern); +void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp, + u32 level); +void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp, + u32 level); +void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp, + u32 level); +void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp, + u32 level); +void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp, + u32 training_lane); +void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp, + u32 training_lane); +void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp, + u32 training_lane); +void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp, + u32 training_lane); +u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp); +u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp); +u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp); +u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp); +void rockchip_edp_reset_macro(struct rockchip_edp_device *edp); +int rockchip_edp_init_video(struct rockchip_edp_device *edp); + +void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp, + u32 color_depth, + u32 color_space, + u32 dynamic_range, + u32 coeff); +int +rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp); +void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value); +void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp, + u32 type); +void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp, + bool enable); +void rockchip_edp_start_video(struct rockchip_edp_device *edp); +int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp); +void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp, + struct video_info *video_info); +void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp); +void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp); +void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp); +int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp); +int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp); +enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp); +void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp); + +/* I2C EDID Chip ID, Slave Address */ +#define I2C_EDID_DEVICE_ADDR 0x50 +#define I2C_E_EDID_DEVICE_ADDR 0x30 + +/* DPCD_ADDR_MAX_LANE_COUNT */ +#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) +#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) + +/* DPCD_ADDR_LANE_COUNT_SET */ +#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f) + +/* DPCD_ADDR_TRAINING_LANE0_SET */ +#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3) +#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3) +#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0) +#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3) + +#endif /* _ROCKCHIP_EDP_CORE_H */ diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.c b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c new file mode 100644 index 0000000..f6d641c --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c @@ -0,0 +1,1202 @@ +/* +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd +* Author: +* Andy yan <andy.yan@rock-chips.com> +* Jeff chen <jeff.chen@rock-chips.com> +* +* based on exynos_dp_reg.c +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "rockchip_edp_core.h" +#include "rockchip_edp_reg.h" + +void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp, + bool enable) +{ + u32 val; + + if (enable) { + val = readl(edp->regs + VIDEO_CTL_1); + val |= VIDEO_MUTE; + writel(val, edp->regs + VIDEO_CTL_1); + } else { + val = readl(edp->regs + VIDEO_CTL_1); + val &= ~VIDEO_MUTE; + writel(val, edp->regs + VIDEO_CTL_1); + } +} + +void rockchip_edp_stop_video(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + VIDEO_CTL_1); + val &= ~VIDEO_EN; + writel(val, edp->regs + VIDEO_CTL_1); +} + +void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable) +{ + u32 val; + + if (enable) + val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 | + LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3; + else + val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; + + writel(val, edp->regs + LANE_MAP); +} + +void rockchip_edp_init_refclk(struct rockchip_edp_device *edp) +{ + writel(SEL_24M, edp->regs + ANALOG_CTL_2); + writel(REF_CLK_24M, edp->regs + PLL_REG_1); + + writel(0x95, edp->regs + PLL_REG_2); + writel(0x40, edp->regs + PLL_REG_3); + writel(0x58, edp->regs + PLL_REG_4); + writel(0x22, edp->regs + PLL_REG_5); + writel(0x19, edp->regs + SSC_REG); + writel(0x87, edp->regs + TX_REG_COMMON); + writel(0x03, edp->regs + DP_AUX); + writel(0x46, edp->regs + DP_BIAS); + writel(0x55, edp->regs + DP_RESERVE2); +} + +void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp) +{ + /* Set interrupt pin assertion polarity as high */ + writel(INT_POL, edp->regs + INT_CTL); + + /* Clear pending valisers */ + writel(0xff, edp->regs + COMMON_INT_STA_1); + writel(0x4f, edp->regs + COMMON_INT_STA_2); + writel(0xff, edp->regs + COMMON_INT_STA_3); + writel(0x27, edp->regs + COMMON_INT_STA_4); + + writel(0x7f, edp->regs + DP_INT_STA); + + /* 0:mask,1: unmask */ + writel(0x00, edp->regs + COMMON_INT_MASK_1); + writel(0x00, edp->regs + COMMON_INT_MASK_2); + writel(0x00, edp->regs + COMMON_INT_MASK_3); + writel(0x00, edp->regs + COMMON_INT_MASK_4); + writel(0x00, edp->regs + DP_INT_STA_MASK); +} + +void rockchip_edp_reset(struct rockchip_edp_device *edp) +{ + u32 val; + + rockchip_edp_stop_video(edp); + rockchip_edp_enable_video_mute(edp, 0); + + val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N | + AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N; + writel(val, edp->regs + FUNC_EN_1); + + val = SSC_FUNC_EN_N | AUX_FUNC_EN_N | + SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N; + writel(val, edp->regs + FUNC_EN_2); + + usleep_range(20, 30); + + rockchip_edp_lane_swap(edp, 0); + + writel(0x0, edp->regs + SYS_CTL_1); + writel(0x40, edp->regs + SYS_CTL_2); + writel(0x0, edp->regs + SYS_CTL_3); + writel(0x0, edp->regs + SYS_CTL_4); + + writel(0x0, edp->regs + PKT_SEND_CTL); + writel(0x0, edp->regs + HDCP_CTL); + + writel(0x5e, edp->regs + HPD_DEGLITCH_L); + writel(0x1a, edp->regs + HPD_DEGLITCH_H); + + writel(0x10, edp->regs + LINK_DEBUG_CTL); + + writel(0x0, edp->regs + VIDEO_FIFO_THRD); + writel(0x20, edp->regs + AUDIO_MARGIN); + + writel(0x4, edp->regs + M_VID_GEN_FILTER_TH); + writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH); + + writel(0x0, edp->regs + SOC_GENERAL_CTL); +} + +void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp) +{ + u32 val; + + /* 0: mask, 1: unmask */ + val = 0; + writel(val, edp->regs + COMMON_INT_MASK_1); + + writel(val, edp->regs + COMMON_INT_MASK_2); + + writel(val, edp->regs + COMMON_INT_MASK_3); + + writel(val, edp->regs + COMMON_INT_MASK_4); + + writel(val, edp->regs + DP_INT_STA_MASK); +} + +u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + DEBUG_CTL); + + return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED; +} + +void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp, + bool enable) +{ + u32 val; + + if (enable) { + val = PD_EXP_BG | PD_AUX | PD_PLL | + PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0; + writel(val, edp->regs + DP_PWRDN); + usleep_range(10, 20); + writel(0x0, edp->regs + DP_PWRDN); + } else { + val = PD_EXP_BG | PD_AUX | PD_PLL | + PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0; + writel(val, edp->regs + DP_PWRDN); + } +} + +void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp) +{ + u32 val; + int wt = 0; + + rockchip_edp_analog_power_ctr(edp, 1); + + val = PLL_LOCK_CHG; + writel(val, edp->regs + COMMON_INT_STA_1); + + val = readl(edp->regs + DEBUG_CTL); + val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); + writel(val, edp->regs + DEBUG_CTL); + + /* Power up PLL */ + while (wt < 100) { + if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) { + dev_dbg(edp->dev, "edp pll locked\n"); + break; + } + wt++; + udelay(5); + } + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + val = readl(edp->regs + FUNC_EN_2); + val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N + | AUX_FUNC_EN_N | SSC_FUNC_EN_N); + writel(val, edp->regs + FUNC_EN_2); +} + +void rockchip_edp_init_hpd(struct rockchip_edp_device *edp) +{ + u32 val; + + val = HOTPLUG_CHG | HPD_LOST | PLUG; + writel(val, edp->regs + COMMON_INT_STA_4); + + val = INT_HPD; + writel(val, edp->regs + DP_INT_STA); + + val = readl(edp->regs + SYS_CTL_3); + val |= (F_HPD | HPD_CTRL); + writel(val, edp->regs + SYS_CTL_3); +} + +void rockchip_edp_reset_aux(struct rockchip_edp_device *edp) +{ + u32 val; + + /* Disable AUX channel module */ + val = readl(edp->regs + FUNC_EN_2); + val |= AUX_FUNC_EN_N; + writel(val, edp->regs + FUNC_EN_2); +} + +void rockchip_edp_init_aux(struct rockchip_edp_device *edp) +{ + u32 val; + + /* Clear inerrupts related to AUX channel */ + val = RPLY_RECEIV | AUX_ERR; + writel(val, edp->regs + DP_INT_STA); + + rockchip_edp_reset_aux(edp); + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + val = DEFER_CTRL_EN | DEFER_COUNT(1); + writel(val, edp->regs + AUX_CH_DEFER_CTL); + + /* Enable AUX channel module */ + val = readl(edp->regs + FUNC_EN_2); + val &= ~AUX_FUNC_EN_N; + writel(val, edp->regs + FUNC_EN_2); +} + +int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + SYS_CTL_3); + if (val & HPD_STATUS) + return 0; + + return -EINVAL; +} + +void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + FUNC_EN_1); + val &= ~SW_FUNC_EN_N; + writel(val, edp->regs + FUNC_EN_1); +} + +int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp) +{ + int val; + int retval = 0; + int timeout_loop = 0; + int aux_timeout = 0; + + /* Enable AUX CH operation */ + val = readl(edp->regs + AUX_CH_CTL_2); + val |= AUX_EN; + writel(val, edp->regs + AUX_CH_CTL_2); + + /* Is AUX CH operation enabled? */ + val = readl(edp->regs + AUX_CH_CTL_2); + while (val & AUX_EN) { + aux_timeout++; + if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) { + dev_err(edp->dev, "AUX CH enable timeout!\n"); + return -ETIMEDOUT; + } + val = readl(edp->regs + AUX_CH_CTL_2); + usleep_range(1000, 2000); + } + + /* Is AUX CH command redply received? */ + val = readl(edp->regs + DP_INT_STA); + while (!(val & RPLY_RECEIV)) { + timeout_loop++; + if (DP_TIMEOUT_LOOP_CNT < timeout_loop) { + dev_err(edp->dev, "AUX CH command redply failed!\n"); + return -ETIMEDOUT; + } + val = readl(edp->regs + DP_INT_STA); + usleep_range(10, 20); + } + + /* Clear interrupt source for AUX CH command redply */ + writel(RPLY_RECEIV, edp->regs + DP_INT_STA); + + /* Clear interrupt source for AUX CH access error */ + val = readl(edp->regs + DP_INT_STA); + if (val & AUX_ERR) { + writel(AUX_ERR, edp->regs + DP_INT_STA); + return -EREMOTEIO; + } + + /* Check AUX CH error access status */ + val = readl(edp->regs + AUX_CH_STA); + if ((val & AUX_STATUS_MASK) != 0) { + dev_err(edp->dev, "AUX CH error happens: %d\n\n", + val & AUX_STATUS_MASK); + return -EREMOTEIO; + } + + return retval; +} + +int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp, + unsigned int val_addr, + unsigned char data) +{ + u32 val; + int i; + int retval; + + for (i = 0; i < 3; i++) { + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, edp->regs + BUFFER_DATA_CTL); + + /* Select DPCD device address */ + val = AUX_ADDR_7_0(val_addr); + writel(val, edp->regs + DP_AUX_ADDR_7_0); + val = AUX_ADDR_15_8(val_addr); + writel(val, edp->regs + DP_AUX_ADDR_15_8); + val = AUX_ADDR_19_16(val_addr); + writel(val, edp->regs + DP_AUX_ADDR_19_16); + + /* Write data buffer */ + val = (unsigned int)data; + writel(val, edp->regs + BUF_DATA_0); + + /* + * Set DisplayPort transaction and write 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval == 0) + break; + + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + } + + return retval; +} + +int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp, + unsigned int val_addr, + unsigned char *data) +{ + u32 val; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, edp->regs + BUFFER_DATA_CTL); + + /* Select DPCD device address */ + val = AUX_ADDR_7_0(val_addr); + writel(val, edp->regs + DP_AUX_ADDR_7_0); + val = AUX_ADDR_15_8(val_addr); + writel(val, edp->regs + DP_AUX_ADDR_15_8); + val = AUX_ADDR_19_16(val_addr); + writel(val, edp->regs + DP_AUX_ADDR_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval == 0) + break; + + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + } + + /* Read data buffer */ + val = readl(edp->regs + BUF_DATA_0); + *data = (unsigned char)(val & 0xff); + + return retval; +} + +int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp, + unsigned int val_addr, + unsigned int count, + unsigned char data[]) +{ + u32 val; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + int i; + int retval = 0; + + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, edp->regs + BUFFER_DATA_CTL); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + for (i = 0; i < 10; i++) { + /* Select DPCD device address */ + val = AUX_ADDR_7_0(val_addr + start_offset); + writel(val, edp->regs + DP_AUX_ADDR_7_0); + val = AUX_ADDR_15_8(val_addr + start_offset); + writel(val, edp->regs + DP_AUX_ADDR_15_8); + val = AUX_ADDR_19_16(val_addr + start_offset); + writel(val, edp->regs + DP_AUX_ADDR_19_16); + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + val = data[start_offset + cur_data_idx]; + writel(val, edp->regs + BUF_DATA_0 + + 4 * cur_data_idx); + } + + /* + * Set DisplayPort transaction and write + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval == 0) + break; + + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + } + + start_offset += cur_data_count; + } + + return retval; +} + +int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp, + unsigned int val_addr, + unsigned int count, + unsigned char data[]) +{ + u32 val; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + int i; + int retval = 0; + + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, edp->regs + BUFFER_DATA_CTL); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + /* AUX CH Request Transaction process */ + for (i = 0; i < 10; i++) { + /* Select DPCD device address */ + val = AUX_ADDR_7_0(val_addr + start_offset); + writel(val, edp->regs + DP_AUX_ADDR_7_0); + val = AUX_ADDR_15_8(val_addr + start_offset); + writel(val, edp->regs + DP_AUX_ADDR_15_8); + val = AUX_ADDR_19_16(val_addr + start_offset); + writel(val, edp->regs + DP_AUX_ADDR_19_16); + + /* + * Set DisplayPort transaction and read + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval == 0) + break; + + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + } + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + val = readl(edp->regs + BUF_DATA_0 + + 4 * cur_data_idx); + data[start_offset + cur_data_idx] = + (unsigned char)val; + } + + start_offset += cur_data_count; + } + + return retval; +} + +int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp, + unsigned int device_addr, + unsigned int val_addr) +{ + u32 val; + int retval; + + /* Set EDID device address */ + val = device_addr; + writel(val, edp->regs + DP_AUX_ADDR_7_0); + writel(0x0, edp->regs + DP_AUX_ADDR_15_8); + writel(0x0, edp->regs + DP_AUX_ADDR_19_16); + + /* Set offset from base address of EDID device */ + writel(val_addr, edp->regs + BUF_DATA_0); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval != 0) + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + + return retval; +} + +int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp, + unsigned int device_addr, + unsigned int val_addr, + unsigned int *data) +{ + u32 val; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, edp->regs + BUFFER_DATA_CTL); + + /* Select EDID device */ + retval = rockchip_edp_select_i2c_device(edp, + device_addr, + val_addr); + if (retval != 0) { + dev_err(edp->dev, "Select EDID device fail!\n"); + continue; + } + + /* + * Set I2C transaction and read data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval == 0) + break; + + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + } + + /* Read data */ + if (retval == 0) + *data = readl(edp->regs + BUF_DATA_0); + + return retval; +} + +int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp, + unsigned int device_addr, + unsigned int val_addr, + unsigned int count, + unsigned char edid[]) +{ + u32 val; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int retval = 0; + + for (i = 0; i < count; i += 16) { + for (j = 0; j < 100; j++) { + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, edp->regs + BUFFER_DATA_CTL); + + /* Set normal AUX CH command */ + val = readl(edp->regs + AUX_CH_CTL_2); + val &= ~ADDR_ONLY; + writel(val, edp->regs + AUX_CH_CTL_2); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) + retval = rockchip_edp_select_i2c_device( + edp, device_addr, val_addr + i); + else + defer = 0; + + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(val, edp->regs + AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = rockchip_edp_start_aux_transaction(edp); + if (retval == 0) + break; + + dev_dbg(edp->dev, "Aux Transaction fail!\n"); + + /* Check if Rx sends defer */ + val = readl(edp->regs + AUX_RX_COMM); + if (val == AUX_RX_COMM_AUX_DEFER || + val == AUX_RX_COMM_I2C_DEFER) { + dev_err(edp->dev, "Defer: %d\n\n", val); + defer = 1; + } + } + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx); + edid[i + cur_data_idx] = (unsigned char)val; + } + } + + return retval; +} + +void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp, + u32 bwtype) +{ + u32 val; + + val = bwtype; + if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62)) + writel(val, edp->regs + LINK_BW_SET); +} + +void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp, + u32 *bwtype) +{ + u32 val; + + val = readl(edp->regs + LINK_BW_SET); + *bwtype = val; +} + +void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp) +{ + u32 val; + + val = HW_LT_EN; + writel(val, edp->regs + HW_LT_CTL); +} + +int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + DP_INT_STA); + if (val&HW_LT_DONE) { + writel(val, edp->regs + DP_INT_STA); + return 0; + } + + return 1; +} + +int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + HW_LT_CTL); + + return (val & HW_LT_ERR_CODE_MASK) >> 4; +} + +void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, u32 count) +{ + u32 val; + + val = count; + writel(val, edp->regs + LANE_CNT_SET); +} + +void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, u32 *count) +{ + u32 val; + + val = readl(edp->regs + LANE_CNT_SET); + *count = val; +} + +void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp, + bool enable) +{ + u32 val; + + if (enable) { + val = readl(edp->regs + SYS_CTL_4); + val |= ENHANCED; + writel(val, edp->regs + SYS_CTL_4); + } else { + val = readl(edp->regs + SYS_CTL_4); + val &= ~ENHANCED; + writel(val, edp->regs + SYS_CTL_4); + } +} + +void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp, + enum pattern_set pattern) +{ + u32 val; + + switch (pattern) { + case PRBS7: + val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; + writel(val, edp->regs + TRAINING_PTN_SET); + break; + case D10_2: + val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; + writel(val, edp->regs + TRAINING_PTN_SET); + break; + case TRAINING_PTN1: + val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; + writel(val, edp->regs + TRAINING_PTN_SET); + break; + case TRAINING_PTN2: + val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; + writel(val, edp->regs + TRAINING_PTN_SET); + break; + case DP_NONE: + val = SCRAMBLING_ENABLE | + LINK_QUAL_PATTERN_SET_DISABLE | + SW_TRAINING_PATTERN_SET_DISABLE; + writel(val, edp->regs + TRAINING_PTN_SET); + break; + default: + break; + } +} + +void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp, + u32 level) +{ + u32 val; + + val = level << PRE_EMPHASIS_SET_SHIFT; + writel(val, edp->regs + LN0_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp, + u32 level) +{ + u32 val; + + val = level << PRE_EMPHASIS_SET_SHIFT; + writel(val, edp->regs + LN1_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp, + u32 level) +{ + u32 val; + + val = level << PRE_EMPHASIS_SET_SHIFT; + writel(val, edp->regs + LN2_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp, + u32 level) +{ + u32 val; + + val = level << PRE_EMPHASIS_SET_SHIFT; + writel(val, edp->regs + LN3_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp, + u32 training_lane) +{ + u32 val; + + val = training_lane; + writel(val, edp->regs + LN0_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp, + u32 training_lane) +{ + u32 val; + + val = training_lane; + writel(val, edp->regs + LN1_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp, + u32 training_lane) +{ + u32 val; + + val = training_lane; + writel(val, edp->regs + LN2_LINK_TRAINING_CTL); +} + +void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp, + u32 training_lane) +{ + u32 val; + + val = training_lane; + writel(val, edp->regs + LN3_LINK_TRAINING_CTL); +} + +u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + LN0_LINK_TRAINING_CTL); + return val; +} + +u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + LN1_LINK_TRAINING_CTL); + return val; +} + +u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + LN2_LINK_TRAINING_CTL); + return val; +} + +u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + LN3_LINK_TRAINING_CTL); + return val; +} + +void rockchip_edp_reset_macro(struct rockchip_edp_device *edp) +{ +} + +int rockchip_edp_init_video(struct rockchip_edp_device *edp) +{ + u32 val; + + val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + writel(val, edp->regs + COMMON_INT_STA_1); + + val = 0x0; + writel(val, edp->regs + SYS_CTL_1); + + val = CHA_CRI(4) | CHA_CTRL; + writel(val, edp->regs + SYS_CTL_2); + + val = VID_HRES_TH(2) | VID_VRES_TH(0); + writel(val, edp->regs + VIDEO_CTL_8); + + return 0; +} + +void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp, + u32 color_dedpth, + u32 color_space, + u32 dynamic_range, + u32 coeff) +{ + u32 val; + + /* Configure the input color dedpth, color space, dynamic range */ + val = (dynamic_range << IN_D_RANGE_SHIFT) | + (color_dedpth << IN_BPC_SHIFT) | + (color_space << IN_COLOR_F_SHIFT); + writel(val, edp->regs + VIDEO_CTL_2); + + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ + val = readl(edp->regs + VIDEO_CTL_3); + val &= ~IN_YC_COEFFI_MASK; + if (coeff) + val |= IN_YC_COEFFI_ITU709; + else + val |= IN_YC_COEFFI_ITU601; + writel(val, edp->regs + VIDEO_CTL_3); +} + +int rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + SYS_CTL_1); + writel(val, edp->regs + SYS_CTL_1); + + val = readl(edp->regs + SYS_CTL_1); + + if (!(val & DET_STA)) { + dev_dbg(edp->dev, "Input stream clock not detected.\n"); + return -EINVAL; + } + + val = readl(edp->regs + SYS_CTL_2); + writel(val, edp->regs + SYS_CTL_2); + + val = readl(edp->regs + SYS_CTL_2); + if (val & CHA_STA) { + dev_dbg(edp->dev, "Input stream clk is changing\n"); + return -EINVAL; + } + + return 0; +} + +void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value) +{ + u32 val; + + if (type == REGISTER_M) { + val = readl(edp->regs + SYS_CTL_4); + val |= FIX_M_VID; + writel(val, edp->regs + SYS_CTL_4); + val = m_value & 0xff; + writel(val, edp->regs + M_VID_0); + val = (m_value >> 8) & 0xff; + writel(val, edp->regs + M_VID_1); + val = (m_value >> 16) & 0xff; + writel(val, edp->regs + M_VID_2); + + val = n_value & 0xff; + writel(val, edp->regs + N_VID_0); + val = (n_value >> 8) & 0xff; + writel(val, edp->regs + N_VID_1); + val = (n_value >> 16) & 0xff; + writel(val, edp->regs + N_VID_2); + } else { + val = readl(edp->regs + SYS_CTL_4); + val &= ~FIX_M_VID; + writel(val, edp->regs + SYS_CTL_4); + + writel(0x00, edp->regs + N_VID_0); + writel(0x80, edp->regs + N_VID_1); + writel(0x00, edp->regs + N_VID_2); + } +} + +void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp, + u32 type) +{ + u32 val; + + if (type == VIDEO_TIMING_FROM_CAPTURE) { + val = readl(edp->regs + VIDEO_CTL_10); + val &= ~F_SEL; + writel(val, edp->regs + VIDEO_CTL_10); + } else { + val = readl(edp->regs + VIDEO_CTL_10); + val |= F_SEL; + writel(val, edp->regs + VIDEO_CTL_10); + } +} + +int rockchip_edp_bist_cfg(struct rockchip_edp_device *edp) +{ + struct video_info *video_info = &edp->video_info; + struct drm_display_mode *mode = &edp->mode; + u16 x_total, y_total, x_act; + u32 val; + + x_total = mode->htotal; + y_total = mode->vtotal; + x_act = mode->hdisplay; + + rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); + rockchip_edp_set_video_color_format(edp, video_info->color_depth, + video_info->color_space, + video_info->dynamic_range, + video_info->ycbcr_coeff); + + val = y_total & 0xff; + writel(val, edp->regs + TOTAL_LINE_CFG_L); + val = (y_total >> 8); + writel(val, edp->regs + TOTAL_LINE_CFG_H); + val = (mode->vdisplay & 0xff); + writel(val, edp->regs + ATV_LINE_CFG_L); + val = (mode->vdisplay >> 8); + writel(val, edp->regs + ATV_LINE_CFG_H); + val = (mode->vsync_start - mode->vdisplay); + writel(val, edp->regs + VF_PORCH_REG); + val = (mode->vsync_end - mode->vsync_start); + writel(val, edp->regs + VSYNC_CFG_REG); + val = (mode->vtotal - mode->vsync_end); + writel(val, edp->regs + VB_PORCH_REG); + val = x_total & 0xff; + writel(val, edp->regs + TOTAL_PIXELL_REG); + val = x_total >> 8; + writel(val, edp->regs + TOTAL_PIXELH_REG); + val = (x_act & 0xff); + writel(val, edp->regs + ATV_PIXELL_REG); + val = (x_act >> 8); + writel(val, edp->regs + ATV_PIXELH_REG); + val = (mode->hsync_start - mode->hdisplay) & 0xff; + writel(val, edp->regs + HF_PORCHL_REG); + val = (mode->hsync_start - mode->hdisplay) >> 8; + writel(val, edp->regs + HF_PORCHH_REG); + val = (mode->hsync_end - mode->hsync_start) & 0xff; + writel(val, edp->regs + HSYNC_CFGL_REG); + val = (mode->hsync_end - mode->hsync_start) >> 8; + writel(val, edp->regs + HSYNC_CFGH_REG); + val = (mode->htotal - mode->hsync_end) & 0xff; + writel(val, edp->regs + HB_PORCHL_REG); + val = (mode->htotal - mode->hsync_end) >> 8; + writel(val, edp->regs + HB_PORCHH_REG); + + val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR; + writel(val, edp->regs + VIDEO_CTL_4); + + val = readl(edp->regs + VIDEO_CTL_10); + val &= ~F_SEL; + writel(val, edp->regs + VIDEO_CTL_10); + return 0; +} + +void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp, + bool enable) +{ +} + +void rockchip_edp_start_video(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + VIDEO_CTL_1); + val |= VIDEO_EN; + writel(val, edp->regs + VIDEO_CTL_1); +} + +int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + SYS_CTL_3); + writel(val, edp->regs + SYS_CTL_3); + + val = readl(edp->regs + SYS_CTL_3); + if (!(val & STRM_VALID)) { + dev_dbg(edp->dev, "Input video stream is not detected.\n"); + return -EINVAL; + } + + return 0; +} + +void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp, + struct video_info *video_info) +{ + u32 val; + + val = readl(edp->regs + FUNC_EN_1); + val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N); + writel(val, edp->regs + FUNC_EN_1); + + val = readl(edp->regs + VIDEO_CTL_10); + val &= ~INTERACE_SCAN_CFG; + val |= (video_info->interlaced << 2); + writel(val, edp->regs + VIDEO_CTL_10); + + val = readl(edp->regs + VIDEO_CTL_10); + val &= ~VSYNC_POLARITY_CFG; + val |= (video_info->v_sync_polarity << 1); + writel(val, edp->regs + VIDEO_CTL_10); + + val = readl(edp->regs + VIDEO_CTL_10); + val &= ~HSYNC_POLARITY_CFG; + val |= (video_info->h_sync_polarity << 0); + writel(val, edp->regs + VIDEO_CTL_10); +} + +void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + TRAINING_PTN_SET); + val &= ~SCRAMBLING_DISABLE; + writel(val, edp->regs + TRAINING_PTN_SET); +} + +void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp) +{ + u32 val; + + val = readl(edp->regs + TRAINING_PTN_SET); + val |= SCRAMBLING_DISABLE; + writel(val, edp->regs + TRAINING_PTN_SET); +} + +enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp) +{ + u32 val; + + /* Parse hotplug interrupt status register */ + val = readl(edp->regs + COMMON_INT_STA_4); + if (val & PLUG) + return DP_IRQ_TYPE_HP_CABLE_IN; + + if (val & HPD_LOST) + return DP_IRQ_TYPE_HP_CABLE_OUT; + + if (val & HOTPLUG_CHG) + return DP_IRQ_TYPE_HP_CHANGE; + + return DP_IRQ_TYPE_UNKNOWN; +} + +void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp) +{ + u32 val; + + val = HOTPLUG_CHG | HPD_LOST | PLUG; + writel(val, edp->regs + COMMON_INT_STA_4); + + val = INT_HPD; + writel(val, edp->regs + DP_INT_STA); +} diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.h b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h new file mode 100644 index 0000000..b50dd47 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h @@ -0,0 +1,345 @@ +/* +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd +* Author: +* Andy yan <andy.yan@rock-chips.com> +* Jeff chen <jeff.chen@rock-chips.com> +* +* based on exynos_dp_reg.h +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#ifndef _ROCKCHIP_EDP_REG_H +#define _ROCKCHIP_EDP_REG_H + +#include <linux/bitops.h> + +#define TX_SW_RST 0x14 +#define FUNC_EN_1 0x18 +#define FUNC_EN_2 0x1C +#define VIDEO_CTL_1 0x20 +#define VIDEO_CTL_2 0x24 +#define VIDEO_CTL_3 0x28 +#define VIDEO_CTL_4 0x2c +#define VIDEO_CTL_8 0x3C +#define VIDEO_CTL_10 0x44 +#define TOTAL_LINE_CFG_L 0x48 +#define TOTAL_LINE_CFG_H 0x4c +#define ATV_LINE_CFG_L 0x50 +#define ATV_LINE_CFG_H 0x54 +#define VF_PORCH_REG 0x58 +#define VSYNC_CFG_REG 0x5c +#define VB_PORCH_REG 0x60 +#define TOTAL_PIXELL_REG 0x64 +#define TOTAL_PIXELH_REG 0x68 +#define ATV_PIXELL_REG 0x6c +#define ATV_PIXELH_REG 0x70 +#define HF_PORCHL_REG 0x74 +#define HF_PORCHH_REG 0x78 +#define HSYNC_CFGL_REG 0x7c +#define HSYNC_CFGH_REG 0x80 +#define HB_PORCHL_REG 0x84 +#define HB_PORCHH_REG 0x88 +#define PLL_REG_1 0xfc + +#define SSC_REG 0x104 +#define TX_REG_COMMON 0x114 +#define DP_AUX 0x120 +#define DP_BIAS 0x124 +#define DP_PWRDN 0x12c +#define DP_RESERVE2 0x134 + +#define LANE_MAP 0x35C +#define ANALOG_CTL_2 0x374 +#define AUX_HW_RETRY_CTL 0x390 +#define COMMON_INT_STA_1 0x3C4 +#define COMMON_INT_STA_2 0x3C8 +#define COMMON_INT_STA_3 0x3CC +#define COMMON_INT_STA_4 0x3D0 +#define DP_INT_STA 0x3DC +#define COMMON_INT_MASK_1 0x3E0 +#define COMMON_INT_MASK_2 0x3E4 +#define COMMON_INT_MASK_3 0x3E8 +#define COMMON_INT_MASK_4 0x3EC +#define DP_INT_STA_MASK 0x3F8 + +#define SYS_CTL_1 0x600 +#define SYS_CTL_2 0x604 +#define SYS_CTL_3 0x608 +#define SYS_CTL_4 0x60C +#define PKT_SEND_CTL 0x640 +#define HDCP_CTL 0x648 +#define LINK_BW_SET 0x680 +#define LANE_CNT_SET 0x684 +#define TRAINING_PTN_SET 0x688 +#define LN0_LINK_TRAINING_CTL 0x68C +#define LN1_LINK_TRAINING_CTL 0x690 +#define LN2_LINK_TRAINING_CTL 0x694 +#define LN3_LINK_TRAINING_CTL 0x698 +#define HW_LT_CTL 0x6a0 +#define DEBUG_CTL 0x6C0 +#define HPD_DEGLITCH_L 0x6C4 +#define HPD_DEGLITCH_H 0x6C8 +#define LINK_DEBUG_CTL 0x6E0 +#define M_VID_0 0x700 +#define M_VID_1 0x704 +#define M_VID_2 0x708 +#define N_VID_0 0x70C +#define N_VID_1 0x710 +#define N_VID_2 0x714 +#define VIDEO_FIFO_THRD 0x730 +#define AUDIO_MARGIN 0x73C +#define M_VID_GEN_FILTER_TH 0x764 +#define M_AUD_GEN_FILTER_TH 0x778 +#define AUX_CH_STA 0x780 +#define AUX_CH_DEFER_CTL 0x788 +#define AUX_RX_COMM 0x78C +#define BUFFER_DATA_CTL 0x790 +#define AUX_CH_CTL_1 0x794 +#define DP_AUX_ADDR_7_0 0x798 +#define DP_AUX_ADDR_15_8 0x79C +#define DP_AUX_ADDR_19_16 0x7A0 +#define AUX_CH_CTL_2 0x7A4 +#define BUF_DATA_0 0x7C0 +#define SOC_GENERAL_CTL 0x800 +#define PLL_REG_2 0x9e4 +#define PLL_REG_3 0x9e8 +#define PLL_REG_4 0x9ec +#define PLL_REG_5 0xa00 + +/* ROCKCHIP_EDP_FUNC_EN_1 */ +#define VID_CAP_FUNC_EN_N BIT(6) +#define VID_FIFO_FUNC_EN_N BIT(5) +#define AUD_FIFO_FUNC_EN_N BIT(4) +#define AUD_FUNC_EN_N BIT(3) +#define HDCP_FUNC_EN_N BIT(2) +#define SW_FUNC_EN_N BIT(0) + +/* ROCKCHIP_EDP_FUNC_EN_2 */ +#define SSC_FUNC_EN_N BIT(7) +#define AUX_FUNC_EN_N BIT(2) +#define SERDES_FIFO_FUNC_EN_N BIT(1) +#define LS_CLK_DOMAIN_FUNC_EN_N BIT(0) + +/* ROCKCHIP_EDP_VIDEO_CTL_1 */ +#define VIDEO_EN BIT(7) +#define VIDEO_MUTE BIT(6) + +/* ROCKCHIP_EDP_VIDEO_CTL_1 */ +#define IN_D_RANGE_MASK (0x1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (0x1 << 7) +#define IN_D_RANGE_VESA (0x0 << 7) +#define IN_BPC_MASK (0x7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (0x3 << 4) +#define IN_BPC_10_BITS (0x2 << 4) +#define IN_BPC_8_BITS (0x1 << 4) +#define IN_BPC_6_BITS (0x0 << 4) +#define IN_COLOR_F_MASK (0x3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (0x2 << 0) +#define IN_COLOR_F_YCBCR422 (0x1 << 0) +#define IN_COLOR_F_RGB (0x0 << 0) + +/* ROCKCHIP_EDP_VIDEO_CTL_3 */ +#define IN_YC_COEFFI_MASK (0x1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (0x1 << 7) +#define IN_YC_COEFFI_ITU601 (0x0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) + +/* ROCKCHIP_EDP_VIDEO_CTL_4 */ +#define BIST_EN (0x1 << 3) +#define BIST_WH_64 (0x1 << 2) +#define BIST_WH_32 (0x0 << 2) +#define BIST_TYPE_COLR_BAR (0x0 << 0) +#define BIST_TYPE_GRAY_BAR (0x1 << 0) +#define BIST_TYPE_MOBILE_BAR (0x2 << 0) + +/* ROCKCHIP_EDP_VIDEO_CTL_8 */ +#define VID_HRES_TH(x) (((x) & 0xf) << 4) +#define VID_VRES_TH(x) (((x) & 0xf) << 0) + +/* ROCKCHIP_EDP_VIDEO_CTL_10 */ +#define F_SEL (0x1 << 4) +#define INTERACE_SCAN_CFG (0x1 << 2) +#define VSYNC_POLARITY_CFG (0x1 << 1) +#define HSYNC_POLARITY_CFG (0x1 << 0) + +/* ROCKCHIP_EDP_PLL_REG_1 */ +#define REF_CLK_24M (0x1 << 1) +#define REF_CLK_27M (0x0 << 1) + +/* ROCKCHIP_EDP_DP_PWRDN */ +#define PD_INC_BG BIT(7) +#define PD_EXP_BG BIT(6) +#define PD_AUX BIT(5) +#define PD_PLL BIT(4) +#define PD_CH3 BIT(3) +#define PD_CH2 BIT(2) +#define PD_CH1 BIT(1) +#define PD_CH0 BIT(0) + +/* ROCKCHIP_EDP_LANE_MAP */ +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) + +/* ROCKCHIP_EDP_ANALOG_CTL_2 */ +#define SEL_24M (0x1 << 3) + +/* ROCKCHIP_EDP_COMMON_INT_STA_1 */ +#define VSYNC_DET BIT(7) +#define PLL_LOCK_CHG BIT(6) +#define SPDIF_ERR BIT(5) +#define SPDIF_UNSTBL BIT(4) +#define VID_FORMAT_CHG BIT(3) +#define AUD_CLK_CHG BIT(2) +#define VID_CLK_CHG BIT(1) +#define SW_INT BIT(0) + +/* ROCKCHIP_EDP_COMMON_INT_STA_2 */ +#define ENC_EN_CHG BIT(6) +#define HW_BKSV_RDY BIT(3) +#define HW_SHA_DONE BIT(2) +#define HW_AUTH_STATE_CHG BIT(1) +#define HW_AUTH_DONE BIT(0) + +/* ROCKCHIP_EDP_COMMON_INT_STA_3 */ +#define AFIFO_UNDER BIT(7) +#define AFIFO_OVER BIT(6) +#define R0_CHK_FLAG BIT(5) + +/* ROCKCHIP_EDP_COMMON_INT_STA_4 */ +#define PSR_ACTIVE BIT(7) +#define PSR_INACTIVE BIT(6) +#define SPDIF_BI_PHASE_ERR BIT(5) +#define HOTPLUG_CHG BIT(2) +#define HPD_LOST BIT(1) +#define PLUG BIT(0) + +/* ROCKCHIP_EDP_INT_STA */ +#define INT_HPD BIT(6) +#define HW_LT_DONE BIT(5) +#define SINK_LOST BIT(3) +#define LINK_LOST BIT(2) +#define RPLY_RECEIV BIT(1) +#define AUX_ERR BIT(0) + +/* ROCKCHIP_EDP_INT_CTL */ +#define INT_CTL 0x3FC +#define SOFT_INT_CTRL BIT(2) +#define INT_POL BIT(0) + +/* ROCKCHIP_EDP_SYS_CTL_1 */ +#define DET_STA BIT(2) +#define FORCE_DET BIT(1) +#define DET_CTRL BIT(0) + +/* ROCKCHIP_EDP_SYS_CTL_2 */ +#define CHA_CRI(x) (((x) & 0xf) << 4) +#define CHA_STA BIT(2) +#define FORCE_CHA BIT(1) +#define CHA_CTRL BIT(0) + +/* ROCKCHIP_EDP_SYS_CTL_3 */ +#define HPD_STATUS BIT(6) +#define F_HPD BIT(5) +#define HPD_CTRL BIT(4) +#define HDCP_RDY BIT(3) +#define STRM_VALID BIT(2) +#define F_VALID BIT(1) +#define VALID_CTRL BIT(0) + +/* ROCKCHIP_EDP_SYS_CTL_4 */ +#define FIX_M_AUD BIT(4) +#define ENHANCED BIT(3) +#define FIX_M_VID BIT(2) +#define M_VID_UPDATE_CTRL BIT(0) + +/* ROCKCHIP_EDP_TRAINING_PTN_SET */ +#define SCRAMBLING_DISABLE (0x1 << 5) +#define SCRAMBLING_ENABLE (0x0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (0x7 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) +#define SW_TRAINING_PATTERN_SET_DISABLE (0x0 << 0) + +/* ROCKCHIP_EDP_HW_LT_CTL */ +#define HW_LT_ERR_CODE_MASK 0x70 +#define HW_LT_EN BIT(0) + +/* ROCKCHIP_EDP_LN0_LINK_TRAINING_CTL */ +#define PRE_EMPHASIS_SET_MASK (0x3 << 3) +#define PRE_EMPHASIS_SET_SHIFT (3) + +/* ROCKCHIP_EDP_DEBUG_CTL */ +#define PLL_LOCK BIT(4) +#define F_PLL_LOCK BIT(3) +#define PLL_LOCK_CTRL BIT(2) +#define POLL_EN BIT(1) +#define PN_INV BIT(0) + +/* ROCKCHIP_EDP_AUX_CH_STA */ +#define AUX_BUSY (0x1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* ROCKCHIP_EDP_AUX_CH_DEFER_CTL */ +#define DEFER_CTRL_EN (0x1 << 7) +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) + +/* ROCKCHIP_EDP_AUX_RX_COMM */ +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) + +/* ROCKCHIP_EDP_BUFFER_DATA_CTL */ +#define BUF_CLR (0x1 << 7) +#define BUF_DATA_COUNT(x) (((x) & 0xf) << 0) + +/* ROCKCHIP_EDP_AUX_CH_CTL_1 */ +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) +#define AUX_TX_COMM_MOT (0x1 << 2) +#define AUX_TX_COMM_WRITE (0x0 << 0) +#define AUX_TX_COMM_READ (0x1 << 0) + +/* OCKCHIP_EDP_AUX_ADDR_7_0 */ +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) + +/* ROCKCHIP_EDP_AUX_ADDR_15_8 */ +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) + +/* ROCKCHIP_EDP_AUX_ADDR_19_16 */ +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) + +/* ROCKCHIP_EDP_AUX_CH_CTL_2 */ +#define ADDR_ONLY BIT(1) +#define AUX_EN BIT(0) + +#endif /* _ROCKCHIP_EDP_REG_H */