Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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)				\
 38static 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}									\
 45static DEVICE_ATTR_RO(field)
 46
 47sdw_master_attr(revision, "0x%x\n");
 48sdw_master_attr(clk_stop_modes, "0x%x\n");
 49sdw_master_attr(max_clk_freq, "%d\n");
 50sdw_master_attr(default_row, "%d\n");
 51sdw_master_attr(default_col, "%d\n");
 52sdw_master_attr(default_frame_rate, "%d\n");
 53sdw_master_attr(dynamic_frame, "%d\n");
 54sdw_master_attr(err_threshold, "%d\n");
 55
 56static 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}
 70static DEVICE_ATTR_RO(clock_frequencies);
 71
 72static 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}
 86static DEVICE_ATTR_RO(clock_gears);
 87
 88static 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};
101ATTRIBUTE_GROUPS(master_node);
102
103static 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
110static 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
115struct 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 */
127int 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);
172device_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 */
182int 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}