18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
78c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "dp_parser.h"
128c2ecf20Sopenharmony_ci#include "dp_reg.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic const struct dp_regulator_cfg sdm845_dp_reg_cfg = {
158c2ecf20Sopenharmony_ci	.num = 2,
168c2ecf20Sopenharmony_ci	.regs = {
178c2ecf20Sopenharmony_ci		{"vdda-1p2", 21800, 4 },	/* 1.2 V */
188c2ecf20Sopenharmony_ci		{"vdda-0p9", 36000, 32 },	/* 0.9 V */
198c2ecf20Sopenharmony_ci	},
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int msm_dss_ioremap(struct platform_device *pdev,
238c2ecf20Sopenharmony_ci				struct dss_io_data *io_data)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct resource *res = NULL;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
288c2ecf20Sopenharmony_ci	if (!res) {
298c2ecf20Sopenharmony_ci		DRM_ERROR("%pS->%s: msm_dss_get_res failed\n",
308c2ecf20Sopenharmony_ci			__builtin_return_address(0), __func__);
318c2ecf20Sopenharmony_ci		return -ENODEV;
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	io_data->len = (u32)resource_size(res);
358c2ecf20Sopenharmony_ci	io_data->base = ioremap(res->start, io_data->len);
368c2ecf20Sopenharmony_ci	if (!io_data->base) {
378c2ecf20Sopenharmony_ci		DRM_ERROR("%pS->%s: ioremap failed\n",
388c2ecf20Sopenharmony_ci			__builtin_return_address(0), __func__);
398c2ecf20Sopenharmony_ci		return -EIO;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return 0;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void msm_dss_iounmap(struct dss_io_data *io_data)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	if (io_data->base) {
488c2ecf20Sopenharmony_ci		iounmap(io_data->base);
498c2ecf20Sopenharmony_ci		io_data->base = NULL;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci	io_data->len = 0;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void dp_parser_unmap_io_resources(struct dp_parser *parser)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct dp_io *io = &parser->io;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	msm_dss_iounmap(&io->dp_controller);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int dp_parser_ctrl_res(struct dp_parser *parser)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int rc = 0;
648c2ecf20Sopenharmony_ci	struct platform_device *pdev = parser->pdev;
658c2ecf20Sopenharmony_ci	struct dp_io *io = &parser->io;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	rc = msm_dss_ioremap(pdev, &io->dp_controller);
688c2ecf20Sopenharmony_ci	if (rc) {
698c2ecf20Sopenharmony_ci		DRM_ERROR("unable to remap dp io resources, rc=%d\n", rc);
708c2ecf20Sopenharmony_ci		goto err;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	io->phy = devm_phy_get(&pdev->dev, "dp");
748c2ecf20Sopenharmony_ci	if (IS_ERR(io->phy)) {
758c2ecf20Sopenharmony_ci		rc = PTR_ERR(io->phy);
768c2ecf20Sopenharmony_ci		goto err;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_cierr:
818c2ecf20Sopenharmony_ci	dp_parser_unmap_io_resources(parser);
828c2ecf20Sopenharmony_ci	return rc;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int dp_parser_misc(struct dp_parser *parser)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct device_node *of_node = parser->pdev->dev.of_node;
888c2ecf20Sopenharmony_ci	int len = 0;
898c2ecf20Sopenharmony_ci	const char *data_lane_property = "data-lanes";
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	len = of_property_count_elems_of_size(of_node,
928c2ecf20Sopenharmony_ci			 data_lane_property, sizeof(u32));
938c2ecf20Sopenharmony_ci	if (len < 0) {
948c2ecf20Sopenharmony_ci		DRM_WARN("Invalid property %s, default max DP lanes = %d\n",
958c2ecf20Sopenharmony_ci				data_lane_property, DP_MAX_NUM_DP_LANES);
968c2ecf20Sopenharmony_ci		len = DP_MAX_NUM_DP_LANES;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	parser->max_dp_lanes = len;
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic inline bool dp_parser_check_prefix(const char *clk_prefix,
1048c2ecf20Sopenharmony_ci						const char *clk_name)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	return !strncmp(clk_prefix, clk_name, strlen(clk_prefix));
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int dp_parser_init_clk_data(struct dp_parser *parser)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int num_clk, i, rc;
1128c2ecf20Sopenharmony_ci	int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
1138c2ecf20Sopenharmony_ci	const char *clk_name;
1148c2ecf20Sopenharmony_ci	struct device *dev = &parser->pdev->dev;
1158c2ecf20Sopenharmony_ci	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
1168c2ecf20Sopenharmony_ci	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
1178c2ecf20Sopenharmony_ci	struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM];
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	num_clk = of_property_count_strings(dev->of_node, "clock-names");
1208c2ecf20Sopenharmony_ci	if (num_clk <= 0) {
1218c2ecf20Sopenharmony_ci		DRM_ERROR("no clocks are defined\n");
1228c2ecf20Sopenharmony_ci		return -EINVAL;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	for (i = 0; i < num_clk; i++) {
1268c2ecf20Sopenharmony_ci		rc = of_property_read_string_index(dev->of_node,
1278c2ecf20Sopenharmony_ci				"clock-names", i, &clk_name);
1288c2ecf20Sopenharmony_ci		if (rc < 0)
1298c2ecf20Sopenharmony_ci			return rc;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		if (dp_parser_check_prefix("core", clk_name))
1328c2ecf20Sopenharmony_ci			core_clk_count++;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		if (dp_parser_check_prefix("ctrl", clk_name))
1358c2ecf20Sopenharmony_ci			ctrl_clk_count++;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		if (dp_parser_check_prefix("stream", clk_name))
1388c2ecf20Sopenharmony_ci			stream_clk_count++;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Initialize the CORE power module */
1428c2ecf20Sopenharmony_ci	if (core_clk_count == 0) {
1438c2ecf20Sopenharmony_ci		DRM_ERROR("no core clocks are defined\n");
1448c2ecf20Sopenharmony_ci		return -EINVAL;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	core_power->num_clk = core_clk_count;
1488c2ecf20Sopenharmony_ci	core_power->clk_config = devm_kzalloc(dev,
1498c2ecf20Sopenharmony_ci			sizeof(struct dss_clk) * core_power->num_clk,
1508c2ecf20Sopenharmony_ci			GFP_KERNEL);
1518c2ecf20Sopenharmony_ci	if (!core_power->clk_config)
1528c2ecf20Sopenharmony_ci		return -EINVAL;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* Initialize the CTRL power module */
1558c2ecf20Sopenharmony_ci	if (ctrl_clk_count == 0) {
1568c2ecf20Sopenharmony_ci		DRM_ERROR("no ctrl clocks are defined\n");
1578c2ecf20Sopenharmony_ci		return -EINVAL;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ctrl_power->num_clk = ctrl_clk_count;
1618c2ecf20Sopenharmony_ci	ctrl_power->clk_config = devm_kzalloc(dev,
1628c2ecf20Sopenharmony_ci			sizeof(struct dss_clk) * ctrl_power->num_clk,
1638c2ecf20Sopenharmony_ci			GFP_KERNEL);
1648c2ecf20Sopenharmony_ci	if (!ctrl_power->clk_config) {
1658c2ecf20Sopenharmony_ci		ctrl_power->num_clk = 0;
1668c2ecf20Sopenharmony_ci		return -EINVAL;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Initialize the STREAM power module */
1708c2ecf20Sopenharmony_ci	if (stream_clk_count == 0) {
1718c2ecf20Sopenharmony_ci		DRM_ERROR("no stream (pixel) clocks are defined\n");
1728c2ecf20Sopenharmony_ci		return -EINVAL;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	stream_power->num_clk = stream_clk_count;
1768c2ecf20Sopenharmony_ci	stream_power->clk_config = devm_kzalloc(dev,
1778c2ecf20Sopenharmony_ci			sizeof(struct dss_clk) * stream_power->num_clk,
1788c2ecf20Sopenharmony_ci			GFP_KERNEL);
1798c2ecf20Sopenharmony_ci	if (!stream_power->clk_config) {
1808c2ecf20Sopenharmony_ci		stream_power->num_clk = 0;
1818c2ecf20Sopenharmony_ci		return -EINVAL;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int dp_parser_clock(struct dp_parser *parser)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int rc = 0, i = 0;
1908c2ecf20Sopenharmony_ci	int num_clk = 0;
1918c2ecf20Sopenharmony_ci	int core_clk_index = 0, ctrl_clk_index = 0, stream_clk_index = 0;
1928c2ecf20Sopenharmony_ci	int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
1938c2ecf20Sopenharmony_ci	const char *clk_name;
1948c2ecf20Sopenharmony_ci	struct device *dev = &parser->pdev->dev;
1958c2ecf20Sopenharmony_ci	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
1968c2ecf20Sopenharmony_ci	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
1978c2ecf20Sopenharmony_ci	struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM];
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	rc =  dp_parser_init_clk_data(parser);
2008c2ecf20Sopenharmony_ci	if (rc) {
2018c2ecf20Sopenharmony_ci		DRM_ERROR("failed to initialize power data %d\n", rc);
2028c2ecf20Sopenharmony_ci		return -EINVAL;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	core_clk_count = core_power->num_clk;
2068c2ecf20Sopenharmony_ci	ctrl_clk_count = ctrl_power->num_clk;
2078c2ecf20Sopenharmony_ci	stream_clk_count = stream_power->num_clk;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	num_clk = core_clk_count + ctrl_clk_count + stream_clk_count;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (i = 0; i < num_clk; i++) {
2128c2ecf20Sopenharmony_ci		rc = of_property_read_string_index(dev->of_node, "clock-names",
2138c2ecf20Sopenharmony_ci				i, &clk_name);
2148c2ecf20Sopenharmony_ci		if (rc) {
2158c2ecf20Sopenharmony_ci			DRM_ERROR("error reading clock-names %d\n", rc);
2168c2ecf20Sopenharmony_ci			return rc;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci		if (dp_parser_check_prefix("core", clk_name) &&
2198c2ecf20Sopenharmony_ci				core_clk_index < core_clk_count) {
2208c2ecf20Sopenharmony_ci			struct dss_clk *clk =
2218c2ecf20Sopenharmony_ci				&core_power->clk_config[core_clk_index];
2228c2ecf20Sopenharmony_ci			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
2238c2ecf20Sopenharmony_ci			clk->type = DSS_CLK_AHB;
2248c2ecf20Sopenharmony_ci			core_clk_index++;
2258c2ecf20Sopenharmony_ci		} else if (dp_parser_check_prefix("stream", clk_name) &&
2268c2ecf20Sopenharmony_ci				stream_clk_index < stream_clk_count) {
2278c2ecf20Sopenharmony_ci			struct dss_clk *clk =
2288c2ecf20Sopenharmony_ci				&stream_power->clk_config[stream_clk_index];
2298c2ecf20Sopenharmony_ci			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
2308c2ecf20Sopenharmony_ci			clk->type = DSS_CLK_PCLK;
2318c2ecf20Sopenharmony_ci			stream_clk_index++;
2328c2ecf20Sopenharmony_ci		} else if (dp_parser_check_prefix("ctrl", clk_name) &&
2338c2ecf20Sopenharmony_ci			   ctrl_clk_index < ctrl_clk_count) {
2348c2ecf20Sopenharmony_ci			struct dss_clk *clk =
2358c2ecf20Sopenharmony_ci				&ctrl_power->clk_config[ctrl_clk_index];
2368c2ecf20Sopenharmony_ci			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
2378c2ecf20Sopenharmony_ci			ctrl_clk_index++;
2388c2ecf20Sopenharmony_ci			if (dp_parser_check_prefix("ctrl_link", clk_name) ||
2398c2ecf20Sopenharmony_ci			    dp_parser_check_prefix("stream_pixel", clk_name))
2408c2ecf20Sopenharmony_ci				clk->type = DSS_CLK_PCLK;
2418c2ecf20Sopenharmony_ci			else
2428c2ecf20Sopenharmony_ci				clk->type = DSS_CLK_AHB;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	DRM_DEBUG_DP("clock parsing successful\n");
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int dp_parser_parse(struct dp_parser *parser)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	int rc = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (!parser) {
2568c2ecf20Sopenharmony_ci		DRM_ERROR("invalid input\n");
2578c2ecf20Sopenharmony_ci		return -EINVAL;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	rc = dp_parser_ctrl_res(parser);
2618c2ecf20Sopenharmony_ci	if (rc)
2628c2ecf20Sopenharmony_ci		return rc;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	rc = dp_parser_misc(parser);
2658c2ecf20Sopenharmony_ci	if (rc)
2668c2ecf20Sopenharmony_ci		return rc;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	rc = dp_parser_clock(parser);
2698c2ecf20Sopenharmony_ci	if (rc)
2708c2ecf20Sopenharmony_ci		return rc;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* Map the corresponding regulator information according to
2738c2ecf20Sopenharmony_ci	 * version. Currently, since we only have one supported platform,
2748c2ecf20Sopenharmony_ci	 * mapping the regulator directly.
2758c2ecf20Sopenharmony_ci	 */
2768c2ecf20Sopenharmony_ci	parser->regulator_cfg = &sdm845_dp_reg_cfg;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistruct dp_parser *dp_parser_get(struct platform_device *pdev)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct dp_parser *parser;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
2868c2ecf20Sopenharmony_ci	if (!parser)
2878c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	parser->parse = dp_parser_parse;
2908c2ecf20Sopenharmony_ci	parser->pdev = pdev;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return parser;
2938c2ecf20Sopenharmony_ci}
294