master.c (4954B)
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", bus->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}