• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2019-2020 Intel Corporation.
3 
4 #include <linux/device.h>
5 #include <linux/acpi.h>
6 #include <linux/pm_runtime.h>
7 #include <linux/soundwire/sdw.h>
8 #include <linux/soundwire/sdw_type.h>
9 #include "bus.h"
10 
11 /*
12  * The 3s value for autosuspend will only be used if there are no
13  * devices physically attached on a bus segment. In practice enabling
14  * the bus operation will result in children devices become active and
15  * the master device will only suspend when all its children are no
16  * longer active.
17  */
18 #define SDW_MASTER_SUSPEND_DELAY_MS 3000
19 
20 /*
21  * The sysfs for properties reflects the MIPI description as given
22  * in the MIPI DisCo spec
23  *
24  * Base file is:
25  *	sdw-master-N
26  *      |---- revision
27  *      |---- clk_stop_modes
28  *      |---- max_clk_freq
29  *      |---- clk_freq
30  *      |---- clk_gears
31  *      |---- default_row
32  *      |---- default_col
33  *      |---- dynamic_shape
34  *      |---- err_threshold
35  */
36 
37 #define sdw_master_attr(field, format_string)				\
38 static ssize_t field##_show(struct device *dev,				\
39 			    struct device_attribute *attr,		\
40 			    char *buf)					\
41 {									\
42 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);	\
43 	return sprintf(buf, format_string, md->bus->prop.field);	\
44 }									\
45 static DEVICE_ATTR_RO(field)
46 
47 sdw_master_attr(revision, "0x%x\n");
48 sdw_master_attr(clk_stop_modes, "0x%x\n");
49 sdw_master_attr(max_clk_freq, "%d\n");
50 sdw_master_attr(default_row, "%d\n");
51 sdw_master_attr(default_col, "%d\n");
52 sdw_master_attr(default_frame_rate, "%d\n");
53 sdw_master_attr(dynamic_frame, "%d\n");
54 sdw_master_attr(err_threshold, "%d\n");
55 
clock_frequencies_show(struct device * dev,struct device_attribute * attr,char * buf)56 static ssize_t clock_frequencies_show(struct device *dev,
57 				      struct device_attribute *attr, char *buf)
58 {
59 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
60 	ssize_t size = 0;
61 	int i;
62 
63 	for (i = 0; i < md->bus->prop.num_clk_freq; i++)
64 		size += sprintf(buf + size, "%8d ",
65 				md->bus->prop.clk_freq[i]);
66 	size += sprintf(buf + size, "\n");
67 
68 	return size;
69 }
70 static DEVICE_ATTR_RO(clock_frequencies);
71 
clock_gears_show(struct device * dev,struct device_attribute * attr,char * buf)72 static ssize_t clock_gears_show(struct device *dev,
73 				struct device_attribute *attr, char *buf)
74 {
75 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
76 	ssize_t size = 0;
77 	int i;
78 
79 	for (i = 0; i < md->bus->prop.num_clk_gears; i++)
80 		size += sprintf(buf + size, "%8d ",
81 				md->bus->prop.clk_gears[i]);
82 	size += sprintf(buf + size, "\n");
83 
84 	return size;
85 }
86 static DEVICE_ATTR_RO(clock_gears);
87 
88 static struct attribute *master_node_attrs[] = {
89 	&dev_attr_revision.attr,
90 	&dev_attr_clk_stop_modes.attr,
91 	&dev_attr_max_clk_freq.attr,
92 	&dev_attr_default_row.attr,
93 	&dev_attr_default_col.attr,
94 	&dev_attr_default_frame_rate.attr,
95 	&dev_attr_dynamic_frame.attr,
96 	&dev_attr_err_threshold.attr,
97 	&dev_attr_clock_frequencies.attr,
98 	&dev_attr_clock_gears.attr,
99 	NULL,
100 };
101 ATTRIBUTE_GROUPS(master_node);
102 
sdw_master_device_release(struct device * dev)103 static void sdw_master_device_release(struct device *dev)
104 {
105 	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
106 
107 	kfree(md);
108 }
109 
110 static const struct dev_pm_ops master_dev_pm = {
111 	SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
112 			   pm_generic_runtime_resume, NULL)
113 };
114 
115 struct device_type sdw_master_type = {
116 	.name =		"soundwire_master",
117 	.release =	sdw_master_device_release,
118 	.pm = &master_dev_pm,
119 };
120 
121 /**
122  * sdw_master_device_add() - create a Linux Master Device representation.
123  * @bus: SDW bus instance
124  * @parent: parent device
125  * @fwnode: firmware node handle
126  */
sdw_master_device_add(struct sdw_bus * bus,struct device * parent,struct fwnode_handle * fwnode)127 int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
128 			  struct fwnode_handle *fwnode)
129 {
130 	struct sdw_master_device *md;
131 	int ret;
132 
133 	if (!parent)
134 		return -EINVAL;
135 
136 	md = kzalloc(sizeof(*md), GFP_KERNEL);
137 	if (!md)
138 		return -ENOMEM;
139 
140 	md->dev.bus = &sdw_bus_type;
141 	md->dev.type = &sdw_master_type;
142 	md->dev.parent = parent;
143 	md->dev.groups = master_node_groups;
144 	md->dev.of_node = parent->of_node;
145 	md->dev.fwnode = fwnode;
146 	md->dev.dma_mask = parent->dma_mask;
147 
148 	dev_set_name(&md->dev, "sdw-master-%d-%d", bus->controller_id, bus->link_id);
149 
150 	ret = device_register(&md->dev);
151 	if (ret) {
152 		dev_err(parent, "Failed to add master: ret %d\n", ret);
153 		/*
154 		 * On err, don't free but drop ref as this will be freed
155 		 * when release method is invoked.
156 		 */
157 		put_device(&md->dev);
158 		goto device_register_err;
159 	}
160 
161 	/* add shortcuts to improve code readability/compactness */
162 	md->bus = bus;
163 	bus->dev = &md->dev;
164 	bus->md = md;
165 
166 	pm_runtime_set_autosuspend_delay(&bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS);
167 	pm_runtime_use_autosuspend(&bus->md->dev);
168 	pm_runtime_mark_last_busy(&bus->md->dev);
169 	pm_runtime_set_active(&bus->md->dev);
170 	pm_runtime_enable(&bus->md->dev);
171 	pm_runtime_idle(&bus->md->dev);
172 device_register_err:
173 	return ret;
174 }
175 
176 /**
177  * sdw_master_device_del() - delete a Linux Master Device representation.
178  * @bus: bus handle
179  *
180  * This function is the dual of sdw_master_device_add()
181  */
sdw_master_device_del(struct sdw_bus * bus)182 int sdw_master_device_del(struct sdw_bus *bus)
183 {
184 	pm_runtime_disable(&bus->md->dev);
185 	device_unregister(bus->dev);
186 
187 	return 0;
188 }
189