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 | |
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: buf + size, fmt: "%8d " , |
65 | md->bus->prop.clk_freq[i]); |
66 | size += sprintf(buf: buf + size, fmt: "\n" ); |
67 | |
68 | return size; |
69 | } |
70 | static DEVICE_ATTR_RO(clock_frequencies); |
71 | |
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: buf + size, fmt: "%8d " , |
81 | md->bus->prop.clk_gears[i]); |
82 | size += sprintf(buf: buf + size, fmt: "\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 | |
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(objp: 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 | const 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 | */ |
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(size: 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(dev: &md->dev, name: "sdw-master-%d-%d" , bus->controller_id, bus->link_id); |
149 | |
150 | ret = device_register(dev: &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(dev: &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(dev: &bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS); |
167 | pm_runtime_use_autosuspend(dev: &bus->md->dev); |
168 | pm_runtime_mark_last_busy(dev: &bus->md->dev); |
169 | pm_runtime_set_active(dev: &bus->md->dev); |
170 | pm_runtime_enable(dev: &bus->md->dev); |
171 | pm_runtime_idle(dev: &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 | */ |
182 | int sdw_master_device_del(struct sdw_bus *bus) |
183 | { |
184 | pm_runtime_disable(dev: &bus->md->dev); |
185 | device_unregister(dev: bus->dev); |
186 | |
187 | return 0; |
188 | } |
189 | |