xref: /openbmc/linux/drivers/soundwire/master.c (revision 154cfc3d)
17ceaa40bSPierre-Louis Bossart // SPDX-License-Identifier: GPL-2.0-only
27ceaa40bSPierre-Louis Bossart // Copyright(c) 2019-2020 Intel Corporation.
37ceaa40bSPierre-Louis Bossart 
47ceaa40bSPierre-Louis Bossart #include <linux/device.h>
57ceaa40bSPierre-Louis Bossart #include <linux/acpi.h>
626d97022SBard Liao #include <linux/pm_runtime.h>
77ceaa40bSPierre-Louis Bossart #include <linux/soundwire/sdw.h>
87ceaa40bSPierre-Louis Bossart #include <linux/soundwire/sdw_type.h>
97ceaa40bSPierre-Louis Bossart #include "bus.h"
107ceaa40bSPierre-Louis Bossart 
11c5778ca4SPierre-Louis Bossart /*
12e04e60fcSPierre-Louis Bossart  * The 3s value for autosuspend will only be used if there are no
13e04e60fcSPierre-Louis Bossart  * devices physically attached on a bus segment. In practice enabling
14e04e60fcSPierre-Louis Bossart  * the bus operation will result in children devices become active and
15e04e60fcSPierre-Louis Bossart  * the master device will only suspend when all its children are no
16e04e60fcSPierre-Louis Bossart  * longer active.
17e04e60fcSPierre-Louis Bossart  */
18e04e60fcSPierre-Louis Bossart #define SDW_MASTER_SUSPEND_DELAY_MS 3000
19e04e60fcSPierre-Louis Bossart 
20e04e60fcSPierre-Louis Bossart /*
21c5778ca4SPierre-Louis Bossart  * The sysfs for properties reflects the MIPI description as given
22c5778ca4SPierre-Louis Bossart  * in the MIPI DisCo spec
23c5778ca4SPierre-Louis Bossart  *
24c5778ca4SPierre-Louis Bossart  * Base file is:
25c5778ca4SPierre-Louis Bossart  *	sdw-master-N
26c5778ca4SPierre-Louis Bossart  *      |---- revision
27c5778ca4SPierre-Louis Bossart  *      |---- clk_stop_modes
28c5778ca4SPierre-Louis Bossart  *      |---- max_clk_freq
29c5778ca4SPierre-Louis Bossart  *      |---- clk_freq
30c5778ca4SPierre-Louis Bossart  *      |---- clk_gears
31c5778ca4SPierre-Louis Bossart  *      |---- default_row
32c5778ca4SPierre-Louis Bossart  *      |---- default_col
33c5778ca4SPierre-Louis Bossart  *      |---- dynamic_shape
34c5778ca4SPierre-Louis Bossart  *      |---- err_threshold
35c5778ca4SPierre-Louis Bossart  */
36c5778ca4SPierre-Louis Bossart 
37c5778ca4SPierre-Louis Bossart #define sdw_master_attr(field, format_string)				\
38c5778ca4SPierre-Louis Bossart static ssize_t field##_show(struct device *dev,				\
39c5778ca4SPierre-Louis Bossart 			    struct device_attribute *attr,		\
40c5778ca4SPierre-Louis Bossart 			    char *buf)					\
41c5778ca4SPierre-Louis Bossart {									\
42c5778ca4SPierre-Louis Bossart 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);	\
43c5778ca4SPierre-Louis Bossart 	return sprintf(buf, format_string, md->bus->prop.field);	\
44c5778ca4SPierre-Louis Bossart }									\
45c5778ca4SPierre-Louis Bossart static DEVICE_ATTR_RO(field)
46c5778ca4SPierre-Louis Bossart 
47c5778ca4SPierre-Louis Bossart sdw_master_attr(revision, "0x%x\n");
48c5778ca4SPierre-Louis Bossart sdw_master_attr(clk_stop_modes, "0x%x\n");
49c5778ca4SPierre-Louis Bossart sdw_master_attr(max_clk_freq, "%d\n");
50c5778ca4SPierre-Louis Bossart sdw_master_attr(default_row, "%d\n");
51c5778ca4SPierre-Louis Bossart sdw_master_attr(default_col, "%d\n");
52c5778ca4SPierre-Louis Bossart sdw_master_attr(default_frame_rate, "%d\n");
53c5778ca4SPierre-Louis Bossart sdw_master_attr(dynamic_frame, "%d\n");
54c5778ca4SPierre-Louis Bossart sdw_master_attr(err_threshold, "%d\n");
55c5778ca4SPierre-Louis Bossart 
clock_frequencies_show(struct device * dev,struct device_attribute * attr,char * buf)56c5778ca4SPierre-Louis Bossart static ssize_t clock_frequencies_show(struct device *dev,
57c5778ca4SPierre-Louis Bossart 				      struct device_attribute *attr, char *buf)
58c5778ca4SPierre-Louis Bossart {
59c5778ca4SPierre-Louis Bossart 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
60c5778ca4SPierre-Louis Bossart 	ssize_t size = 0;
61c5778ca4SPierre-Louis Bossart 	int i;
62c5778ca4SPierre-Louis Bossart 
63c5778ca4SPierre-Louis Bossart 	for (i = 0; i < md->bus->prop.num_clk_freq; i++)
64c5778ca4SPierre-Louis Bossart 		size += sprintf(buf + size, "%8d ",
65c5778ca4SPierre-Louis Bossart 				md->bus->prop.clk_freq[i]);
66c5778ca4SPierre-Louis Bossart 	size += sprintf(buf + size, "\n");
67c5778ca4SPierre-Louis Bossart 
68c5778ca4SPierre-Louis Bossart 	return size;
69c5778ca4SPierre-Louis Bossart }
70c5778ca4SPierre-Louis Bossart static DEVICE_ATTR_RO(clock_frequencies);
71c5778ca4SPierre-Louis Bossart 
clock_gears_show(struct device * dev,struct device_attribute * attr,char * buf)72c5778ca4SPierre-Louis Bossart static ssize_t clock_gears_show(struct device *dev,
73c5778ca4SPierre-Louis Bossart 				struct device_attribute *attr, char *buf)
74c5778ca4SPierre-Louis Bossart {
75c5778ca4SPierre-Louis Bossart 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
76c5778ca4SPierre-Louis Bossart 	ssize_t size = 0;
77c5778ca4SPierre-Louis Bossart 	int i;
78c5778ca4SPierre-Louis Bossart 
79c5778ca4SPierre-Louis Bossart 	for (i = 0; i < md->bus->prop.num_clk_gears; i++)
80c5778ca4SPierre-Louis Bossart 		size += sprintf(buf + size, "%8d ",
81c5778ca4SPierre-Louis Bossart 				md->bus->prop.clk_gears[i]);
82c5778ca4SPierre-Louis Bossart 	size += sprintf(buf + size, "\n");
83c5778ca4SPierre-Louis Bossart 
84c5778ca4SPierre-Louis Bossart 	return size;
85c5778ca4SPierre-Louis Bossart }
86c5778ca4SPierre-Louis Bossart static DEVICE_ATTR_RO(clock_gears);
87c5778ca4SPierre-Louis Bossart 
88c5778ca4SPierre-Louis Bossart static struct attribute *master_node_attrs[] = {
89c5778ca4SPierre-Louis Bossart 	&dev_attr_revision.attr,
90c5778ca4SPierre-Louis Bossart 	&dev_attr_clk_stop_modes.attr,
91c5778ca4SPierre-Louis Bossart 	&dev_attr_max_clk_freq.attr,
92c5778ca4SPierre-Louis Bossart 	&dev_attr_default_row.attr,
93c5778ca4SPierre-Louis Bossart 	&dev_attr_default_col.attr,
94c5778ca4SPierre-Louis Bossart 	&dev_attr_default_frame_rate.attr,
95c5778ca4SPierre-Louis Bossart 	&dev_attr_dynamic_frame.attr,
96c5778ca4SPierre-Louis Bossart 	&dev_attr_err_threshold.attr,
97c5778ca4SPierre-Louis Bossart 	&dev_attr_clock_frequencies.attr,
98c5778ca4SPierre-Louis Bossart 	&dev_attr_clock_gears.attr,
99c5778ca4SPierre-Louis Bossart 	NULL,
100c5778ca4SPierre-Louis Bossart };
101c5778ca4SPierre-Louis Bossart ATTRIBUTE_GROUPS(master_node);
102c5778ca4SPierre-Louis Bossart 
sdw_master_device_release(struct device * dev)1037ceaa40bSPierre-Louis Bossart static void sdw_master_device_release(struct device *dev)
1047ceaa40bSPierre-Louis Bossart {
1057ceaa40bSPierre-Louis Bossart 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
1067ceaa40bSPierre-Louis Bossart 
1077ceaa40bSPierre-Louis Bossart 	kfree(md);
1087ceaa40bSPierre-Louis Bossart }
1097ceaa40bSPierre-Louis Bossart 
11026d97022SBard Liao static const struct dev_pm_ops master_dev_pm = {
11126d97022SBard Liao 	SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
11226d97022SBard Liao 			   pm_generic_runtime_resume, NULL)
11326d97022SBard Liao };
11426d97022SBard Liao 
1157ceaa40bSPierre-Louis Bossart struct device_type sdw_master_type = {
1167ceaa40bSPierre-Louis Bossart 	.name =		"soundwire_master",
1177ceaa40bSPierre-Louis Bossart 	.release =	sdw_master_device_release,
11826d97022SBard Liao 	.pm = &master_dev_pm,
1197ceaa40bSPierre-Louis Bossart };
1207ceaa40bSPierre-Louis Bossart 
1217ceaa40bSPierre-Louis Bossart /**
1227ceaa40bSPierre-Louis Bossart  * sdw_master_device_add() - create a Linux Master Device representation.
1237ceaa40bSPierre-Louis Bossart  * @bus: SDW bus instance
1247ceaa40bSPierre-Louis Bossart  * @parent: parent device
1257ceaa40bSPierre-Louis Bossart  * @fwnode: firmware node handle
1267ceaa40bSPierre-Louis Bossart  */
sdw_master_device_add(struct sdw_bus * bus,struct device * parent,struct fwnode_handle * fwnode)1277ceaa40bSPierre-Louis Bossart int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
1287ceaa40bSPierre-Louis Bossart 			  struct fwnode_handle *fwnode)
1297ceaa40bSPierre-Louis Bossart {
1307ceaa40bSPierre-Louis Bossart 	struct sdw_master_device *md;
1317ceaa40bSPierre-Louis Bossart 	int ret;
1327ceaa40bSPierre-Louis Bossart 
1337ceaa40bSPierre-Louis Bossart 	if (!parent)
1347ceaa40bSPierre-Louis Bossart 		return -EINVAL;
1357ceaa40bSPierre-Louis Bossart 
1367ceaa40bSPierre-Louis Bossart 	md = kzalloc(sizeof(*md), GFP_KERNEL);
1377ceaa40bSPierre-Louis Bossart 	if (!md)
1387ceaa40bSPierre-Louis Bossart 		return -ENOMEM;
1397ceaa40bSPierre-Louis Bossart 
1407ceaa40bSPierre-Louis Bossart 	md->dev.bus = &sdw_bus_type;
1417ceaa40bSPierre-Louis Bossart 	md->dev.type = &sdw_master_type;
1427ceaa40bSPierre-Louis Bossart 	md->dev.parent = parent;
143c5778ca4SPierre-Louis Bossart 	md->dev.groups = master_node_groups;
1447ceaa40bSPierre-Louis Bossart 	md->dev.of_node = parent->of_node;
1457ceaa40bSPierre-Louis Bossart 	md->dev.fwnode = fwnode;
1467ceaa40bSPierre-Louis Bossart 	md->dev.dma_mask = parent->dma_mask;
1477ceaa40bSPierre-Louis Bossart 
148*154cfc3dSPierre-Louis Bossart 	dev_set_name(&md->dev, "sdw-master-%d-%d", bus->controller_id, bus->link_id);
1497ceaa40bSPierre-Louis Bossart 
1507ceaa40bSPierre-Louis Bossart 	ret = device_register(&md->dev);
1517ceaa40bSPierre-Louis Bossart 	if (ret) {
1527ceaa40bSPierre-Louis Bossart 		dev_err(parent, "Failed to add master: ret %d\n", ret);
1537ceaa40bSPierre-Louis Bossart 		/*
1547ceaa40bSPierre-Louis Bossart 		 * On err, don't free but drop ref as this will be freed
1557ceaa40bSPierre-Louis Bossart 		 * when release method is invoked.
1567ceaa40bSPierre-Louis Bossart 		 */
1577ceaa40bSPierre-Louis Bossart 		put_device(&md->dev);
1587ceaa40bSPierre-Louis Bossart 		goto device_register_err;
1597ceaa40bSPierre-Louis Bossart 	}
1607ceaa40bSPierre-Louis Bossart 
1617ceaa40bSPierre-Louis Bossart 	/* add shortcuts to improve code readability/compactness */
1627ceaa40bSPierre-Louis Bossart 	md->bus = bus;
1637ceaa40bSPierre-Louis Bossart 	bus->dev = &md->dev;
1647ceaa40bSPierre-Louis Bossart 	bus->md = md;
1657ceaa40bSPierre-Louis Bossart 
166e04e60fcSPierre-Louis Bossart 	pm_runtime_set_autosuspend_delay(&bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS);
167e04e60fcSPierre-Louis Bossart 	pm_runtime_use_autosuspend(&bus->md->dev);
168e04e60fcSPierre-Louis Bossart 	pm_runtime_mark_last_busy(&bus->md->dev);
169e04e60fcSPierre-Louis Bossart 	pm_runtime_set_active(&bus->md->dev);
170bd84256eSBard Liao 	pm_runtime_enable(&bus->md->dev);
171e04e60fcSPierre-Louis Bossart 	pm_runtime_idle(&bus->md->dev);
1727ceaa40bSPierre-Louis Bossart device_register_err:
1737ceaa40bSPierre-Louis Bossart 	return ret;
1747ceaa40bSPierre-Louis Bossart }
1757ceaa40bSPierre-Louis Bossart 
1767ceaa40bSPierre-Louis Bossart /**
1777ceaa40bSPierre-Louis Bossart  * sdw_master_device_del() - delete a Linux Master Device representation.
1787ceaa40bSPierre-Louis Bossart  * @bus: bus handle
1797ceaa40bSPierre-Louis Bossart  *
1807ceaa40bSPierre-Louis Bossart  * This function is the dual of sdw_master_device_add()
1817ceaa40bSPierre-Louis Bossart  */
sdw_master_device_del(struct sdw_bus * bus)1827ceaa40bSPierre-Louis Bossart int sdw_master_device_del(struct sdw_bus *bus)
1837ceaa40bSPierre-Louis Bossart {
184bd84256eSBard Liao 	pm_runtime_disable(&bus->md->dev);
1857ceaa40bSPierre-Louis Bossart 	device_unregister(bus->dev);
1867ceaa40bSPierre-Louis Bossart 
1877ceaa40bSPierre-Louis Bossart 	return 0;
1887ceaa40bSPierre-Louis Bossart }
189