From f70575805708cabdedea7498aaa3f710fde4d920 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 31 Jan 2024 03:29:01 +0000 Subject: [PATCH] add lvds1024*800 --- kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 713 ++++++++++++++++++++++++++++++++++------------------------ 1 files changed, 418 insertions(+), 295 deletions(-) diff --git a/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 26ba45e..9f09525 100644 --- a/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Rockchip SoC DP (Display Port) interface driver. * @@ -5,11 +6,6 @@ * Author: Andy Yan <andy.yan@rock-chips.com> * Yakir Yang <ykk@rock-chips.com> * Jeff Chen <jeff.chen@rock-chips.com> - * - * 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/component.h> @@ -17,86 +13,118 @@ #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/regmap.h> -#include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/clk.h> - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_dp_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <uapi/linux/videodev2.h> #include <video/of_videomode.h> #include <video/videomode.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/bridge/analogix_dp.h> - -#include "../bridge/analogix/analogix_dp_core.h" +#include <drm/drm_dp_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> #include "rockchip_drm_drv.h" -#include "rockchip_drm_psr.h" #include "rockchip_drm_vop.h" - -#define RK3288_GRF_SOC_CON6 0x25c -#define RK3288_EDP_LCDC_SEL BIT(5) -#define RK3399_GRF_SOC_CON20 0x6250 -#define RK3399_EDP_LCDC_SEL BIT(5) - -#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100 #define to_dp(nm) container_of(nm, struct rockchip_dp_device, nm) +#define GRF_REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + .valid = true, \ + } + +struct rockchip_grf_reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; + bool valid; +}; + /** * struct rockchip_dp_chip_data - splite the grf setting of kind of chips - * @lcdsel_grf_reg: grf register offset of lcdc select - * @lcdsel_big: reg value of selecting vop big for eDP - * @lcdsel_lit: reg value of selecting vop little for eDP + * @lcdc_sel: grf register field of lcdc_sel + * @spdif_sel: grf register field of spdif_sel + * @i2s_sel: grf register field of i2s_sel + * @edp_mode: grf register field of edp_mode * @chip_type: specific chip type * @ssc: check if SSC is supported by source * @audio: check if audio is supported by source + * @split_mode: check if split mode is supported */ struct rockchip_dp_chip_data { - u32 lcdsel_grf_reg; - u32 lcdsel_big; - u32 lcdsel_lit; + const struct rockchip_grf_reg_field lcdc_sel; + const struct rockchip_grf_reg_field spdif_sel; + const struct rockchip_grf_reg_field i2s_sel; + const struct rockchip_grf_reg_field edp_mode; u32 chip_type; bool ssc; bool audio; + bool split_mode; }; struct rockchip_dp_device { struct drm_device *drm_dev; struct device *dev; struct drm_encoder encoder; - struct drm_bridge *bridge; struct drm_display_mode mode; - int num_clks; - u8 id; - struct clk_bulk_data *clks; struct regmap *grf; struct reset_control *rst; struct reset_control *apb_reset; - struct regulator *vcc_supply; - struct regulator *vccio_supply; struct platform_device *audio_pdev; const struct rockchip_dp_chip_data *data; + int id; struct analogix_dp_device *adp; struct analogix_dp_plat_data plat_data; struct rockchip_drm_sub_dev sub_dev; + + unsigned int min_refresh_rate; + unsigned int max_refresh_rate; }; + +static int rockchip_grf_write(struct regmap *grf, unsigned int reg, + unsigned int mask, unsigned int val) +{ + return regmap_write(grf, reg, (mask << 16) | (val & mask)); +} + +static int rockchip_grf_field_write(struct regmap *grf, + const struct rockchip_grf_reg_field *field, + unsigned int val) +{ + unsigned int mask; + + if (!field->valid) + return 0; + + mask = GENMASK(field->msb, field->lsb); + val <<= field->lsb; + + return rockchip_grf_write(grf, field->reg, mask, val); +} static int rockchip_dp_audio_hw_params(struct device *dev, void *data, struct hdmi_codec_daifmt *daifmt, struct hdmi_codec_params *params) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + rockchip_grf_field_write(dp->grf, &dp->data->spdif_sel, + daifmt->fmt == HDMI_SPDIF); + rockchip_grf_field_write(dp->grf, &dp->data->i2s_sel, + daifmt->fmt == HDMI_I2S); return analogix_dp_audio_hw_params(dp->adp, daifmt, params); } @@ -106,6 +134,9 @@ struct rockchip_dp_device *dp = dev_get_drvdata(dev); analogix_dp_audio_shutdown(dp->adp); + + rockchip_grf_field_write(dp->grf, &dp->data->spdif_sel, 0); + rockchip_grf_field_write(dp->grf, &dp->data->i2s_sel, 0); } static int rockchip_dp_audio_startup(struct device *dev, void *data) @@ -130,27 +161,24 @@ .get_eld = rockchip_dp_audio_get_eld, }; -static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled) +static int rockchip_dp_match_by_id(struct device *dev, const void *data) { - struct rockchip_dp_device *dp = to_dp(encoder); - int ret; + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + const unsigned int *id = data; - if (!analogix_dp_psr_enabled(dp->adp)) - return 0; + return dp->id == *id; +} - DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit"); +static struct rockchip_dp_device * +rockchip_dp_find_by_id(struct device_driver *drv, unsigned int id) +{ + struct device *dev; - ret = rockchip_drm_wait_vact_end(dp->encoder.crtc, - PSR_WAIT_LINE_FLAG_TIMEOUT_MS); - if (ret) { - DRM_DEV_ERROR(dp->dev, "line flag interrupt did not arrive\n"); - return -ETIMEDOUT; - } + dev = driver_find_device(drv, NULL, &id, rockchip_dp_match_by_id); + if (!dev) + return NULL; - if (enabled) - return analogix_dp_enable_psr(dp->adp); - else - return analogix_dp_disable_psr(dp->adp); + return dev_get_drvdata(dev); } static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) @@ -171,50 +199,20 @@ struct rockchip_dp_device *dp = to_dp(plat_data); int ret; - if (dp->vcc_supply) { - ret = regulator_enable(dp->vcc_supply); - if (ret) - dev_warn(dp->dev, "failed to enable vcc: %d\n", ret); - } - - if (dp->vccio_supply) { - ret = regulator_enable(dp->vccio_supply); - if (ret) - dev_warn(dp->dev, "failed to enable vccio: %d\n", ret); - } - ret = rockchip_dp_pre_init(dp); if (ret < 0) { DRM_DEV_ERROR(dp->dev, "failed to dp pre init %d\n", ret); return ret; } - return ret; -} - -static int rockchip_dp_poweron_end(struct analogix_dp_plat_data *plat_data) -{ - struct rockchip_dp_device *dp = to_dp(plat_data); - - return rockchip_drm_psr_inhibit_put(&dp->encoder); + return rockchip_grf_field_write(dp->grf, &dp->data->edp_mode, 1); } static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data) { struct rockchip_dp_device *dp = to_dp(plat_data); - int ret; - ret = rockchip_drm_psr_inhibit_get(&dp->encoder); - if (ret != 0) - return ret; - - if (dp->vccio_supply) - regulator_disable(dp->vccio_supply); - - if (dp->vcc_supply) - regulator_disable(dp->vcc_supply); - - return 0; + return rockchip_grf_field_write(dp->grf, &dp->data->edp_mode, 0); } static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data, @@ -223,7 +221,6 @@ struct drm_display_info *di = &connector->display_info; /* VOP couldn't output YUV video format for eDP rightly */ u32 mask = DRM_COLOR_FORMAT_YCRCB444 | DRM_COLOR_FORMAT_YCRCB422; - int ret = 0; if ((di->color_formats & mask)) { DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n"); @@ -232,12 +229,52 @@ di->bpc = 8; } - if (list_empty(&connector->probed_modes) && !plat_data->panel) { - ret = rockchip_drm_add_modes_noedid(connector); - DRM_ERROR("analogix dp get edid mode failed, use default mode\n"); + return 0; +} + +static int rockchip_dp_loader_protect(struct drm_encoder *encoder, bool on) +{ + struct rockchip_dp_device *dp = to_dp(encoder); + struct analogix_dp_plat_data *plat_data = &dp->plat_data; + struct rockchip_dp_device *secondary = NULL; + int ret; + + if (plat_data->right) { + secondary = rockchip_dp_find_by_id(dp->dev->driver, !dp->id); + + ret = rockchip_dp_loader_protect(&secondary->encoder, on); + if (ret) + return ret; } - return ret; + if (!on) + return 0; + + if (plat_data->panel) + panel_simple_loader_protect(plat_data->panel); + + ret = analogix_dp_loader_protect(dp->adp); + if (ret) { + if (secondary) + analogix_dp_disable(secondary->adp); + return ret; + } + + return 0; +} + +static bool rockchip_dp_skip_connector(struct drm_bridge *bridge) +{ + if (!bridge) + return false; + + if (of_device_is_compatible(bridge->of_node, "dp-connector")) + return false; + + if (bridge->ops & DRM_BRIDGE_OP_MODES) + return false; + + return true; } static int rockchip_dp_bridge_attach(struct analogix_dp_plat_data *plat_data, @@ -245,17 +282,53 @@ struct drm_connector *connector) { struct rockchip_dp_device *dp = to_dp(plat_data); - int ret; + struct rockchip_drm_sub_dev *sdev = &dp->sub_dev; - if (dp->bridge) { - ret = drm_bridge_attach(&dp->encoder, dp->bridge, bridge); - if (ret) { - DRM_ERROR("Failed to attach bridge to drm: %d\n", ret); - return ret; - } + if (!connector) { + struct list_head *connector_list = + &bridge->dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) + if (drm_connector_has_possible_encoder(connector, + bridge->encoder)) + break; + } + + if (connector) { + sdev->connector = connector; + sdev->of_node = dp->dev->of_node; + sdev->loader_protect = rockchip_dp_loader_protect; + rockchip_drm_register_sub_dev(sdev); } return 0; +} + +static void rockchip_dp_bridge_detach(struct analogix_dp_plat_data *plat_data, + struct drm_bridge *bridge) +{ + struct rockchip_dp_device *dp = to_dp(plat_data); + struct rockchip_drm_sub_dev *sdev = &dp->sub_dev; + + if (sdev->connector) + rockchip_drm_unregister_sub_dev(sdev); +} + +static enum drm_mode_status +rockchip_dp_drm_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + struct rockchip_dp_device *dp = to_dp(encoder); + struct videomode vm; + + drm_display_mode_to_videomode(mode, &vm); + + if (!vm.hfront_porch || !vm.hback_porch || !vm.vfront_porch || !vm.vback_porch) { + DRM_DEV_ERROR(dp->dev, "front porch or back porch can not be 0\n"); + return MODE_BAD; + } + + return MODE_OK; } static bool @@ -274,37 +347,79 @@ /* do nothing */ } -static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder) +static +struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_connector *connector; + struct drm_connector_state *conn_state; + + connector = drm_atomic_get_new_connector_for_encoder(state, encoder); + if (!connector) + return NULL; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return NULL; + + return conn_state->crtc; +} + +static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct rockchip_dp_device *dp = to_dp(encoder); + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; int ret; - u32 val; - if (!dp->data->lcdsel_grf_reg) + crtc = rockchip_dp_drm_get_new_crtc(encoder, state); + if (!crtc) + return; + + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + /* Coming back from self refresh, nothing to do */ + if (old_crtc_state && old_crtc_state->self_refresh_active) return; ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder); if (ret < 0) return; - if (ret) - val = dp->data->lcdsel_lit; - else - val = dp->data->lcdsel_big; - DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); - ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val); + ret = rockchip_grf_field_write(dp->grf, &dp->data->lcdc_sel, ret); if (ret != 0) DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret); } -static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder) +static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { - struct drm_crtc *crtc = encoder->crtc; - struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + struct rockchip_dp_device *dp = to_dp(encoder); + struct drm_crtc *crtc; + struct drm_crtc *old_crtc = encoder->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(old_crtc->state); + int ret; - s->output_if &= ~VOP_OUTPUT_IF_eDP0; + if (dp->plat_data.split_mode) + s->output_if &= ~(VOP_OUTPUT_IF_eDP1 | VOP_OUTPUT_IF_eDP0); + else + s->output_if &= ~(dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0); + crtc = rockchip_dp_drm_get_new_crtc(encoder, state); + /* No crtc means we're doing a full shutdown */ + if (!crtc) + return; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + /* If we're not entering self-refresh, no need to wait for vact */ + if (!new_crtc_state || !new_crtc_state->self_refresh_active) + return; + + ret = rockchip_drm_wait_vact_end(crtc, PSR_WAIT_LINE_FLAG_TIMEOUT_MS); + if (ret) + DRM_DEV_ERROR(dp->dev, "line flag irq timed out\n"); } static int @@ -312,8 +427,15 @@ struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { + struct rockchip_dp_device *dp = to_dp(encoder); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); struct drm_display_info *di = &conn_state->connector->display_info; + int refresh_rate; + + if (di->num_bus_formats) + s->bus_format = di->bus_formats[0]; + else + s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; /* * The hardware IC designed that VOP must output the RGB10 video @@ -325,98 +447,72 @@ s->output_mode = ROCKCHIP_OUT_MODE_AAAA; s->output_type = DRM_MODE_CONNECTOR_eDP; - s->output_if |= VOP_OUTPUT_IF_eDP0; + if (dp->plat_data.split_mode) { + s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; + s->output_flags |= dp->id ? ROCKCHIP_OUTPUT_DATA_SWAP : 0; + s->output_if |= VOP_OUTPUT_IF_eDP0 | VOP_OUTPUT_IF_eDP1; + } else { + s->output_if |= dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0; + } + + if (dp->plat_data.dual_connector_split) { + s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CONNECTOR_SPLIT_MODE; + + if (dp->plat_data.left_display) + s->output_if_left_panel |= dp->id ? + VOP_OUTPUT_IF_eDP1 : + VOP_OUTPUT_IF_eDP0; + } + s->output_bpc = di->bpc; - if (di->num_bus_formats) - s->bus_format = di->bus_formats[0]; - else - s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; s->bus_flags = di->bus_flags; s->tv_state = &conn_state->tv; - s->eotf = TRADITIONAL_GAMMA_SDR; + s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; s->color_space = V4L2_COLORSPACE_DEFAULT; + /** + * It's priority to user rate range define in dtsi. + */ + if (dp->max_refresh_rate && dp->min_refresh_rate) { + s->max_refresh_rate = dp->max_refresh_rate; + s->min_refresh_rate = dp->min_refresh_rate; + } else { + s->max_refresh_rate = di->monitor_range.max_vfreq; + s->min_refresh_rate = di->monitor_range.min_vfreq; + } - return 0; -} - -static int rockchip_dp_drm_encoder_loader_protect(struct drm_encoder *encoder, - bool on) -{ - struct rockchip_dp_device *dp = to_dp(encoder); - int ret; - - if (on) { - if (dp->vcc_supply) { - ret = regulator_enable(dp->vcc_supply); - if (ret) - dev_warn(dp->dev, - "failed to enable vcc: %d\n", ret); - } - - if (dp->vccio_supply) { - ret = regulator_enable(dp->vccio_supply); - if (ret) - dev_warn(dp->dev, - "failed to enable vccio: %d\n", ret); - } - - rockchip_drm_psr_inhibit_put(&dp->encoder); + /** + * Timing exposed in DisplayID or legacy EDID is usually optimized + * for bandwidth by using minimum horizontal and vertical blank. If + * timing beyond the Adaptive-Sync range, it should not enable the + * Ignore MSA option in this timing. If the refresh rate of the + * timing is with the Adaptive-Sync range, this timing should support + * the Adaptive-Sync from the timing's refresh rate to minimum + * support range. + */ + refresh_rate = drm_mode_vrefresh(&crtc_state->adjusted_mode); + if (refresh_rate > s->max_refresh_rate || refresh_rate < s->min_refresh_rate) { + s->max_refresh_rate = 0; + s->min_refresh_rate = 0; + } else if (refresh_rate < s->max_refresh_rate) { + s->max_refresh_rate = refresh_rate; } return 0; } - -static int rockchip_dp_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, - u64 *val, - struct analogix_dp_plat_data *data) -{ - struct drm_encoder *encoder = data->encoder; - struct rockchip_dp_device *dp = to_dp(encoder); - struct rockchip_drm_private *private = connector->dev->dev_private; - - if (property == private->connector_id_prop) { - *val = dp->id; - return 0; - } - - DRM_ERROR("failed to get rockchip analogic dp property\n"); - return -EINVAL; -} - -static int rockchip_dp_attach_properties(struct drm_connector *connector) -{ - struct rockchip_drm_private *private = connector->dev->dev_private; - - drm_object_attach_property(&connector->base, private->connector_id_prop, 0); - - return 0; -} - -static const struct analogix_dp_property_ops rockchip_dp_encoder_property_ops = { - .get_property = rockchip_dp_get_property, - .attach_properties = rockchip_dp_attach_properties, -}; static struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = { + .mode_valid = rockchip_dp_drm_encoder_mode_valid, .mode_fixup = rockchip_dp_drm_encoder_mode_fixup, .mode_set = rockchip_dp_drm_encoder_mode_set, - .enable = rockchip_dp_drm_encoder_enable, - .disable = rockchip_dp_drm_encoder_disable, + .atomic_enable = rockchip_dp_drm_encoder_enable, + .atomic_disable = rockchip_dp_drm_encoder_disable, .atomic_check = rockchip_dp_drm_encoder_atomic_check, - .loader_protect = rockchip_dp_drm_encoder_loader_protect, -}; - -static struct drm_encoder_funcs rockchip_dp_encoder_funcs = { - .destroy = drm_encoder_cleanup, }; static int rockchip_dp_of_probe(struct rockchip_dp_device *dp) { struct device *dev = dp->dev; struct device_node *np = dev->of_node; - int ret = 0; if (of_property_read_bool(np, "rockchip,grf")) { dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); @@ -425,14 +521,6 @@ return PTR_ERR(dp->grf); } } - - ret = devm_clk_bulk_get_all(dev, &dp->clks); - if (ret < 0) { - DRM_DEV_ERROR(dev, "failed to get clocks %d\n", ret); - return ret; - } - - dp->num_clks = ret; dp->rst = devm_reset_control_get(dev, "dp"); if (IS_ERR(dp->rst)) { @@ -446,29 +534,6 @@ return PTR_ERR(dp->apb_reset); } - dp->vcc_supply = devm_regulator_get_optional(dev, "vcc"); - if (IS_ERR(dp->vcc_supply)) { - if (PTR_ERR(dp->vcc_supply) != -ENODEV) { - ret = PTR_ERR(dp->vcc_supply); - dev_err(dev, "failed to get vcc regulator: %d\n", ret); - return ret; - } - - dp->vcc_supply = NULL; - } - - dp->vccio_supply = devm_regulator_get_optional(dev, "vccio"); - if (IS_ERR(dp->vccio_supply)) { - if (PTR_ERR(dp->vccio_supply) != -ENODEV) { - ret = PTR_ERR(dp->vccio_supply); - dev_err(dev, "failed to get vccio regulator: %d\n", - ret); - return ret; - } - - dp->vccio_supply = NULL; - } - return 0; } @@ -479,12 +544,12 @@ struct device *dev = dp->dev; int ret; - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, - dev->of_node); + encoder->possible_crtcs = rockchip_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_dp_encoder_funcs, - DRM_MODE_ENCODER_TMDS, NULL); + ret = drm_simple_encoder_init(drm_dev, encoder, + DRM_MODE_ENCODER_TMDS); if (ret) { DRM_ERROR("failed to initialize encoder with drm\n"); return ret; @@ -499,36 +564,20 @@ void *data) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); - const struct rockchip_dp_chip_data *dp_data; struct drm_device *drm_dev = data; int ret; - dp_data = of_device_get_match_data(dev); - if (!dp_data) - return -ENODEV; - - dp->data = dp_data; dp->drm_dev = drm_dev; - ret = rockchip_dp_drm_create_encoder(dp); - if (ret) { - DRM_ERROR("failed to create drm encoder\n"); - return ret; + if (!dp->plat_data.left) { + ret = rockchip_dp_drm_create_encoder(dp); + if (ret) { + DRM_ERROR("failed to create drm encoder\n"); + return ret; + } + + dp->plat_data.encoder = &dp->encoder; } - - dp->plat_data.encoder = &dp->encoder; - dp->plat_data.ssc = dp->data->ssc; - dp->plat_data.dev_type = dp->data->chip_type; - dp->plat_data.power_on_start = rockchip_dp_poweron_start; - dp->plat_data.power_on_end = rockchip_dp_poweron_end; - dp->plat_data.power_off = rockchip_dp_powerdown; - dp->plat_data.get_modes = rockchip_dp_get_modes; - dp->plat_data.attach = rockchip_dp_bridge_attach; - dp->plat_data.property_ops = &rockchip_dp_encoder_property_ops; - - ret = rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set); - if (ret < 0) - goto err_cleanup_encoder; if (dp->data->audio) { struct hdmi_codec_pdata codec_data = { @@ -545,26 +594,19 @@ sizeof(codec_data)); if (IS_ERR(dp->audio_pdev)) { ret = PTR_ERR(dp->audio_pdev); - goto err_unreg_psr; + goto err_cleanup_encoder; } } - dp->adp = analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data); - if (IS_ERR(dp->adp)) { - ret = PTR_ERR(dp->adp); - goto err_unreg_audio; - } - - dp->sub_dev.connector = &dp->adp->connector; - dp->sub_dev.of_node = dev->of_node; - rockchip_drm_register_sub_dev(&dp->sub_dev); + ret = analogix_dp_bind(dp->adp, drm_dev); + if (ret) + goto err_unregister_audio_pdev; return 0; -err_unreg_audio: + +err_unregister_audio_pdev: if (dp->audio_pdev) platform_device_unregister(dp->audio_pdev); -err_unreg_psr: - rockchip_drm_psr_unregister(&dp->encoder); err_cleanup_encoder: dp->encoder.funcs->destroy(&dp->encoder); return ret; @@ -575,14 +617,10 @@ { struct rockchip_dp_device *dp = dev_get_drvdata(dev); - rockchip_drm_unregister_sub_dev(&dp->sub_dev); if (dp->audio_pdev) platform_device_unregister(dp->audio_pdev); analogix_dp_unbind(dp->adp); - rockchip_drm_psr_unregister(&dp->encoder); dp->encoder.funcs->destroy(&dp->encoder); - - dp->adp = ERR_PTR(-ENODEV); } static const struct component_ops rockchip_dp_component_ops = { @@ -593,10 +631,15 @@ static int rockchip_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct rockchip_dp_chip_data *dp_data; struct drm_panel *panel = NULL; struct drm_bridge *bridge = NULL; struct rockchip_dp_device *dp; - int ret, id; + int id, i, ret; + + dp_data = of_device_get_match_data(dev); + if (!dp_data) + return -ENODEV; ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge); if (ret < 0 && ret != -ENODEV) @@ -609,12 +652,32 @@ id = of_alias_get_id(dev->of_node, "edp"); if (id < 0) id = 0; - dp->id = id; + + i = 0; + while (is_rockchip(dp_data[i].chip_type)) + i++; + + if (id >= i) { + dev_err(dev, "invalid id: %d\n", id); + return -ENODEV; + } + dp->dev = dev; + dp->id = id; dp->adp = ERR_PTR(-ENODEV); + dp->data = &dp_data[id]; + dp->plat_data.ssc = dp->data->ssc; dp->plat_data.panel = panel; - dp->plat_data.skip_connector = !!bridge; - dp->bridge = bridge; + dp->plat_data.dev_type = dp->data->chip_type; + dp->plat_data.power_on_start = rockchip_dp_poweron_start; + dp->plat_data.power_off = rockchip_dp_powerdown; + dp->plat_data.get_modes = rockchip_dp_get_modes; + dp->plat_data.attach = rockchip_dp_bridge_attach; + dp->plat_data.detach = rockchip_dp_bridge_detach; + dp->plat_data.convert_to_split_mode = drm_mode_convert_to_split_mode; + dp->plat_data.convert_to_origin_mode = drm_mode_convert_to_origin_mode; + dp->plat_data.skip_connector = rockchip_dp_skip_connector(bridge); + dp->plat_data.bridge = bridge; ret = rockchip_dp_of_probe(dp); if (ret < 0) @@ -622,18 +685,56 @@ platform_set_drvdata(pdev, dp); - return component_add(dev, &rockchip_dp_component_ops); + dp->adp = analogix_dp_probe(dev, &dp->plat_data); + if (IS_ERR(dp->adp)) + return PTR_ERR(dp->adp); + + if (dp->data->split_mode && device_property_read_bool(dev, "split-mode")) { + struct rockchip_dp_device *secondary = + rockchip_dp_find_by_id(dev->driver, !dp->id); + if (!secondary) { + ret = -EPROBE_DEFER; + goto err_dp_remove; + } + + dp->plat_data.right = secondary->adp; + dp->plat_data.split_mode = true; + secondary->plat_data.panel = dp->plat_data.panel; + secondary->plat_data.left = dp->adp; + secondary->plat_data.split_mode = true; + } + + device_property_read_u32(dev, "min-refresh-rate", &dp->min_refresh_rate); + device_property_read_u32(dev, "max-refresh-rate", &dp->max_refresh_rate); + + if (dp->data->split_mode && device_property_read_bool(dev, "dual-connector-split")) { + dp->plat_data.dual_connector_split = true; + if (device_property_read_bool(dev, "left-display")) + dp->plat_data.left_display = true; + } + + ret = component_add(dev, &rockchip_dp_component_ops); + if (ret) + goto err_dp_remove; + + return 0; + +err_dp_remove: + analogix_dp_remove(dp->adp); + return ret; } static int rockchip_dp_remove(struct platform_device *pdev) { + struct rockchip_dp_device *dp = platform_get_drvdata(pdev); + component_del(&pdev->dev, &rockchip_dp_component_ops); + analogix_dp_remove(dp->adp); return 0; } -#ifdef CONFIG_PM_SLEEP -static int rockchip_dp_suspend(struct device *dev) +static __maybe_unused int rockchip_dp_suspend(struct device *dev) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); @@ -643,7 +744,7 @@ return analogix_dp_suspend(dp->adp); } -static int rockchip_dp_resume(struct device *dev) +static __maybe_unused int rockchip_dp_resume(struct device *dev) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); @@ -653,64 +754,86 @@ return analogix_dp_resume(dp->adp); } -static int rockchip_dp_runtime_suspend(struct device *dev) +static __maybe_unused int rockchip_dp_runtime_suspend(struct device *dev) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); - clk_bulk_disable_unprepare(dp->num_clks, dp->clks); + if (IS_ERR(dp->adp)) + return 0; - return 0; + return analogix_dp_runtime_suspend(dp->adp); } -static int rockchip_dp_runtime_resume(struct device *dev) +static __maybe_unused int rockchip_dp_runtime_resume(struct device *dev) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); - return clk_bulk_prepare_enable(dp->num_clks, dp->clks); + if (IS_ERR(dp->adp)) + return 0; + + return analogix_dp_runtime_resume(dp->adp); } -#endif static const struct dev_pm_ops rockchip_dp_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend_late = rockchip_dp_suspend, - .resume_early = rockchip_dp_resume, - .runtime_suspend = rockchip_dp_runtime_suspend, - .runtime_resume = rockchip_dp_runtime_resume, -#endif + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_dp_suspend, rockchip_dp_resume) + SET_RUNTIME_PM_OPS(rockchip_dp_runtime_suspend, + rockchip_dp_runtime_resume, NULL) }; -static const struct rockchip_dp_chip_data rk3399_edp = { - .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, - .lcdsel_big = HIWORD_UPDATE(0, RK3399_EDP_LCDC_SEL), - .lcdsel_lit = HIWORD_UPDATE(RK3399_EDP_LCDC_SEL, RK3399_EDP_LCDC_SEL), - .chip_type = RK3399_EDP, - .ssc = true, +static const struct rockchip_dp_chip_data rk3399_edp[] = { + { + .chip_type = RK3399_EDP, + .lcdc_sel = GRF_REG_FIELD(0x6250, 5, 5), + .ssc = true, + }, + { /* sentinel */ } }; -static const struct rockchip_dp_chip_data rk3368_edp = { - .chip_type = RK3368_EDP, - .ssc = true, +static const struct rockchip_dp_chip_data rk3288_dp[] = { + { + .chip_type = RK3288_DP, + .lcdc_sel = GRF_REG_FIELD(0x025c, 5, 5), + .ssc = true, + }, + { /* sentinel */ } }; -static const struct rockchip_dp_chip_data rk3288_dp = { - .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, - .lcdsel_big = HIWORD_UPDATE(0, RK3288_EDP_LCDC_SEL), - .lcdsel_lit = HIWORD_UPDATE(RK3288_EDP_LCDC_SEL, RK3288_EDP_LCDC_SEL), - .chip_type = RK3288_DP, - .ssc = true, +static const struct rockchip_dp_chip_data rk3568_edp[] = { + { + .chip_type = RK3568_EDP, + .ssc = true, + .audio = true, + }, + { /* sentinel */ } }; -static const struct rockchip_dp_chip_data rk3568_edp = { - .chip_type = RK3568_EDP, - .ssc = true, - .audio = true, +static const struct rockchip_dp_chip_data rk3588_edp[] = { + { + .chip_type = RK3588_EDP, + .spdif_sel = GRF_REG_FIELD(0x0000, 4, 4), + .i2s_sel = GRF_REG_FIELD(0x0000, 3, 3), + .edp_mode = GRF_REG_FIELD(0x0000, 0, 0), + .ssc = true, + .audio = true, + .split_mode = true, + }, + { + .chip_type = RK3588_EDP, + .spdif_sel = GRF_REG_FIELD(0x0004, 4, 4), + .i2s_sel = GRF_REG_FIELD(0x0004, 3, 3), + .edp_mode = GRF_REG_FIELD(0x0004, 0, 0), + .ssc = true, + .audio = true, + .split_mode = true, + }, + { /* sentinel */ } }; static const struct of_device_id rockchip_dp_dt_ids[] = { {.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp }, - {.compatible = "rockchip,rk3368-edp", .data = &rk3368_edp }, {.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp }, {.compatible = "rockchip,rk3568-edp", .data = &rk3568_edp }, + {.compatible = "rockchip,rk3588-edp", .data = &rk3588_edp }, {} }; MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids); -- Gitblit v1.6.2