162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/of_gpio.h>
762306a36Sopenharmony_ci#include <linux/phy/phy.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <drm/drm_of.h>
1062306a36Sopenharmony_ci#include <drm/drm_print.h>
1162306a36Sopenharmony_ci#include <drm/drm_bridge.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "dp_parser.h"
1462306a36Sopenharmony_ci#include "dp_reg.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define DP_DEFAULT_AHB_OFFSET	0x0000
1762306a36Sopenharmony_ci#define DP_DEFAULT_AHB_SIZE	0x0200
1862306a36Sopenharmony_ci#define DP_DEFAULT_AUX_OFFSET	0x0200
1962306a36Sopenharmony_ci#define DP_DEFAULT_AUX_SIZE	0x0200
2062306a36Sopenharmony_ci#define DP_DEFAULT_LINK_OFFSET	0x0400
2162306a36Sopenharmony_ci#define DP_DEFAULT_LINK_SIZE	0x0C00
2262306a36Sopenharmony_ci#define DP_DEFAULT_P0_OFFSET	0x1000
2362306a36Sopenharmony_ci#define DP_DEFAULT_P0_SIZE	0x0400
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void __iomem *dp_ioremap(struct platform_device *pdev, int idx, size_t *len)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct resource *res;
2862306a36Sopenharmony_ci	void __iomem *base;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	base = devm_platform_get_and_ioremap_resource(pdev, idx, &res);
3162306a36Sopenharmony_ci	if (!IS_ERR(base))
3262306a36Sopenharmony_ci		*len = resource_size(res);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return base;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int dp_parser_ctrl_res(struct dp_parser *parser)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct platform_device *pdev = parser->pdev;
4062306a36Sopenharmony_ci	struct dp_io *io = &parser->io;
4162306a36Sopenharmony_ci	struct dss_io_data *dss = &io->dp_controller;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	dss->ahb.base = dp_ioremap(pdev, 0, &dss->ahb.len);
4462306a36Sopenharmony_ci	if (IS_ERR(dss->ahb.base))
4562306a36Sopenharmony_ci		return PTR_ERR(dss->ahb.base);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	dss->aux.base = dp_ioremap(pdev, 1, &dss->aux.len);
4862306a36Sopenharmony_ci	if (IS_ERR(dss->aux.base)) {
4962306a36Sopenharmony_ci		/*
5062306a36Sopenharmony_ci		 * The initial binding had a single reg, but in order to
5162306a36Sopenharmony_ci		 * support variation in the sub-region sizes this was split.
5262306a36Sopenharmony_ci		 * dp_ioremap() will fail with -EINVAL here if only a single
5362306a36Sopenharmony_ci		 * reg is specified, so fill in the sub-region offsets and
5462306a36Sopenharmony_ci		 * lengths based on this single region.
5562306a36Sopenharmony_ci		 */
5662306a36Sopenharmony_ci		if (PTR_ERR(dss->aux.base) == -EINVAL) {
5762306a36Sopenharmony_ci			if (dss->ahb.len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) {
5862306a36Sopenharmony_ci				DRM_ERROR("legacy memory region not large enough\n");
5962306a36Sopenharmony_ci				return -EINVAL;
6062306a36Sopenharmony_ci			}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci			dss->ahb.len = DP_DEFAULT_AHB_SIZE;
6362306a36Sopenharmony_ci			dss->aux.base = dss->ahb.base + DP_DEFAULT_AUX_OFFSET;
6462306a36Sopenharmony_ci			dss->aux.len = DP_DEFAULT_AUX_SIZE;
6562306a36Sopenharmony_ci			dss->link.base = dss->ahb.base + DP_DEFAULT_LINK_OFFSET;
6662306a36Sopenharmony_ci			dss->link.len = DP_DEFAULT_LINK_SIZE;
6762306a36Sopenharmony_ci			dss->p0.base = dss->ahb.base + DP_DEFAULT_P0_OFFSET;
6862306a36Sopenharmony_ci			dss->p0.len = DP_DEFAULT_P0_SIZE;
6962306a36Sopenharmony_ci		} else {
7062306a36Sopenharmony_ci			DRM_ERROR("unable to remap aux region: %pe\n", dss->aux.base);
7162306a36Sopenharmony_ci			return PTR_ERR(dss->aux.base);
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		dss->link.base = dp_ioremap(pdev, 2, &dss->link.len);
7562306a36Sopenharmony_ci		if (IS_ERR(dss->link.base)) {
7662306a36Sopenharmony_ci			DRM_ERROR("unable to remap link region: %pe\n", dss->link.base);
7762306a36Sopenharmony_ci			return PTR_ERR(dss->link.base);
7862306a36Sopenharmony_ci		}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		dss->p0.base = dp_ioremap(pdev, 3, &dss->p0.len);
8162306a36Sopenharmony_ci		if (IS_ERR(dss->p0.base)) {
8262306a36Sopenharmony_ci			DRM_ERROR("unable to remap p0 region: %pe\n", dss->p0.base);
8362306a36Sopenharmony_ci			return PTR_ERR(dss->p0.base);
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	io->phy = devm_phy_get(&pdev->dev, "dp");
8862306a36Sopenharmony_ci	if (IS_ERR(io->phy))
8962306a36Sopenharmony_ci		return PTR_ERR(io->phy);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic u32 dp_parser_link_frequencies(struct device_node *of_node)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct device_node *endpoint;
9762306a36Sopenharmony_ci	u64 frequency = 0;
9862306a36Sopenharmony_ci	int cnt;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	endpoint = of_graph_get_endpoint_by_regs(of_node, 1, 0); /* port@1 */
10162306a36Sopenharmony_ci	if (!endpoint)
10262306a36Sopenharmony_ci		return 0;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	cnt = of_property_count_u64_elems(endpoint, "link-frequencies");
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (cnt > 0)
10762306a36Sopenharmony_ci		of_property_read_u64_index(endpoint, "link-frequencies",
10862306a36Sopenharmony_ci						cnt - 1, &frequency);
10962306a36Sopenharmony_ci	of_node_put(endpoint);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	do_div(frequency,
11262306a36Sopenharmony_ci		10 * /* from symbol rate to link rate */
11362306a36Sopenharmony_ci		1000); /* kbytes */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return frequency;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int dp_parser_misc(struct dp_parser *parser)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct device_node *of_node = parser->pdev->dev.of_node;
12162306a36Sopenharmony_ci	int cnt;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/*
12462306a36Sopenharmony_ci	 * data-lanes is the property of dp_out endpoint
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	cnt = drm_of_get_data_lanes_count_ep(of_node, 1, 0, 1, DP_MAX_NUM_DP_LANES);
12762306a36Sopenharmony_ci	if (cnt < 0) {
12862306a36Sopenharmony_ci		/* legacy code, data-lanes is the property of mdss_dp node */
12962306a36Sopenharmony_ci		cnt = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (cnt > 0)
13362306a36Sopenharmony_ci		parser->max_dp_lanes = cnt;
13462306a36Sopenharmony_ci	else
13562306a36Sopenharmony_ci		parser->max_dp_lanes = DP_MAX_NUM_DP_LANES; /* 4 lanes */
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	parser->max_dp_link_rate = dp_parser_link_frequencies(of_node);
13862306a36Sopenharmony_ci	if (!parser->max_dp_link_rate)
13962306a36Sopenharmony_ci		parser->max_dp_link_rate = DP_LINK_RATE_HBR2;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic inline bool dp_parser_check_prefix(const char *clk_prefix,
14562306a36Sopenharmony_ci						const char *clk_name)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return !strncmp(clk_prefix, clk_name, strlen(clk_prefix));
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int dp_parser_init_clk_data(struct dp_parser *parser)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int num_clk, i, rc;
15362306a36Sopenharmony_ci	int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
15462306a36Sopenharmony_ci	const char *clk_name;
15562306a36Sopenharmony_ci	struct device *dev = &parser->pdev->dev;
15662306a36Sopenharmony_ci	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
15762306a36Sopenharmony_ci	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
15862306a36Sopenharmony_ci	struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM];
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	num_clk = of_property_count_strings(dev->of_node, "clock-names");
16162306a36Sopenharmony_ci	if (num_clk <= 0) {
16262306a36Sopenharmony_ci		DRM_ERROR("no clocks are defined\n");
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	for (i = 0; i < num_clk; i++) {
16762306a36Sopenharmony_ci		rc = of_property_read_string_index(dev->of_node,
16862306a36Sopenharmony_ci				"clock-names", i, &clk_name);
16962306a36Sopenharmony_ci		if (rc < 0)
17062306a36Sopenharmony_ci			return rc;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		if (dp_parser_check_prefix("core", clk_name))
17362306a36Sopenharmony_ci			core_clk_count++;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		if (dp_parser_check_prefix("ctrl", clk_name))
17662306a36Sopenharmony_ci			ctrl_clk_count++;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		if (dp_parser_check_prefix("stream", clk_name))
17962306a36Sopenharmony_ci			stream_clk_count++;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Initialize the CORE power module */
18362306a36Sopenharmony_ci	if (core_clk_count == 0) {
18462306a36Sopenharmony_ci		DRM_ERROR("no core clocks are defined\n");
18562306a36Sopenharmony_ci		return -EINVAL;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	core_power->num_clk = core_clk_count;
18962306a36Sopenharmony_ci	core_power->clocks = devm_kcalloc(dev,
19062306a36Sopenharmony_ci			core_power->num_clk, sizeof(struct clk_bulk_data),
19162306a36Sopenharmony_ci			GFP_KERNEL);
19262306a36Sopenharmony_ci	if (!core_power->clocks)
19362306a36Sopenharmony_ci		return -ENOMEM;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Initialize the CTRL power module */
19662306a36Sopenharmony_ci	if (ctrl_clk_count == 0) {
19762306a36Sopenharmony_ci		DRM_ERROR("no ctrl clocks are defined\n");
19862306a36Sopenharmony_ci		return -EINVAL;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ctrl_power->num_clk = ctrl_clk_count;
20262306a36Sopenharmony_ci	ctrl_power->clocks = devm_kcalloc(dev,
20362306a36Sopenharmony_ci			ctrl_power->num_clk, sizeof(struct clk_bulk_data),
20462306a36Sopenharmony_ci			GFP_KERNEL);
20562306a36Sopenharmony_ci	if (!ctrl_power->clocks) {
20662306a36Sopenharmony_ci		ctrl_power->num_clk = 0;
20762306a36Sopenharmony_ci		return -ENOMEM;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Initialize the STREAM power module */
21162306a36Sopenharmony_ci	if (stream_clk_count == 0) {
21262306a36Sopenharmony_ci		DRM_ERROR("no stream (pixel) clocks are defined\n");
21362306a36Sopenharmony_ci		return -EINVAL;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	stream_power->num_clk = stream_clk_count;
21762306a36Sopenharmony_ci	stream_power->clocks = devm_kcalloc(dev,
21862306a36Sopenharmony_ci			stream_power->num_clk, sizeof(struct clk_bulk_data),
21962306a36Sopenharmony_ci			GFP_KERNEL);
22062306a36Sopenharmony_ci	if (!stream_power->clocks) {
22162306a36Sopenharmony_ci		stream_power->num_clk = 0;
22262306a36Sopenharmony_ci		return -ENOMEM;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int dp_parser_clock(struct dp_parser *parser)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	int rc = 0, i = 0;
23162306a36Sopenharmony_ci	int num_clk = 0;
23262306a36Sopenharmony_ci	int core_clk_index = 0, ctrl_clk_index = 0, stream_clk_index = 0;
23362306a36Sopenharmony_ci	int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
23462306a36Sopenharmony_ci	const char *clk_name;
23562306a36Sopenharmony_ci	struct device *dev = &parser->pdev->dev;
23662306a36Sopenharmony_ci	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
23762306a36Sopenharmony_ci	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
23862306a36Sopenharmony_ci	struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM];
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	rc =  dp_parser_init_clk_data(parser);
24162306a36Sopenharmony_ci	if (rc) {
24262306a36Sopenharmony_ci		DRM_ERROR("failed to initialize power data %d\n", rc);
24362306a36Sopenharmony_ci		return -EINVAL;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	core_clk_count = core_power->num_clk;
24762306a36Sopenharmony_ci	ctrl_clk_count = ctrl_power->num_clk;
24862306a36Sopenharmony_ci	stream_clk_count = stream_power->num_clk;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	num_clk = core_clk_count + ctrl_clk_count + stream_clk_count;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for (i = 0; i < num_clk; i++) {
25362306a36Sopenharmony_ci		rc = of_property_read_string_index(dev->of_node, "clock-names",
25462306a36Sopenharmony_ci				i, &clk_name);
25562306a36Sopenharmony_ci		if (rc) {
25662306a36Sopenharmony_ci			DRM_ERROR("error reading clock-names %d\n", rc);
25762306a36Sopenharmony_ci			return rc;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci		if (dp_parser_check_prefix("core", clk_name) &&
26062306a36Sopenharmony_ci				core_clk_index < core_clk_count) {
26162306a36Sopenharmony_ci			core_power->clocks[core_clk_index].id = devm_kstrdup(dev, clk_name, GFP_KERNEL);
26262306a36Sopenharmony_ci			core_clk_index++;
26362306a36Sopenharmony_ci		} else if (dp_parser_check_prefix("stream", clk_name) &&
26462306a36Sopenharmony_ci				stream_clk_index < stream_clk_count) {
26562306a36Sopenharmony_ci			stream_power->clocks[stream_clk_index].id = devm_kstrdup(dev, clk_name, GFP_KERNEL);
26662306a36Sopenharmony_ci			stream_clk_index++;
26762306a36Sopenharmony_ci		} else if (dp_parser_check_prefix("ctrl", clk_name) &&
26862306a36Sopenharmony_ci			   ctrl_clk_index < ctrl_clk_count) {
26962306a36Sopenharmony_ci			ctrl_power->clocks[ctrl_clk_index].id = devm_kstrdup(dev, clk_name, GFP_KERNEL);
27062306a36Sopenharmony_ci			ctrl_clk_index++;
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciint devm_dp_parser_find_next_bridge(struct device *dev, struct dp_parser *parser)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct platform_device *pdev = parser->pdev;
28062306a36Sopenharmony_ci	struct drm_bridge *bridge;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	bridge = devm_drm_of_get_bridge(dev, pdev->dev.of_node, 1, 0);
28362306a36Sopenharmony_ci	if (IS_ERR(bridge))
28462306a36Sopenharmony_ci		return PTR_ERR(bridge);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	parser->next_bridge = bridge;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int dp_parser_parse(struct dp_parser *parser)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	int rc = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!parser) {
29662306a36Sopenharmony_ci		DRM_ERROR("invalid input\n");
29762306a36Sopenharmony_ci		return -EINVAL;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	rc = dp_parser_ctrl_res(parser);
30162306a36Sopenharmony_ci	if (rc)
30262306a36Sopenharmony_ci		return rc;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	rc = dp_parser_misc(parser);
30562306a36Sopenharmony_ci	if (rc)
30662306a36Sopenharmony_ci		return rc;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	rc = dp_parser_clock(parser);
30962306a36Sopenharmony_ci	if (rc)
31062306a36Sopenharmony_ci		return rc;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistruct dp_parser *dp_parser_get(struct platform_device *pdev)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct dp_parser *parser;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
32062306a36Sopenharmony_ci	if (!parser)
32162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	parser->parse = dp_parser_parse;
32462306a36Sopenharmony_ci	parser->pdev = pdev;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return parser;
32762306a36Sopenharmony_ci}
328