linux/drivers/soundwire/master.c
<<
>>
Prefs
   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}
 189