// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #include #include #include #include #include #include "dp_parser.h" #include "dp_reg.h" #define DP_DEFAULT_AHB_OFFSET 0x0000 #define DP_DEFAULT_AHB_SIZE 0x0200 #define DP_DEFAULT_AUX_OFFSET 0x0200 #define DP_DEFAULT_AUX_SIZE 0x0200 #define DP_DEFAULT_LINK_OFFSET 0x0400 #define DP_DEFAULT_LINK_SIZE 0x0C00 #define DP_DEFAULT_P0_OFFSET 0x1000 #define DP_DEFAULT_P0_SIZE 0x0400 static const struct dp_regulator_cfg sdm845_dp_reg_cfg = { .num = 2, .regs = { {"vdda-1p2", 21800, 4 }, /* 1.2 V */ {"vdda-0p9", 36000, 32 }, /* 0.9 V */ }, }; static void __iomem *dp_ioremap(struct platform_device *pdev, int idx, size_t *len) { struct resource *res; void __iomem *base; base = devm_platform_get_and_ioremap_resource(pdev, idx, &res); if (!IS_ERR(base)) *len = resource_size(res); return base; } static int dp_parser_ctrl_res(struct dp_parser *parser) { struct platform_device *pdev = parser->pdev; struct dp_io *io = &parser->io; struct dss_io_data *dss = &io->dp_controller; dss->ahb.base = dp_ioremap(pdev, 0, &dss->ahb.len); if (IS_ERR(dss->ahb.base)) return PTR_ERR(dss->ahb.base); dss->aux.base = dp_ioremap(pdev, 1, &dss->aux.len); if (IS_ERR(dss->aux.base)) { /* * The initial binding had a single reg, but in order to * support variation in the sub-region sizes this was split. * dp_ioremap() will fail with -EINVAL here if only a single * reg is specified, so fill in the sub-region offsets and * lengths based on this single region. */ if (PTR_ERR(dss->aux.base) == -EINVAL) { if (dss->ahb.len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) { DRM_ERROR("legacy memory region not large enough\n"); return -EINVAL; } dss->ahb.len = DP_DEFAULT_AHB_SIZE; dss->aux.base = dss->ahb.base + DP_DEFAULT_AUX_OFFSET; dss->aux.len = DP_DEFAULT_AUX_SIZE; dss->link.base = dss->ahb.base + DP_DEFAULT_LINK_OFFSET; dss->link.len = DP_DEFAULT_LINK_SIZE; dss->p0.base = dss->ahb.base + DP_DEFAULT_P0_OFFSET; dss->p0.len = DP_DEFAULT_P0_SIZE; } else { DRM_ERROR("unable to remap aux region: %pe\n", dss->aux.base); return PTR_ERR(dss->aux.base); } } else { dss->link.base = dp_ioremap(pdev, 2, &dss->link.len); if (IS_ERR(dss->link.base)) { DRM_ERROR("unable to remap link region: %pe\n", dss->link.base); return PTR_ERR(dss->link.base); } dss->p0.base = dp_ioremap(pdev, 3, &dss->p0.len); if (IS_ERR(dss->p0.base)) { DRM_ERROR("unable to remap p0 region: %pe\n", dss->p0.base); return PTR_ERR(dss->p0.base); } } io->phy = devm_phy_get(&pdev->dev, "dp"); if (IS_ERR(io->phy)) return PTR_ERR(io->phy); return 0; } static int dp_parser_misc(struct dp_parser *parser) { struct device_node *of_node = parser->pdev->dev.of_node; int len = 0; const char *data_lane_property = "data-lanes"; len = of_property_count_elems_of_size(of_node, data_lane_property, sizeof(u32)); if (len < 0) { DRM_WARN("Invalid property %s, default max DP lanes = %d\n", data_lane_property, DP_MAX_NUM_DP_LANES); len = DP_MAX_NUM_DP_LANES; } parser->max_dp_lanes = len; return 0; } static inline bool dp_parser_check_prefix(const char *clk_prefix, const char *clk_name) { return !strncmp(clk_prefix, clk_name, strlen(clk_prefix)); } static int dp_parser_init_clk_data(struct dp_parser *parser) { int num_clk, i, rc; int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0; const char *clk_name; struct device *dev = &parser->pdev->dev; struct dss_module_power *core_power = &parser->mp[DP_CORE_PM]; struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM]; struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM]; num_clk = of_property_count_strings(dev->of_node, "clock-names"); if (num_clk <= 0) { DRM_ERROR("no clocks are defined\n"); return -EINVAL; } for (i = 0; i < num_clk; i++) { rc = of_property_read_string_index(dev->of_node, "clock-names", i, &clk_name); if (rc < 0) return rc; if (dp_parser_check_prefix("core", clk_name)) core_clk_count++; if (dp_parser_check_prefix("ctrl", clk_name)) ctrl_clk_count++; if (dp_parser_check_prefix("stream", clk_name)) stream_clk_count++; } /* Initialize the CORE power module */ if (core_clk_count == 0) { DRM_ERROR("no core clocks are defined\n"); return -EINVAL; } core_power->num_clk = core_clk_count; core_power->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * core_power->num_clk, GFP_KERNEL); if (!core_power->clk_config) return -EINVAL; /* Initialize the CTRL power module */ if (ctrl_clk_count == 0) { DRM_ERROR("no ctrl clocks are defined\n"); return -EINVAL; } ctrl_power->num_clk = ctrl_clk_count; ctrl_power->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * ctrl_power->num_clk, GFP_KERNEL); if (!ctrl_power->clk_config) { ctrl_power->num_clk = 0; return -EINVAL; } /* Initialize the STREAM power module */ if (stream_clk_count == 0) { DRM_ERROR("no stream (pixel) clocks are defined\n"); return -EINVAL; } stream_power->num_clk = stream_clk_count; stream_power->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * stream_power->num_clk, GFP_KERNEL); if (!stream_power->clk_config) { stream_power->num_clk = 0; return -EINVAL; } return 0; } static int dp_parser_clock(struct dp_parser *parser) { int rc = 0, i = 0; int num_clk = 0; int core_clk_index = 0, ctrl_clk_index = 0, stream_clk_index = 0; int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0; const char *clk_name; struct device *dev = &parser->pdev->dev; struct dss_module_power *core_power = &parser->mp[DP_CORE_PM]; struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM]; struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM]; rc = dp_parser_init_clk_data(parser); if (rc) { DRM_ERROR("failed to initialize power data %d\n", rc); return -EINVAL; } core_clk_count = core_power->num_clk; ctrl_clk_count = ctrl_power->num_clk; stream_clk_count = stream_power->num_clk; num_clk = core_clk_count + ctrl_clk_count + stream_clk_count; for (i = 0; i < num_clk; i++) { rc = of_property_read_string_index(dev->of_node, "clock-names", i, &clk_name); if (rc) { DRM_ERROR("error reading clock-names %d\n", rc); return rc; } if (dp_parser_check_prefix("core", clk_name) && core_clk_index < core_clk_count) { struct dss_clk *clk = &core_power->clk_config[core_clk_index]; strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); clk->type = DSS_CLK_AHB; core_clk_index++; } else if (dp_parser_check_prefix("stream", clk_name) && stream_clk_index < stream_clk_count) { struct dss_clk *clk = &stream_power->clk_config[stream_clk_index]; strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); clk->type = DSS_CLK_PCLK; stream_clk_index++; } else if (dp_parser_check_prefix("ctrl", clk_name) && ctrl_clk_index < ctrl_clk_count) { struct dss_clk *clk = &ctrl_power->clk_config[ctrl_clk_index]; strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); ctrl_clk_index++; if (dp_parser_check_prefix("ctrl_link", clk_name) || dp_parser_check_prefix("stream_pixel", clk_name)) clk->type = DSS_CLK_PCLK; else clk->type = DSS_CLK_AHB; } } DRM_DEBUG_DP("clock parsing successful\n"); return 0; } static int dp_parser_find_next_bridge(struct dp_parser *parser) { struct device *dev = &parser->pdev->dev; struct drm_bridge *bridge; bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); if (IS_ERR(bridge)) return PTR_ERR(bridge); parser->next_bridge = bridge; return 0; } static int dp_parser_parse(struct dp_parser *parser, int connector_type) { int rc = 0; if (!parser) { DRM_ERROR("invalid input\n"); return -EINVAL; } rc = dp_parser_ctrl_res(parser); if (rc) return rc; rc = dp_parser_misc(parser); if (rc) return rc; rc = dp_parser_clock(parser); if (rc) return rc; /* * External bridges are mandatory for eDP interfaces: one has to * provide at least an eDP panel (which gets wrapped into panel-bridge). * * For DisplayPort interfaces external bridges are optional, so * silently ignore an error if one is not present (-ENODEV). */ rc = dp_parser_find_next_bridge(parser); if (rc == -ENODEV) { if (connector_type == DRM_MODE_CONNECTOR_eDP) { DRM_ERROR("eDP: next bridge is not present\n"); return rc; } } else if (rc) { if (rc != -EPROBE_DEFER) DRM_ERROR("DP: error parsing next bridge: %d\n", rc); return rc; } /* Map the corresponding regulator information according to * version. Currently, since we only have one supported platform, * mapping the regulator directly. */ parser->regulator_cfg = &sdm845_dp_reg_cfg; return 0; } struct dp_parser *dp_parser_get(struct platform_device *pdev) { struct dp_parser *parser; parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL); if (!parser) return ERR_PTR(-ENOMEM); parser->parse = dp_parser_parse; parser->pdev = pdev; return parser; }