18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci// Copyright(c) 2019-2020 Intel Corporation.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/device.h>
58c2ecf20Sopenharmony_ci#include <linux/acpi.h>
68c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
78c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw.h>
88c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw_type.h>
98c2ecf20Sopenharmony_ci#include "bus.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * The 3s value for autosuspend will only be used if there are no
138c2ecf20Sopenharmony_ci * devices physically attached on a bus segment. In practice enabling
148c2ecf20Sopenharmony_ci * the bus operation will result in children devices become active and
158c2ecf20Sopenharmony_ci * the master device will only suspend when all its children are no
168c2ecf20Sopenharmony_ci * longer active.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci#define SDW_MASTER_SUSPEND_DELAY_MS 3000
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * The sysfs for properties reflects the MIPI description as given
228c2ecf20Sopenharmony_ci * in the MIPI DisCo spec
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Base file is:
258c2ecf20Sopenharmony_ci *	sdw-master-N
268c2ecf20Sopenharmony_ci *      |---- revision
278c2ecf20Sopenharmony_ci *      |---- clk_stop_modes
288c2ecf20Sopenharmony_ci *      |---- max_clk_freq
298c2ecf20Sopenharmony_ci *      |---- clk_freq
308c2ecf20Sopenharmony_ci *      |---- clk_gears
318c2ecf20Sopenharmony_ci *      |---- default_row
328c2ecf20Sopenharmony_ci *      |---- default_col
338c2ecf20Sopenharmony_ci *      |---- dynamic_shape
348c2ecf20Sopenharmony_ci *      |---- err_threshold
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define sdw_master_attr(field, format_string)				\
388c2ecf20Sopenharmony_cistatic ssize_t field##_show(struct device *dev,				\
398c2ecf20Sopenharmony_ci			    struct device_attribute *attr,		\
408c2ecf20Sopenharmony_ci			    char *buf)					\
418c2ecf20Sopenharmony_ci{									\
428c2ecf20Sopenharmony_ci	struct sdw_master_device *md = dev_to_sdw_master_device(dev);	\
438c2ecf20Sopenharmony_ci	return sprintf(buf, format_string, md->bus->prop.field);	\
448c2ecf20Sopenharmony_ci}									\
458c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cisdw_master_attr(revision, "0x%x\n");
488c2ecf20Sopenharmony_cisdw_master_attr(clk_stop_modes, "0x%x\n");
498c2ecf20Sopenharmony_cisdw_master_attr(max_clk_freq, "%d\n");
508c2ecf20Sopenharmony_cisdw_master_attr(default_row, "%d\n");
518c2ecf20Sopenharmony_cisdw_master_attr(default_col, "%d\n");
528c2ecf20Sopenharmony_cisdw_master_attr(default_frame_rate, "%d\n");
538c2ecf20Sopenharmony_cisdw_master_attr(dynamic_frame, "%d\n");
548c2ecf20Sopenharmony_cisdw_master_attr(err_threshold, "%d\n");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic ssize_t clock_frequencies_show(struct device *dev,
578c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
608c2ecf20Sopenharmony_ci	ssize_t size = 0;
618c2ecf20Sopenharmony_ci	int i;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	for (i = 0; i < md->bus->prop.num_clk_freq; i++)
648c2ecf20Sopenharmony_ci		size += sprintf(buf + size, "%8d ",
658c2ecf20Sopenharmony_ci				md->bus->prop.clk_freq[i]);
668c2ecf20Sopenharmony_ci	size += sprintf(buf + size, "\n");
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return size;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(clock_frequencies);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic ssize_t clock_gears_show(struct device *dev,
738c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
768c2ecf20Sopenharmony_ci	ssize_t size = 0;
778c2ecf20Sopenharmony_ci	int i;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	for (i = 0; i < md->bus->prop.num_clk_gears; i++)
808c2ecf20Sopenharmony_ci		size += sprintf(buf + size, "%8d ",
818c2ecf20Sopenharmony_ci				md->bus->prop.clk_gears[i]);
828c2ecf20Sopenharmony_ci	size += sprintf(buf + size, "\n");
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return size;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(clock_gears);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct attribute *master_node_attrs[] = {
898c2ecf20Sopenharmony_ci	&dev_attr_revision.attr,
908c2ecf20Sopenharmony_ci	&dev_attr_clk_stop_modes.attr,
918c2ecf20Sopenharmony_ci	&dev_attr_max_clk_freq.attr,
928c2ecf20Sopenharmony_ci	&dev_attr_default_row.attr,
938c2ecf20Sopenharmony_ci	&dev_attr_default_col.attr,
948c2ecf20Sopenharmony_ci	&dev_attr_default_frame_rate.attr,
958c2ecf20Sopenharmony_ci	&dev_attr_dynamic_frame.attr,
968c2ecf20Sopenharmony_ci	&dev_attr_err_threshold.attr,
978c2ecf20Sopenharmony_ci	&dev_attr_clock_frequencies.attr,
988c2ecf20Sopenharmony_ci	&dev_attr_clock_gears.attr,
998c2ecf20Sopenharmony_ci	NULL,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(master_node);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void sdw_master_device_release(struct device *dev)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	kfree(md);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic const struct dev_pm_ops master_dev_pm = {
1118c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
1128c2ecf20Sopenharmony_ci			   pm_generic_runtime_resume, NULL)
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistruct device_type sdw_master_type = {
1168c2ecf20Sopenharmony_ci	.name =		"soundwire_master",
1178c2ecf20Sopenharmony_ci	.release =	sdw_master_device_release,
1188c2ecf20Sopenharmony_ci	.pm = &master_dev_pm,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/**
1228c2ecf20Sopenharmony_ci * sdw_master_device_add() - create a Linux Master Device representation.
1238c2ecf20Sopenharmony_ci * @bus: SDW bus instance
1248c2ecf20Sopenharmony_ci * @parent: parent device
1258c2ecf20Sopenharmony_ci * @fwnode: firmware node handle
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_ciint sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
1288c2ecf20Sopenharmony_ci			  struct fwnode_handle *fwnode)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct sdw_master_device *md;
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!parent)
1348c2ecf20Sopenharmony_ci		return -EINVAL;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	md = kzalloc(sizeof(*md), GFP_KERNEL);
1378c2ecf20Sopenharmony_ci	if (!md)
1388c2ecf20Sopenharmony_ci		return -ENOMEM;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	md->dev.bus = &sdw_bus_type;
1418c2ecf20Sopenharmony_ci	md->dev.type = &sdw_master_type;
1428c2ecf20Sopenharmony_ci	md->dev.parent = parent;
1438c2ecf20Sopenharmony_ci	md->dev.groups = master_node_groups;
1448c2ecf20Sopenharmony_ci	md->dev.of_node = parent->of_node;
1458c2ecf20Sopenharmony_ci	md->dev.fwnode = fwnode;
1468c2ecf20Sopenharmony_ci	md->dev.dma_mask = parent->dma_mask;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	dev_set_name(&md->dev, "sdw-master-%d", bus->id);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ret = device_register(&md->dev);
1518c2ecf20Sopenharmony_ci	if (ret) {
1528c2ecf20Sopenharmony_ci		dev_err(parent, "Failed to add master: ret %d\n", ret);
1538c2ecf20Sopenharmony_ci		/*
1548c2ecf20Sopenharmony_ci		 * On err, don't free but drop ref as this will be freed
1558c2ecf20Sopenharmony_ci		 * when release method is invoked.
1568c2ecf20Sopenharmony_ci		 */
1578c2ecf20Sopenharmony_ci		put_device(&md->dev);
1588c2ecf20Sopenharmony_ci		goto device_register_err;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* add shortcuts to improve code readability/compactness */
1628c2ecf20Sopenharmony_ci	md->bus = bus;
1638c2ecf20Sopenharmony_ci	bus->dev = &md->dev;
1648c2ecf20Sopenharmony_ci	bus->md = md;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS);
1678c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(&bus->md->dev);
1688c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&bus->md->dev);
1698c2ecf20Sopenharmony_ci	pm_runtime_set_active(&bus->md->dev);
1708c2ecf20Sopenharmony_ci	pm_runtime_enable(&bus->md->dev);
1718c2ecf20Sopenharmony_ci	pm_runtime_idle(&bus->md->dev);
1728c2ecf20Sopenharmony_cidevice_register_err:
1738c2ecf20Sopenharmony_ci	return ret;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/**
1778c2ecf20Sopenharmony_ci * sdw_master_device_del() - delete a Linux Master Device representation.
1788c2ecf20Sopenharmony_ci * @bus: bus handle
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * This function is the dual of sdw_master_device_add()
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_ciint sdw_master_device_del(struct sdw_bus *bus)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	pm_runtime_disable(&bus->md->dev);
1858c2ecf20Sopenharmony_ci	device_unregister(bus->dev);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
189