| 1 | |
| 2 | |
| 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 + size, "%8d ",
|
| 65 | md->bus->prop.clk_freq[i]);
|
| 66 | size += sprintf(buf + size, "\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 + size, "%8d ",
|
| 81 | md->bus->prop.clk_gears[i]);
|
| 82 | size += sprintf(buf + size, "\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(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_obj(*md);
|
| 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 |
|
| 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);
|
| 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(&bus->md->dev);
|
| 185 | device_unregister(bus->dev);
|
| 186 |
|
| 187 | return 0;
|
| 188 | }
|