brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · b2c6451 Raw
189 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// 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 no13 * devices physically attached on a bus segment. In practice enabling14 * the bus operation will result in children devices become active and15 * the master device will only suspend when all its children are no16 * longer active.17 */18#define SDW_MASTER_SUSPEND_DELAY_MS 300019 20/*21 * The sysfs for properties reflects the MIPI description as given22 * in the MIPI DisCo spec23 *24 * Base file is:25 *	sdw-master-N26 *      |---- revision27 *      |---- clk_stop_modes28 *      |---- max_clk_freq29 *      |---- clk_freq30 *      |---- clk_gears31 *      |---- default_row32 *      |---- default_col33 *      |---- dynamic_shape34 *      |---- err_threshold35 */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 115const 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 instance124 * @parent: parent device125 * @fwnode: firmware node handle126 */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 freed155		 * 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 handle179 *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}189