Blame view

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